# Terraform Ls > We use (JSON) objects to group some config options. Where applicable and necessary, we refer to nested fields using the`.`as a separator. i.e. A hypothetical`bar`option under`{"foo": { "bar": ".. --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/SETTINGS.md # Settings ## Data Structures We use (JSON) objects to group some config options. Where applicable and necessary, we refer to nested fields using the `.` as a separator. i.e. A hypothetical `bar` option under `{"foo": { "bar": "..." } }` would be referred to as `foo.bar`. Clients which expose these config options to the end-user are advised to match the option names and, if possible data structures. Some clients (VS Code extension) may however use flat structure, such as `{"foo.bar": "..."}` if using objects is not possible or practical. ## Supported Options The language server supports the following configuration options: ## `terraform` (object `{}`) Terraform CLI related settings (used e.g. in formatting code via `terraform fmt`). ### `logFilePath` (`string`) Path to a file for Terraform executions to be logged into (`TF_LOG_PATH`) with support for variables (e.g. Timestamp, Pid, Ppid) via Go template syntax `{{.VarName}}` ### `timeout` (`string`) Overrides Terraform execution timeout in [`time.ParseDuration`](https://pkg.go.dev/time#ParseDuration) compatible format (e.g. `30s`) ### `path` (`string`) Path to the Terraform binary. This is usually looked up automatically from `$PATH` and should not need to be specified in majority of cases. Use this to override the automatic lookup. ## **DEPRECATED**: `terraformLogFilePath` (`string`) Deprecated in favour of `terraform.logFilePath` ## **DEPRECATED**: `terraformExecTimeout` (`string`) Deprecated in favour of `terraform.timeout` ## **DEPRECATED**: `terraformExecPath` (`string`) Deprecated in favour of `terraform.path` ## **DEPRECATED**: `rootModulePaths` (`[]string`) This option is deprecated and ignored from v0.29+, it was used as an escape hatch to force indexing of paths in cases where indexer wouldn't index them otherwise. Indexer in 0.29.0 no longer limited to just initialized modules (folders with `.terraform`) and instead indexes all directories with `*.tf` files in them. Therefore this option is no longer relevant. If you previously used it to force indexing of a folder outside of a workspace, you can just add that folder to the workspace and it will be indexed as usual. ## **DEPRECATED**: `excludeModulePaths` (`[]string`) Deprecated in favour of `indexing.ignorePaths` ## `indexing` (object `{}`) ### `ignorePaths` (`[]string`) Paths to ignore when indexing the workspace on initialization. This can serve as an escape hatch in large workspaces. Key side effect of ignoring a path is that go-to-definition, go-to-references and generally most IntelliSense related to local `module` blocks will **not** work until the target module code is explicitly opened. Relative paths are resolved relative to the root (workspace) path opened in the editor. Path separators are converted automatically to the match separators of the target platform (e.g. `\` on Windows, or `/` on Unix), symlinks are followed, trailing slashes automatically removed, and `~` is replaced with your home directory. ## `ignoreDirectoryNames` (`[]string`) This allows excluding directories from being indexed upon initialization by passing a list of directory names. The following list of directories will always be ignored: - `.git` - `.idea` - `.vscode` - `terraform.tfstate.d` - `.terragrunt-cache` ## **DEPRECATED**: `ignoreDirectoryNames` (`[]string`) Deprecated in favour of `indexing.ignoreDirectoryNames` ## `commandPrefix` Some clients such as VS Code keep a global registry of commands published by language servers, and the names must be unique, even between terraform-ls instances. Setting this allows multiple servers to run side by side, albeit the client is now responsible for routing commands to the correct server. Users should not need to worry about this, the frontend client extension should manage it. The prefix will be applied to the front of the command name, which already contains a `terraform-ls` prefix. `commandPrefix.terraform-ls.commandName` Or if left empty `terraform-ls.commandName` This setting should be deprecated once the language server supports multiple workspaces, as this arises in VS code because a server instance is started per VS Code workspace. ## `ignoreSingleFileWarning` (`bool`) This setting controls whether terraform-ls sends a warning about opening up a single Terraform file instead of a Terraform folder. Setting this to `true` will prevent the message being sent. The default value is `false`. ## `experimentalFeatures` (object) This object contains inner settings used to opt into experimental features not yet ready to be on by default. ### `validateOnSave` (`bool`) Enabling this feature will run terraform validate within the folder of the file saved. This comes with some user experience caveats. - Validation is not run on file open, only once it's saved. - When editing a module file, validation is not run due to not knowing which "rootmodule" to run validation from (there could be multiple). This creates an awkward workflow where when saving a file in a rootmodule, a diagnostic is raised in a module file. Editing the module file will not clear the diagnostic for the reason mentioned above, it will only clear once a file is saved back in the original "rootmodule". We will continue to attempt improve this user experience. ### `prefillRequiredFields` (`bool`) Enables advanced completion for `provider`, `resource`, and `data` blocks where any required fields for that block are pre-filled. All such attributes and blocks are sorted alphabetically to ensure consistent ordering. When disabled (unset or set to `false`), completion only provides the label name. For example, when completing the `aws_appmesh_route` resource the `mesh_name`, `name`, `virtual_router_name` attributes and the `spec` block will fill and prompt you for appropriate values. ## `validation` (object) This object contains settings related to validation unless it's experimental, in which case it's under [`experimentalFeatures`](#experimentalfeatures-object). ### `enableEnhancedValidation` (`bool`, defaults to `true`) Enables/disables enhanced validation, as documented under [`validation.md`](validation.md#enhanced-validation). ## How to pass settings The server expects static settings to be passed as part of LSP `initialize` call, but how settings are requested from on the UI side depends on the client. ### Sublime Text Use `initializationOptions` key under the `clients.terraform` section, e.g. ```json { "clients": { "terraform": { "initializationOptions": { "rootModulePaths": ["/any/path"] }, } } } ``` or ```json { "clients": { "terraform": { "initializationOptions": { "excludeModulePaths": ["/any/path"] }, } } } ``` ### VS Code Use `terraform-ls`, e.g. ```json { "terraform-ls": { "rootModulePaths": ["/any/path"] } } ``` or ```json { "terraform-ls": { "excludeRootModules": ["/any/path"] } } --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/TROUBLESHOOTING.md # Troubleshooting ## Logging The language server produces detailed logs (including every LSP request and response) which are send to stderr by default. It may be helpful to share these logs when reporting bugs. ### stderr (default) Most clients provide a way of inspecting these logs when server is launched in the default "stdio" mode where stdout & stdin are used as communication channels for LSP. For example: [**Terraform VS Code Extension**](https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform) 1. `View` -> `Output`\ ![vscode-view-output-menu](./images/vscode-view-output-menu.png) 2. `Output` -> `HashiCorp Terraform`\ ![vscode-output-pane](./images/vscode-output-pane.png) [**Sublime Text LSP-terraform**](https://github.com/sublimelsp/LSP-terraform) 1. Open the command palette via `⌘/Ctrl + Shift + P` 2. `LSP: Toggle Log Panel`\ ![sublime-text-cmd-palette-log](./images/sublime-text-cmd-palette-log.png) 3. See logs in the bottom pane\ ![sublime-text-log-panel](./images/sublime-text-log-panel.png) ### Logging to Files Server logs can also be directed to files using **`-log-file=`** of the `serve` command. ```sh $ terraform-ls serve -log-file='/tmp/terraform-ls-{{pid}}.log' ``` Clients which manage LS installation typically allow passing extra arguments. For example: [**Terraform VS Code Extension**](https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform) 1. Open the command palette via `⌘/Ctrl + Shift + P` 2. ![vscode-open-settings-json](./images/vscode-open-settings-json.png) 3. ![vscode-json-ls-settings](./images/vscode-json-ls-settings.png) [**Sublime Text LSP-terraform**](https://github.com/sublimelsp/LSP-terraform) 1. Open the command palette via `⌘/Ctrl + Shift + P` 2. ![sublime-text-cmd-palette-settings](./images/sublime-text-cmd-palette-settings.png) 3. ![sublime-text-settings](./images/sublime-text-settings.png) ### Terraform CLI Execution Logs Given that the server may also execute Terraform itself, it may be useful to collect logs from all these executions too. This is equivalent to setting [`TF_LOG_PATH` variable](https://www.terraform.io/internals/debugging). This can be enabled via [`terraformLogFilePath` LSP settings](./SETTINGS.md#terraformlogfilepath-string). Clients which manage LS installation typically expose this as a dedicated setting option. For example: [**Terraform VS Code Extension**](https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform) 1. Open the command palette via `⌘/Ctrl + Shift + P` 2. ![vscode-open-settings-json](./images/vscode-open-settings-json.png) 3. Set `"terraform-ls.terraformLogFilePath"` to a file path, such as `/tmp/tf-exec-{{lsPid}}-{{method}}-{{timestamp}}.log` [**Sublime Text LSP-terraform**](https://github.com/sublimelsp/LSP-terraform) 1. Open the command palette via `⌘/Ctrl + Shift + P` 2. ![sublime-text-cmd-palette-settings](./images/sublime-text-cmd-palette-settings.png) 3. Set `terraformLogFilePath` under `initializationOptions` ```json { "initializationOptions": { "terraformLogFilePath": "/tmp/tf-exec-{{lsPid}}-{{method}}-{{timestamp}}.log" } } ``` ### How To Share Logs It is recommended to avoid pasting logs into the body of an issue, unless you are trying to draw attention to a selected line or two. It's always better to upload the log as [GitHub Gist](https://gist.github.com/) and attach the link to your issue/comment, or [attach the file to your issue/comment](https://docs.github.com/en/github/managing-your-work-on-github/file-attachments-on-issues-and-pull-requests). ### Sensitive Data Logs may contain sensitive data (such as content of the files being edited in the editor). If you consider the content sensitive you may PGP encrypt it using [HashiCorp's key](https://www.hashicorp.com/security#secure-communications) to reduce the exposure of the sensitive data to HashiCorp. ### Log Rotation Keep in mind that the language server itself does not have any log rotation facility, but the destination path will be truncated before being logged into. Static paths may produce large files over the lifetime of the server and templated paths (as described below) may produce many log files over time. ### Log Path Templating Log paths support template syntax. This allows for separation of logs while accounting for: - multiple server instances - multiple clients - multiple Terraform executions which may happen in parallel **`-log-file`** flag supports the following functions: - `timestamp` - current timestamp (formatted as [`Time.Unix()`](https://golang.org/pkg/time/#Time.Unix), i.e. the number of seconds elapsed since January 1, 1970 UTC) - `pid` - process ID of the language server - `ppid` - parent process ID (typically editor's or editor plugin's PID) **`terraformLogFilePath`** option supports the following functions: - `timestamp` - current timestamp (formatted as [`Time.Unix()`](https://golang.org/pkg/time/#Time.Unix), i.e. the number of seconds elapsed since January 1, 1970 UTC) - `lsPid` - process ID of the language server - `lsPpid` - parent process ID of the language server (typically editor's or editor plugin's PID) - `method` - [`terraform-exec`](https://pkg.go.dev/github.com/hashicorp/terraform-exec) method (e.g. `Format` or `Version`) The path is interpreted as [Go template](https://golang.org/pkg/text/template/), e.g. `/tmp/terraform-ls-{{timestamp}}.log`. ## CPU Profiling If the bug you are reporting is related to high CPU usage it may be helpful to collect and share CPU profile which can be done via `cpuprofile` flag. For example you can modify the launch arguments in your editor to: ```sh $ terraform-ls serve \ -cpuprofile=/tmp/terraform-ls-cpu.prof ``` Clients which manage LS installation typically allow passing extra arguments. See [Logging to Files](#logging-to-files) section above for examples. The target file will be truncated before being written into. ### Path Templating Path supports template syntax. This allows for separation of logs while accounting for multiple server or client instances. **`-cpuprofile`** supports the following functions: - `timestamp` - current timestamp (formatted as [`Time.Unix()`](https://golang.org/pkg/time/#Time.Unix), i.e. the number of seconds elapsed since January 1, 1970 UTC) - `pid` - process ID of the language server - `ppid` - parent process ID (typically editor's or editor plugin's PID) The path is interpreted as [Go template](https://golang.org/pkg/text/template/), e.g. `/tmp/terraform-ls-cpuprofile-{{timestamp}}.log`. ## Memory Profiling If the bug you are reporting is related to high memory usage it may be helpful to collect and share memory profile which can be done via `memprofile` flag. For example you can modify the launch arguments in your editor to: ```sh $ terraform-ls serve \ -memprofile=/tmp/terraform-ls-mem.prof ``` Clients which manage LS installation typically allow passing extra arguments. See [Logging to Files](#logging-to-files) section above for examples. The target file will be truncated before being written into. ### Path Templating Path supports template syntax. This allows for separation of logs while accounting for multiple server or client instances. **`-memprofile`** supports the following functions: - `timestamp` - current timestamp (formatted as [`Time.Unix()`](https://golang.org/pkg/time/#Time.Unix), i.e. the number of seconds elapsed since January 1, 1970 UTC) - `pid` - process ID of the language server - `ppid` - parent process ID (typically editor's or editor plugin's PID) The path is interpreted as [Go template](https://golang.org/pkg/text/template/), e.g. `/tmp/terraform-ls-memprofile-{{timestamp}}.log`. --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/USAGE.md # Usage of Terraform Language Server This guide assumes you have installed the server by following instructions in the [README.md](../README.md) if that is applicable to your client (i.e. if the client doesn't download the server itself). The following filetypes are supported by the Terraform Language Server: - `terraform` - standard `*.tf` config files - `terraform-vars` - variable files (`*.tfvars`) - `terraform-stack` - standard `*.tfcomponent.hcl` and `*.tfstack.hcl` files - `terraform-deploy` - standard `*.tfdeploy.hcl` files - `terraform-search` - standard `*.tfquery.hcl` files _NOTE:_ Clients should be configured to follow the above language ID conventions and do **not** send `*.tf.json`, `*.tfvars.json` nor Packer HCL config nor any other HCL config files as the server is not equipped to handle these file types. Most clients with a dedicated Terraform extension/plugin already have the default configuration, so you should not need to worry about it. Instructions for popular IDEs are below and pull requests for updates or addition of more IDEs are welcomed. See also [settings](./SETTINGS.md) to understand how you may configure the settings. ## Workspaces / Folders / Files Most text editors allow you to open files to edit a single file, or a folder to edit many files at once. When opening a folder, this is commonly referred to as "workspace" or "root folder". Opening folders is always preferred over individual files as it allows the language server to index the whole folder and keep track of changes more easily. We do however support "single-file mode" which provides limited IntelliSense. Indexing enables IntelliSense related to `module` blocks, such as go-to-definition, completion of `module.*` references, or workspace-wide symbol lookup. The server will _not_ index any folders or files above the workspace root initially opened in the editor. ## Editors ### Visual Studio Code - Install the [Terraform VS Code Extension](https://marketplace.visualstudio.com/items?itemName=hashicorp.terraform) `>=2.24.0` - The latest compatible version of [terraform-ls](https://github.com/hashicorp/terraform-ls) is bundled with the extension - See [VS Code Configuration](https://github.com/hashicorp/vscode-terraform/blob/main/README.md#configuration) in case you need to tweak anything. Default settings should work for majority of users. ### Sublime Text - Install the [LSP package](https://github.com/sublimelsp/LSP#installation) - Install the [LSP-terraform package](https://github.com/sublimelsp/LSP-terraform#installation) ### Vim / NeoVim #### coc.nvim - Install the [coc.nvim plugin](https://github.com/neoclide/coc.nvim) - Add the following snippet to the `coc-setting.json` file (editable via `:CocConfig` in NeoVim) ```json { "languageserver": { "terraform": { "command": "terraform-ls", "args": ["serve"], "filetypes": ["terraform", "tf"], "initializationOptions": {}, "settings": {} } } } ``` Make sure to read through the [example vim configuration](https://github.com/neoclide/coc.nvim#example-vim-configuration) of the plugin, especially key remapping, which is required for completion to work correctly: ```vim " Use to trigger completion. inoremap coc#refresh() ``` #### vim-lsp - [Install](https://opensource.com/article/20/2/how-install-vim-plugins) the following plugins: - [async.vim plugin](https://github.com/prabirshrestha/async.vim) - [vim-lsp plugin](https://github.com/prabirshrestha/vim-lsp) - [asyncomplete.vim plugin](https://github.com/prabirshrestha/asyncomplete.vim) - [asyncomplete-lsp.vim plugin](https://github.com/prabirshrestha/asyncomplete-lsp.vim) - Add the following to your `.vimrc`: ```vim if executable('terraform-ls') au User lsp_setup call lsp#register_server({ \ 'name': 'terraform-ls', \ 'cmd': {server_info->['terraform-ls', 'serve']}, \ 'whitelist': ['terraform'], \ }) endif ``` #### YouCompleteMe - [Install](https://opensource.com/article/20/2/how-install-vim-plugins) the following plugins: - [YouCompleteMe plugin](https://github.com/ycm-core/YouCompleteMe) - Add the following to your `.vimrc`: ```vim " Remove this line if additional custom language servers are set elsewhere let g:ycm_language_server = [] if executable('terraform-ls') let g:ycm_language_server += [ \ { \ 'name': 'terraform', \ 'cmdline': [ 'terraform-ls', 'serve' ], \ 'filetypes': [ 'terraform' ], \ 'project_root_files': [ '*.tf', '*.tfvars' ], \ }, \ ] endif ``` #### LanguageClient-neovim - Install the [LanguageClient-neovim plugin](https://github.com/autozimu/LanguageClient-neovim) - Add the following to your `.vimrc`: ```vim let g:LanguageClient_serverCommands = { \ 'terraform': ['terraform-ls', 'serve'], \ } ``` #### Neovim v0.5.0+ - Install the [nvim-lspconfig plugin](https://github.com/neovim/nvim-lspconfig) - Add the following to your `.vimrc` or `init.vim`: ```vim lua < Languages - In _Language-specific settings_ section, add an entry for Terraform - In the Server tab, Set _Command_ to `terraform-ls` and _Arguments_ to `serve` - Once you've correctly installed `terraform-ls` and configured BBEdit, the status indicator on this settings panel will flip to green - If you'd like to pass any [settings](./SETTINGS.md) to the server you can do so via the _Arguments_ field. ### Kate KDE [Kate editor](https://kate-editor.org/) supports LSP and is user configurable. - Install the `terraform-ls` package (or the equivalent package name appropriate to your distro) - Open Kate configuration (Settings Menu -> `Configure` Kate or Kate -> `Preferences` on macOS) - Select _LSP Client_ in the left pane - Select _User Server Settings_ tab - Paste the following JSON and _Save_: ```json { "servers": { "terraform": { "command": ["terraform-ls", "serve"], "url": "https://github.com/hashicorp/terraform-ls", "highlightingModeRegex": "^Terraform$", "rootIndicationFileNames": ["*.tf", "*.tfvars"] } } } ``` - Restart of the editor should _not_ be necessary. --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/architecture.md # Language Server Architecture This is a summary of some main components of the language server, aiming to help maintainers and contributors with navigating the codebase. ## Decoder Majority of the language server functionality such as completion, hover, document links, semantic tokens, symbols etc. are provided by [the `decoder` package of `hashicorp/hcl-lang`](https://pkg.go.dev/github.com/hashicorp/hcl-lang@main/decoder). `hcl-lang` is generally considered a reusable component for any HCL2-based language server (that is not _just_ Terraform). Any functionality which other HCL2-based language server may reuse should be contributed there, not into `terraform-ls`. The decoder essentially takes in directories of parsed HCL files + schemas and uses both to walk the AST to provide completion candidates, hover data and other relevant data. ![decoder-flow](./images/decoder-flow.png) ## Schema Decoder needs schema to produce relevant completion candidates, hover data etc. [`hashicorp/terraform-schema`](https://pkg.go.dev/github.com/hashicorp/terraform-schema) houses most of the Terraform Core schema (such as `terraform`, `resource` or `variable` blocks) + helpers to combine that [Core schema](https://github.com/hashicorp/terraform-schema/tree/main/internal/schema) with provider schemas (such as inner parts of `resource` or `data` blocks) and help assemble schemas for modules. ![schema-merging](./images/schema-merging.png) ## Global State Most of the global state is maintained within various [`go-memdb`](https://pkg.go.dev/github.com/hashicorp/go-memdb) tables under [`state`](https://pkg.go.dev/github.com/hashicorp/terraform-ls@main/internal/state) package, passed around via [`state.StateStore`](https://pkg.go.dev/github.com/hashicorp/terraform-ls@main/internal/state#StateStore). This includes - `documents` - documents open by the client (see [Document Storage](#document-storage)) - `jobs` - pending/running jobs (see [Job Scheduler](#job-scheduler)) - `modules` - AST and other metadata about Terraform modules collected by indexing _jobs_ ^ - `provider_schemas` - provider schemas pre-baked or obtained via Terraform CLI by indexing _jobs_ ^ - `provider_ids` & `module_ids` - mapping between potentially sensitive identifiers and randomly generated UUIDs, to enable privacy-respecting telemetry ## Document Storage `documents` package, and [`document.Document`](https://pkg.go.dev/github.com/hashicorp/terraform-ls@main/internal/document#Document) struct in particular represents open documents server receives from the client via LSP text synchronization methods such as `textDocument/didOpen`, `textDocument/didChange`, stored as an entry in the `documents` memdb table. The `textDocument/didClose` method removes the document from state, making other components assume that it then matches OS filesystem. AST representation of these documents is passed to the decoder, which in turn ensures that all completion candidates, hover data etc. is relevant to what the user sees in their editor window even if the file/document is not saved. Each document also maintains line-separated version, to enable line-based diffing and to enable conversion between LSP's representation of position (line:column) to HCL's representation (`hcl.Pos`) which mostly uses byte offsets. ## Filesystem `filesystem` package provides an `io/fs` compatible interface primarily for any jobs which need to operate on the whole directory (Terraform module) regardless of where the file contents comes from (virtual document or OS filesystem). ![filesystem-decision-logic](./images/filesystem-decision-logic.png) ## LSP/RPC Layer `langserver` package represents the RPC layer responsible for processing any incoming and outgoing LSP (RPC JSON) requests/responses between the server and client. The `langserver/handlers` package generally follows a pattern of 1 file per LSP method. The package also contains E2E tests which exercise the language server from client's perspective. [`service.go`](https://github.com/hashicorp/terraform-ls/blob/main/internal/langserver/handlers/service.go) represents the "hot path" of the LSP/RPC layer, basically mapping functions to method names which the server supports. [`protocol`](https://pkg.go.dev/github.com/hashicorp/terraform-ls@main/internal/protocol) package represents the structs reflecting LSP spec, i.e. the structure of request and response JSON bodies. Given that there is no other complete and/or well-maintained representation of the LSP spec for Go (at the time of writing), majority of this is copied from within `gopls`, which in turn generates these from the TypeScript SDK - practically the only officially maintained and most complete implementation of LSP spec to date. Mentioned `protocol` request/response representations may not always be practical throughout the codebase and within `hcl-lang`, therefore `lsp` package contains various helpers to convert the `protocol` types from and to other internal types we use to represent the same data. It also filters and checks the data using client and server capabilities, such that other parts of the codebase don't have to. ## "Features" The `internal/features` package tries to group certain "dialects" of the Terraform language into self-contained features. A feature manages its own state, jobs, decoder, and file parsing logic. We currently have several features: - `*.tf` and `*.tf.json` files are handled in the `modules` feature - `*.tfvars` and `*.tfvars.json` files are handled in the `variables` feature - `.terraform/` and `.terraform.lock.hcl` related operations are handled in the `rootmodules` feature - `*.tfcomponent.hcl`, `*.tfstack.hcl` and `*.tfdeploy.hcl` files are handled in the `stacks` feature - `*.tfquery.hcl` files are handled in the `search` feature A feature can provide data to the external consumers through methods. For example, the `variables` feature needs a list of variables from the `modules` feature. There should be no direct import from feature packages (we could enforce this by using `internal/`, but we won't for now) into other parts of the codebase. The "hot path" service mentioned above takes care of initializing each feature at the start of a new LS session. The `jobs` package of each feature contains all the different indexing jobs needed to retrieve all kinds of data and metadata, to perform completion, hover, go-to-definition, and so on. The jobs are scheduled on the global job scheduler as a result of various events (e.g. `didOpen`). ### Modules Feature Jobs - `ParseModuleConfiguration` - parses `*.tf` files to turn `[]byte` into `hcl` types (AST) - `LoadModuleMetadata` - uses [`earlydecoder`](https://pkg.go.dev/github.com/hashicorp/terraform-schema@main/earlydecoder) to do early TF version-agnostic decoding to obtain metadata (variables, outputs etc.) which can be used to do more detailed decoding in hot-path within `hcl-lang` decoder - `PreloadEmbeddedSchema` – loads provider schemas based on provider requirements from the bundled schemas - `DecodeReferenceTargets` - uses `hcl-lang` decoder to collect reference targets within `*.tf` - `DecodeReferenceOrigins` - uses `hcl-lang` decoder to collect reference origins within `*.tf` - `GetModuleDataFromRegistry` - obtains data about any modules (inputs & outputs) from the Registry API based on module calls - `SchemaModuleValidation` - does schema-based validation of module files (`*.tf`) and produces diagnostics associated with any "invalid" parts of code - `ReferenceValidation` - does validation based on (mis)matched reference origins and targets, to flag up "orphaned" references - `TerraformValidate` - uses Terraform CLI to run the validate subcommand and turn the provided (JSON) output into diagnostics ### Variables Feature Jobs - `ParseVariables` - parses `*.tfvars` files to turn `[]byte` into `hcl` types (AST) - `DecodeVarsReferences` - uses `hcl-lang` decoder to collect references within `*.tfvars` - `SchemaVariablesValidation` - does schema-based validation of variable files (\*.tfvars) and produces diagnostics associated with any "invalid" parts of code ### Root Modules Feature Jobs - `GetTerraformVersion` - obtains Terraform version via `terraform version -json` - `ParseModuleManifest` - parses module manifest with metadata about any installed modules - `ObtainSchema` - obtains provider schemas via `terraform providers schema -json` - `ParseProviderVersions` is a job complimentary to `ObtainSchema` in that it obtains versions of providers/schemas from Terraform CLI's lock file ### Stack Feature Jobs - `ParseStackConfiguration` - parses `*.tfcomponent.hcl`, `*.tfstack.hcl` and `*.tfdeploy.hcl` files to turn `[]byte` into `hcl` types (AST) - `LoadStackMetadata` - uses [`earlydecoder`](https://pkg.go.dev/github.com/hashicorp/terraform-schema@main/earlydecoder/stacks) to do early TF version-agnostic decoding to obtain metadata (variables, outputs etc.) which can be used to do more detailed decoding in hot-path within `hcl-lang` decoder - `PreloadEmbeddedSchema` – loads provider schemas based on provider requirements from the bundled schemas - `DecodeReferenceTargets` - uses `hcl-lang` decoder to collect reference targets within `*.tfcomponent.hcl`, `*.tfstack.hcl` and `*.tfdeploy.hcl` - `DecodeReferenceOrigins` - uses `hcl-lang` decoder to collect reference origins within `*.tfcomponent.hcl`, `*.tfstack.hcl` and `*.tfdeploy.hcl` - `SchemaStackValidation` - does schema-based validation of module files (`*.tfcomponent.hcl`, `*.tfstack.hcl` and `*.tfdeploy.hcl`) and produces diagnostics associated with any "invalid" parts of code - `ReferenceValidation` - does validation based on (mis)matched reference origins and targets, to flag up "orphaned" references ### Search Feature Jobs - `ParseSearchConfiguration` - parses `*.tfquery.hcl` files to turn `[]byte` into `hcl` types (AST) - `LoadSearchMetadata` - uses [`earlydecoder`](https://pkg.go.dev/github.com/hashicorp/terraform-schema@main/earlydecoder/search) to do early TF version-agnostic decoding to obtain metadata (variables, outputs etc.) which can be used to do more detailed decoding in hot-path within `hcl-lang` decoder - `PreloadEmbeddedSchema` – loads provider schemas based on provider requirements from the bundled schemas - `DecodeReferenceTargets` - uses `hcl-lang` decoder to collect reference targets within `*.tfquery.hcl` - `DecodeReferenceOrigins` - uses `hcl-lang` decoder to collect reference origins within `*.tfquery.hcl` - `SchemaSearchValidation` - does schema-based validation of module files (`*.hcl.hcl`) and produces diagnostics associated with any "invalid" parts of code - `ReferenceValidation` - does validation based on (mis)matched reference origins and targets, to flag up "orphaned" references ### Adding a new feature / "language" The existing `variables` feature is a good starting point when introducing a new language. Usually you need to roughly follow these steps to get a minimal working example: 1. Create a new feature with the same folder structure as existing ones 1. Model the internal state representation 1. Subscribe to some events of the event bus 1. Add a parsing job that gets triggered from an event 1. Add a decoder that makes use of some kind of schema 1. Register the new feature in `internal/langserver/handlers/service.go` - Start the feature as part of `configureSessionDependencies()` - Make sure to call the `Stop()` function in `shutdown()` as well 1. If the feature reports diagnostics, add a call to collect them in `updateDiagnostics()` in `internal/langserver/handlers/hooks_module.go` ## Job Scheduler All jobs end up in the `jobs` memdb table, from where they're picked up from by any of the two schedulers described below. [`scheduler`](https://pkg.go.dev/github.com/hashicorp/terraform-ls@main/internal/scheduler) contains a relatively general-purpose implementation of a job scheduler. There are [two instances](https://github.com/hashicorp/terraform-ls/blob/031e30f62ab169104837fbb1e9ef2633ded73329/internal/langserver/handlers/service.go#L427-L435) of the scheduler in use, both of which are launched by `initialize` LSP request and shut down with `shutdown` LSP request. - `openDirIndexer` processes any jobs concerning directories which have any files open - `closedDirIndexer` processes any jobs concerning directories which do _not_ have any files open The overall flow of jobs is illustrated in the diagram below. ![job-scheduler-flow](./images/job-scheduler-flow.png) The mentioned `documents` memdb table is consulted for whether a directory has any open files - i.e. whether server has received `textDocument/didOpen` and _not_ `textDocument/didClose` concerning a particular directory. Using two separate schedulers loosely reflects the fact that data for files which the user is editing at the moment are more critical, unlike additional data about other directories/modules which would only _enrich_ editing of the open files (such as by adding cross-module context, providing go-to-definition etc.). Jobs also depend on each other. These dependencies are illustrated in the diagrams below. ### didOpen Job Flow ![didOpen-job-flow](./images/didopen-job-flow.png) ## Event Bus The [`eventbus`](https://github.com/hashicorp/terraform-ls/blob/main/internal/eventbus/bus.go) is responsible for distributing events to subscribers. It comes with a fixed list of topics that anyone can subscribe to. An event is sent to all subscribers of a topic. A subscriber can decide to block until the event is processed by using a return channel. It is primarily used to distribute LSP document synchronization events. ### Event Sources ![event-bus-triggers](./images/event-bus-triggers.png) ## Walker The Walker is responsible for walking the file system hierarchy of the entire workspace (including files that the user may not have open) in the background to gain a better understanding of the workspace structure. The walker doesn't schedule any jobs and doesn't do any additional work other than reporting the directory structure and the files it contains. The walker follows the LSP/RPC lifecycle of the server, i.e. it is started by an `initialize` request and shut down by a `shutdown` request. The walker logic is contained in [`internal/walker/walker.go`](https://github.com/hashicorp/terraform-ls/blob/main/internal/walker/walker.go). ## Watched Files Clients are expected to watch `*.tf` and `*.tfvars` files by default and send updates to the server via [`workspace/didChangeWatchedFiles` notifications](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeWatchedFiles). Additionally, the server uses [dynamic watcher registration](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#didChangeWatchedFilesRegistrationOptions) per LSP to instruct clients to watch for plugin and module lock files within `.terraform` directories, such that it can refresh schemas or module metadata, both of which can be used to provide IntelliSense. The mentioned dynamic registration happens as part of [`initialized`](https://github.com/hashicorp/terraform-ls/blob/ca335f5ec3f320ab5a517592ae63ac90b04f127f/internal/langserver/handlers/initialized.go#L22-L71). [`workspace/didChangeWatchedFiles` handler](https://github.com/hashicorp/terraform-ls/blob/ca335f5ec3f320ab5a517592ae63ac90b04f127f/internal/langserver/handlers/did_change_watched_files.go#L20) invalidates relevant data based on what files were changed. --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/benchmarks.md # Benchmarks There is a few factors which affect how much CPU and memory the language server uses. - amount of (initialized or referenced) modules within any workspace - amount of providers used within these modules - size of the providers (schema) used within these modules (i.e. amount of resources, data sources and attributes within) While we generally aim to keep both CPU and memory usage low, we'd consider optimizing for lower CPU usage as a higher priority than lower memory usage. i.e. we trade lower CPU for higher memory where such a trade-off is necessary. We optimize for what we consider the common case, which is approximately 1-100 reasonably sized modules, each typically with a single provider, within a workspace. ## Benchmarked Modules We run benchmarks with the following modules which estimates the **average time to index** + **average memory allocation** on `Standard_DS2_v2` MS Azure VMs (via [GitHub-hosted runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#cloud-hosts-for-github-hosted-runners)). - [nearly empty local module with no submodule and no provider](../internal/langserver/handlers/testdata/single-module-no-provider) - `15ms` - `1.7MB` - [local module with a single submodule, no provider](../internal/langserver/handlers/testdata/single-submodule-no-provider) - `225ms` - `5.9MB` - [local module with the `random` provider](../internal/langserver/handlers/testdata/single-module-random) - `220ms` - `2.5MB` - [local module with the `aws` provider](../internal/langserver/handlers/testdata/single-module-aws) - `1.5s` - `119MB` - [aws-consul](https://github.com/hashicorp/terraform-aws-consul) - `1.6s` - `158MB` - [aws-eks](https://registry.terraform.io/modules/terraform-aws-modules/eks/aws) - `1.9s` - `253MB` - [aws-vpc](https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws) - `1.7s` - `198MB` - [google-project](https://registry.terraform.io/modules/terraform-google-modules/project-factory/google) - `1.9s` - `217MB` - [google-network](https://registry.terraform.io/modules/terraform-google-modules/network/google) - `1.8s` - `267MB` - [google-gke](https://registry.terraform.io/modules/terraform-google-modules/kubernetes-engine/google) - `3.3s` - `404MB` - [k8s-metrics-server](https://registry.terraform.io/modules/cookielab/metrics-server/kubernetes) - `1.6s` - `65MB` - [k8s-dashboard](https://registry.terraform.io/modules/cookielab/dashboard/kubernetes) - `2.0s` - `68MB` Sections below provide more details on the factors affecting the usage and usage patterns. ## CPU usage The server is expected to consume a little more CPU upon launch until it indexes the workspace and runs various commands in these folders, such as `terraform providers schema -json` or `terraform version`. Based on benchmarking various publicly available modules, we expect indexing of a single module to take between `200ms` and `2s`. This will vary depending on the amount of installed _submodules_ of that module and size of provider schema (with AWS representing possibly the largest publicly known provider). The indexers currently don't attempt to utilize more than 1 CPU core (i.e. parallelise), to reduce CPU spikes but we may consider making this opt-in in the future to allow users trade reduced indexing time for higher CPU usage where this makes sense. ## Memory usage The server is expected to consume around 300 MB upon launch without any open/indexed files. The majority of these 300 MB is consumed by the embedded schemas of approximately 200 providers. Every open file will be stored in memory along with its AST, diagnostics, references and other metadata. Similar to embedded schemas, any schemas obtained locally from any installed providers via `terraform providers schema -json` will be persisted and likely take up most of the total memory footprint of the server. --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/code-actions.md # Code Actions The Terraform Language Server implements a set of Code Actions which perform different actions on the current document. These commands are typically code fixes to either refactor code, fix problems or to beautify/refactor code. ## Available Actions ### `source.formatAll.terraform` The server will format a given document according to Terraform formatting conventions. ## Usage ### VS Code To enable the format code action globally, set `source.formatAll.terraform` to *true* for the `editor.codeActionsOnSave` setting and set `editor.formatOnSave` to *false*. ```json "editor.formatOnSave": false, "editor.codeActionsOnSave": { "source.formatAll.terraform": true }, "[terraform]": { "editor.defaultFormatter": "hashicorp.terraform", } ``` > *Important:* Disable `editor.formatOnSave` if you are using `source.formatAll.terraform` in `editor.codeActionsOnSave`. The `source.formatAll.terraform` code action is is meant to be used instead of `editor.formatOnSave`, as it provides a [guarantee of order of execution](https://github.com/microsoft/vscode-docs/blob/71643d75d942e2c32cfd781c2b5322521775fb4a/release-notes/v1_44.md#explicit-ordering-for-editorcodeactionsonsave) based on the list provided. If you have both settings enabled, then your document will be formatted twice. If you would like `editor.formatOnSave` to be *true* for other extensions but *false* for the Terraform extension, you can configure your settings as follows: ```json "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.formatAll.terraform": true }, "[terraform]": { "editor.defaultFormatter": "hashicorp.terraform", "editor.formatOnSave": false, }, ``` Alternatively, you can include all terraform related Code Actions inside the language specific setting if you prefer: ```json "editor.formatOnSave": true, "[terraform]": { "editor.defaultFormatter": "hashicorp.terraform", "editor.formatOnSave": false, "editor.codeActionsOnSave": { "source.formatAll.terraform": true }, }, ``` --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/commands.md # Commands The server exposes the following executable commands via LSP to clients. Typically these commands are not invokable by end-users automatically. Instead this serves as a documentation for client maintainers, and clients may expose these e.g. via command palette where appropriate. Every care is taken to avoid breaking changes, but these interfaces should not be considered stable yet and may change. Either way clients should always follow LSP spec in the sense that they check whether a command is actually supported or not (via `ServerCapabilities.executeCommandProvider.commands`). ## Command Prefix All commands use `terraform-ls.` prefix to avoid any conflicts with commands registered by any other language servers user may be using at the same time. Some clients may also choose to generate additional prefix where e.g. the language server runs in multiple instances and registering the same commands would lead to conflicts. This can be passed as part of `initializationOptions`, as documented in [Settings](./SETTINGS.md#commandprefix). ## Arguments All commands accept arguments as string arrays with `=` used as a separator between key and value. i.e. ```json { "command": "command-name", "arguments": [ "key=value" ] } ``` ## Supported Commands ### `terraform.init` Runs [`terraform init`](https://www.terraform.io/docs/cli/commands/init.html) using available `terraform` installation from `$PATH`. **Arguments:** - `uri` - URI of the directory in which to run `terraform init` **Outputs:** Error is returned e.g. when `terraform` is not installed, or when execution fails, but no output is returned if `init` successfully finishes. ### `terraform.validate` Runs [`terraform validate`](https://www.terraform.io/docs/cli/commands/validate.html) using available `terraform` installation from `$PATH`. Any violations are published back the the client via [`textDocument/publishDiagnostics` notification](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_publishDiagnostics). Diagnostics are not persisted and any document change will cause them to be lost. **Arguments:** - `uri` - URI of the directory in which to run `terraform validate` **Outputs:** Error is returned e.g. when `terraform` is not installed, or when execution fails, but no output is returned if `validate` successfully finishes. ### `module.callers` In Terraform module hierarchy "callers" are modules which _call_ another module via `module "..." {` blocks. Language server will attempt to discover any module hierarchy within the workspace and this command can be used to obtain the data about such hierarchy, which can be used to hint the user e.g. where to run `init` or `validate` from. **Arguments:** - `uri` - URI of the directory of the module in question, e.g. `file:///path/to/network` **Outputs:** - `v` - describes version of the format; Will be used in the future to communicate format changes. - `callers` - array of any modules found in the workspace which call the module in question - `uri` - URI of the directory (absolute path) ```json { "v": 0, "callers": [ { "uri": "file:///path/to/dev", }, { "uri": "file:///path/to/prod", } ] } ``` ### `module.calls` List of modules called by the module under the given URI. Empty array may be returned when e.g. - the URI doesn't represent a module - the configuration is invalid - there are no module calls The data is sourced from the declared modules inside the files of the module. **Arguments:** - `uri` - URI of the directory of the module in question, e.g. `file:///path/to/network` **Outputs:** - `v` - describes version of the format; Will be used in the future to communicate format changes. - `module_calls` - array of modules which are called from the module in question - `name` - the reference name of this particular module (i.e. `network` from `module "network" { ...`) - `source_addr` - human-readable version of the source address given for this module call (e.g. `terraform-aws-modules/eks/aws`) - `version` - version constraint of the module call; applicable to modules hosted by the Terraform Registry (e.g. `~> 1.0` - `source_type` - source of the Terraform module, e.g. `github` or `tfregistry` - `docs_link` - a link to the module documentation; if available - `dependent_modules` - **DEPRECATED** (always empty in `v0.29+`) - array of dependent modules with the same structure as `module_calls` ```json { "v": 0, "module_calls": [ { "name": "child", "source_addr": "./child", "source_type": "local", "dependent_modules": [] }, { "name": "vpc", "source_addr": "terraform-aws-modules/vpc/aws", "version": "3.11.0", "source_type": "tfregistry", "docs_link": "https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/3.11.0", "dependent_modules": [] } ] } ``` ### `module.providers` Provides information about the providers of the current module, including requirements and installed version. **Arguments:** - `uri` - URI of the directory of the module in question, e.g. `file:///path/to/network` **Outputs:** - `v` - describes version of the format; Will be used in the future to communicate format changes. - `provider_requirements` - map of provider FQN string to requirements object - `display_name` - a human-readable name of the provider (e.g. `hashicorp/aws`) - `version_constraint` - a comma-separated list of version constraints (e.g. `>= 1.0, < 1.2`) - `docs_link` - a link to the provider documentation; if available - `installed_providers` - map where _key_ is the provider FQN and _value_ is the installed version of the provider; can be empty if none are installed ```json { "v": 0, "provider_requirements": { "registry.terraform.io/hashicorp/aws": { "display_name": "hashicorp/aws", "version_constraint": "~> 3.64.0", "docs_link": "https://registry.terraform.io/providers/hashicorp/aws/latest" } }, "installed_providers": { "registry.terraform.io/hashicorp/aws": "3.64.2" } } ``` ### `module.terraform` Provides information about the terraform binary version for the current module. **Arguments:** - `uri` - URI of the directory of the module in question, e.g. `file:///path/to/network` **Outputs:** - `v` - describes version of the format; Will be used in the future to communicate format changes. - `required_version` - Version constraint specified in configuration - `discovered_version` - Version discovered from `terraform version --json` in the directory specified in `uri` ```json { "v": 0, "required_version": "~> 0.15", "discovered_version": "1.1.0" } ``` --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/features.md # Features of the server ## LSP methods The LSP is a relatively extensive protocol with many features/methods, not all of which are implemented and not all of which are relevant for Terraform. The following matrix should provide some visibility into the current and future state. It's important to note that ✅ does **not** imply the functionality is _fully_ implemented (some features have various opt-in capabilities), just that the method is in use by the server. You can filter any known open issues by the relevant label, e.g. [`textDocument/completion` issues](https://github.com/hashicorp/terraform-ls/issues?q=is%3Aopen+is%3Aissue+label%3AtextDocument%2Fcompletion) and open new issues for any method which you would like to be implemented. ### Requests | LSP method | Implemented | Note | | :--- | :----: | :--- | | callHierarchy/incomingCalls | ❌ | | | callHierarchy/outgoingCalls | ❌ | | | client/registerCapability | ❌ | | | client/unregisterCapability | ❌ | | | codeAction/resolve | ❌ | | | codeLens/resolve | ❌ | | | completionItem/resolve | ✅ | | | documentLink/resolve | ❌ | | | initialize | ✅ | | | inlayHint/resolve | ❌ | | | shutdown | ✅ | | | textDocument/codeAction | ✅ | See [code-actions.md](https://github.com/hashicorp/terraform-ls/blob/main/docs/code-actions.md) | | textDocument/codeLens | ✅ | See [Code Lens section](https://github.com/hashicorp/terraform-ls/blob/main/docs/language-clients.md#code-lens) | | textDocument/colorPresentation | ❌ | Not relevant | | textDocument/completion | ✅ | | | textDocument/declaration | ✅ | | | textDocument/definition | ✅ | | | textDocument/diagnostic | ❌ | | | textDocument/documentColor | ❌ | Not relevant | | textDocument/documentHighlight | ❌ | | | textDocument/documentLink | ✅ | | | textDocument/documentSymbol | ✅ | | | textDocument/foldingRange | ❌ | | | textDocument/formatting | ✅ | | | textDocument/hover | ✅ | | | textDocument/implementation | ❌ | | | textDocument/inlayHint | ❌ | | | textDocument/inlineValue | ❌ | | | textDocument/linkedEditingRange | ❌ | | | textDocument/moniker | ❌ | | | textDocument/onTypeFormatting | ❌ | | | textDocument/prepareCallHierarchy | ❌ | | | textDocument/prepareRename | ❌ | | | textDocument/prepareTypeHierarchy | ❌ | | | textDocument/rangeFormatting | ❌ | | | textDocument/references | ✅ | | | textDocument/rename | ❌ | | | textDocument/selectionRange | ❌ | | | textDocument/semanticTokens/full | ✅ | See [syntax-highlighting.md](https://github.com/hashicorp/terraform-ls/blob/main/docs/syntax-highlighting.md#semantic-tokens) | | textDocument/semanticTokens/full/delta | ❌ | | | textDocument/semanticTokens/range | ❌ | | | textDocument/signatureHelp | ✅ | | | textDocument/typeDefinition | ❌ | | | textDocument/willSaveWaitUntil | ❌ | | | typeHierarchy/subtypes | ❌ | | | typeHierarchy/supertypes | ❌ | | | window/showDocument | ❌ | | | window/showMessageRequest | ✅ | | | window/workDoneProgress/create | ❌ | | | workspace/applyEdit | ❌ | | | workspace/codeLens/refresh | ✅ | | | workspace/configuration | ❌ | | | workspace/diagnostic | ❌ | | | workspace/diagnostic/refresh | ❌ | | | workspace/executeCommand | ✅ | See [commands.md](https://github.com/hashicorp/terraform-ls/blob/main/docs/commands.md) | | workspace/inlayHint/refresh | ❌ | | | workspace/inlineValue/refresh | ❌ | | | workspace/semanticTokens/refresh | ✅ | See [syntax-highlighting.md](https://github.com/hashicorp/terraform-ls/blob/main/docs/syntax-highlighting.md#semantic-tokens) | | workspace/symbol | ✅ | | | workspace/willCreateFiles | ❌ | | | workspace/willDeleteFiles | ❌ | | | workspace/willRenameFiles | ❌ | | | workspace/workspaceFolders | ✅ | | | workspaceSymbol/resolve | ❌ | | List of methods sourced via ```sh curl -s https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/metaModel/metaModel.json | jq -r '.requests[].method' | sort ``` ### Notifications | LSP method | Implemented | Note | | :--- | :----: | :--- | | $/cancelRequest | ✅ | | | $/logTrace | ❌ | | | $/progress | ✅ | | | $/setTrace | ❌ | | | exit | ✅ | | | initialized | ✅ | | | notebookDocument/didChange | ❌ | | | notebookDocument/didClose | ❌ | | | notebookDocument/didOpen | ❌ | | | notebookDocument/didSave | ❌ | | | telemetry/event | ✅ | See [telemetry.md](https://github.com/hashicorp/terraform-ls/blob/main/docs/telemetry.md) | | textDocument/didChange | ✅ | | | textDocument/didClose | ✅ | | | textDocument/didOpen | ✅ | | | textDocument/didSave | ✅ | | | textDocument/publishDiagnostics | ✅ | | | textDocument/willSave | ❌ | | | window/logMessage | ❌ | | | window/showMessage | ✅ | | | window/workDoneProgress/cancel | ❌ | | | workspace/didChangeConfiguration | ❌ | | | workspace/didChangeWatchedFiles | ✅ | See [Watched Files section](https://github.com/hashicorp/terraform-ls/blob/main/docs/language-clients.md#watched-files) | | workspace/didChangeWorkspaceFolders | ✅ | | | workspace/didCreateFiles | ❌ | | | workspace/didDeleteFiles | ❌ | | | workspace/didRenameFiles | ❌ | | List of methods sourced via ```sh curl -s https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/metaModel/metaModel.json | jq -r '.requests[].method' | sort ``` --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/installation.md # Installation ## Automatic Installation Some editors have built-in logic to install and update the language server automatically, so you typically shouldn't need to worry about installation or updating of the server in these editors, as long as you use the linked extension. - Terraform VS Code extension [stable](https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform) / [preview](https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform-preview) - [Sublime Text LSP-terraform](https://packagecontrol.io/packages/LSP-terraform) ## Manual Installation You can install the language server manually using one of the many package managers available or download an archive from the release page. After installation, follow the [install instructions for your IDE](./USAGE.md) ### Homebrew (macOS / Linux) You can install via [Homebrew](https://brew.sh) ``` brew install hashicorp/tap/terraform-ls ``` This tap only contains stable releases (i.e. no pre-releases). ### Linux We support Debian & Ubuntu via apt and RHEL, CentOS, Fedora and Amazon Linux via RPM. You can follow the instructions in the [Official Packaging Guide](https://www.hashicorp.com/official-packaging-guide) to install the server from the official HashiCorp-maintained repositories. The package name is `terraform-ls` in all repositories. As documented in the Guide linked above, pre-releases are available through test repos. ### Other platforms 1. [Download for the latest version](https://releases.hashicorp.com/terraform-ls/) of the language server relevant for your operating system and architecture. 2. The language server is distributed as a single binary. Install it by unzipping it and moving it to a directory included in your system's `PATH`. 3. You can verify integrity by comparing the SHA256 checksums which are part of the release (called `terraform-ls__SHA256SUMS`). 4. Check that you have installed the server correctly via `terraform-ls -v`. You should see the latest version printed to your terminal. --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/language-clients.md # Language Client Implementation Notes This document contains notes for language client developers. ## Language IDs The following file types are currently supported and language IDs expected: - `terraform` - standard `*.tf` config files - `terraform-vars` - variable files (`*.tfvars`) - `terraform-stack` - standard `*.tfcomponent.hcl` and `*.tfstack.hcl` files - `terraform-deploy` - standard `*.tfdeploy.hcl` files - `terraform-search` - standard `*.tfquery.hcl` files Client can choose to highlight other files locally, but such other files must **not** be send to the server as the server isn't equipped to handle those. Clients specifically should **not** send `*.tf.json`, `*.tfvars.json` nor Packer HCL config nor any other HCL config files as the server is not equipped to handle these file types. ## Configuration Unless the client allows the end-user to pass arbitrary config options (e.g. generic Sublime Text LSP package without Terraform LSP package), the client should expose configuration as per [SETTINGS.md](./SETTINGS.md). Client should match the option names exactly, and if possible match the underlying data structures too. i.e. if a field is documented as `ignoreDirectoryNames`, it should be exposed as `ignoreDirectoryNames`, **not** ~`IgnoreDirectoryNames`~, or ~`ignore_directory_names`~. This is to avoid user confusion when the server refers to any config option in informative, warning, or error messages. Client may use a flat structure using the `.` (single dot) as a separator between the object name and option nested under it, such as `{ "foo.bar": "..." }` instead of `{ "foo": { "bar": "..." } }`. This is acceptable in situations when using objects is not possible or practical (e.g. VS Code wouldn't display objects in the Settings UI). The server will generally refer to options using the `.` address, for simplicity and avoidance of doubts. ## Watched Files The server (`>= 0.28.0`) supports `workspace/didChangeWatchedFiles` notifications. This allows IntelliSense to remain accurate e.g. when switching branches in VCS or when there are any other changes made to these files outside the editor. If the client implements file watcher, it should watch for any changes in `**/*.tf`, `**/*.tfvars`, `**/*.tfstack.hcl`, `**/*.tfcomponent.hcl`, `**/*.tfdeploy.hcl` and `**/*.tfquery.hcl` files in the workspace. Client should **not** send changes for any other files. ## Syntax Highlighting Read more about how we recommend Terraform files to be highlighted in [syntax-highlighting.md](./syntax-highlighting.md). ### Internal parser The server expects clients to use standard text synchronization LSP methods for synchronizing the above supported files. Server will itself parse the whole module in order to provide completion/hover and referencing throughout the module, not just within opened files. As a result the server maintains an "overlay virtual filesystem" for any files that client sends via LSP and where appropriate such files are treated as the main source of truth, so that functionality can be provided even before files are saved to disk. ## Use Client/Server Capabilities Please always make sure that your client reads and reflects [`ServerCapabilities`](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#serverCapabilities) and never makes blind assumptions about what is or is not supported. The server will always read [`ClientCapabilities`](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#clientCapabilities) and make decisions about whether to provide any LSP features based on those capabilities, so make sure these are accurate. For example the server will not provide completion snippets unless the client explicitly communicates it supports them via [`CompletionClientCapabilities`](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#completionClientCapabilities). ### Multiple Folders Language server supports multiple folders natively from version `0.19`. Client is expected to always launch a single instance of the server and check for [`workspace.workspaceFolders.supported`](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#workspaceFoldersServerCapabilities) server capability, and then: - launch any more instances (_one instance per folder_) if multiple folders are _not supported_ - avoid launching any more instances if multiple folders _are supported_ It is assumed that paths to these folders will be provided as part of `workspaceFolders` in the `initialize` request per LSP. ## Code Actions The server implements a set of opt-in code actions which perform different actions for the user. The code action request is sent from the client to the server to compute commands for a given text document and range. These commands are typically code fixes to either fix problems or to beautify/refactor code. See [code-actions](./code-actions.md) for a list of supported code actions. A Code Action is an action that changes content in the active editor. Each Code Action is grouped into kinds that have a `command` and/or a series of `edits`. They are triggered either by the user or through events. > Documentation for code actions outside of VS Code is unfortunately very limited beyond description of the LSP methods. VS Code internally makes certain assumptions. We follow these assumptions (as documented below) and we recommend other clients to follow these assumptions for best experience, unless/until LSP documentation recommends otherwise. ### Triggers In VS Code, code action can be _invoked manually_ or _automatically_ based on the respective [CodeActionTriggerKind](https://code.visualstudio.com/api/references/vscode-api#CodeActionTriggerKind). **Manually invoked** actions come from the contextual in-line💡 icon inside the editor, and are chosen by the user. The user can choose which action is invoked and _then_ invoke it. However, in order for the client to display the contextual actions, the client requests LS to "pre-calculate" any actions relevant to the cursor position. Then, when the user selects the action in the UI, the client applies the `edits` or executes the `command` as provided by the server. **Automatically triggered** actions come from events such as "on save", as configured via the `editor.codeActionsOnSave` setting. These usually do not give much choice to the user, they are either on or off, as they cannot accept user input. For example, formatting a document or removing simple style errors don't prompt for user action before or during execution. ### Kinds Each `Code Action` has a [`CodeActionKind`](https://code.visualstudio.com/api/references/vscode-api#CodeActionKind). `Code Action Kinds` are a hierarchical list of identifiers separated by `.`. For example in `refactor.extract.function`: `refactor` is the trunk, `extract` is the branch, and `function` is the leaf. The branches and leaves are the point of intended customization, you add new branches and/or leaves for each type of function you perform. In this example, a new code action that operated on variables would be called `refactor.extract.variable`. #### Custom Kinds Adding new roots or branches of the hierarchy is not emphasized or really encouraged. In most cases they are meant to be concepts that can be applied generically, not specifically to a certain language. For example, extracting a value to a variable is something common to most languages. You do not need a `refactor.extract.golang.variable` to extract a variable in Go, it still makes sense to use `refactor.extract.variable` whether in Go or in PowerShell. Keeping to existing `kinds` also helps in registration of supported code actions. If you register support for `source.fixAll` instead of `source.fixAll.languageId`, then a user that has `source.fixAll` in their `codeActionsOnSave` does not need to add specific lines for your extension, all supported extensions are called on save. Another reason is VS Code won't list your custom supported code actions inside `editor.codeActionsOnSave`, and there isn't a way for the extension to get them there. The user will have to consult your documentation to find out what actions are supported and add them without intellisense. A reason to add custom _kinds_ is if the action is sufficiently different from an existing base action. For example, formatting of the current file on save. The interpretation of `source.fixAll` is to _apply any/all actions that address an existing diagnostic and have a clear fix that does not require user input_. Formatting therefore doesn't fit the interpretation of `source.fixAll`. A custom kind `source.formatAll.terraform` may format code. A user can request both `source.fixAll` and `source.formatAll.terraform` via their editor/client settings and the server would run `source.formatAll.terraform` only. Other servers may run `source.fixAll` but not `source.formatAll.terraform`, assuming they do not support that custom code action kind. Unlike generic kinds, custom ones are only discoverable in server-specific documentation and only relevant to the server. ### Execution of Code Actions A request can have zero or more code actions to perform and the [LS is responsible for processing](https://github.com/microsoft/language-server-protocol/issues/970) all requested actions. The client will send a list of any code actions to execute (which may also be empty). An empty list means execute anything supported and return the list of edits to the client. This often comes from _manually invoked_ actions by the user. This is the easiest situation for the LS to choose what to do. The LS has a list of supported actions it can execute, so it executes and returns the edits. However, such actions will not include any that either require user input or make a change that could introduce an error (creating files, changing code structure, etc). A list of actions means to execute anything in that list, which is actually interpreted as _execute the hierarchy of a code action from these actions in the list_. For example, if a client requests a `refactor` action the LS should return all of the possible `refactor` actions it can execute (`refactor.extract`, `refactor.inline`, `refactor.rewrite`, etc.), and the user can choose which to execute. A client that sends `refactor.extract.method` would receive just that single code action from the LS. So a request with one action could return ten results, or just one. Clients are expected to filter actions to only send unique ones. For example, the user may have configured both `source.fixAll` and `source.fixAll.eslint` which are equivalent from the perspective of a single server (eslint). The client should favour the most specific (in this case `source.fixAll.eslint`) if possible, such that the server doesn't have to perform any de-duplication and the same action doesn't run multiple times. Clients may also impose a timeout for returning response for any of these requests. If the LS takes too long to process and return an action, the client may either give up and not do anything, or (preferably) display a progress indicator. This timeout may be configurable by the user, but ideally the default one is sufficient. ## Code Lens ### Reference Counts (opt-in) The server implements an opt-in code lens which displays number of references to any "root level" targettable block or attribute, such as local value, variable, resource etc. LSP has not standardized client-side command IDs nor does it provide mechanism for negotiating what the right command ID is and whether it's available. This is why **client has to opt-in by providing a command ID** in experimental client capabilities. For example: ```json { "capabilities": { "experimental": { "showReferencesCommandId": "client.showReferences" } } } ``` This enables the code lens. The client-side command is executed with 2 arguments (position, reference context): ```json [ { "line": 0, "character": 8 }, { "includeDeclaration": false } ] ``` These arguments are to be passed by the client to a subsequent [`textDocument/references`](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references) request back to the server to obtain the list of references relevant to that position and finally display received references in the editor. See [example implementation in the Terraform VS Code extension](https://github.com/hashicorp/vscode-terraform/pull/686). ## Custom Commands Clients are encouraged to implement custom commands in a command palette or similar functionality. See [./commands.md](./commands.md) for more. ## Telemetry See [./telemetry.md](./telemetry.md). --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/language-server.md # Language Server Implementation Notes ## How to add an Experimental Capability Add a new entry to the ExperimentalServerCapabilities struct in `internal/protocol/expertimental.go:6`: ```go type ExperimentalServerCapabilities struct { ReferenceCountCodeLens bool `json:"referenceCountCodeLens"` RefreshModuleProviders bool `json:"refreshModuleProviders"` RefreshModuleCalls bool `json:"refreshModuleCalls"` } ``` > Note the casing in the mapstructure field compared to the field name. Add a new method to retrieve the client side command id in `internal/protocol/expertimental.go`: ```go func (cc ExpClientCapabilities) NewItemHereCommandId() (string, bool) { if cc == nil { return "", false } cmdId, ok := cc["newItemHereCommandId"].(string) return cmdId, ok } ``` > Note that the command ID matches the experimental capabilities struct in expertimental.go` Add a new stanza to `internal/langServer/handlers/initialize.go:63` to pull the command ID and register the capability: ```go if _, ok := expClientCaps.NewItemHereCommandId(); ok { expServerCaps.NewItemHere = true properties["experimentalCapabilities.newItemHere"] = true } ``` > Note the casing in the proprties hash. Finally, register the command handler in `internal/langServer/handlers/service.go:454`: ```go if commandId, ok := lsp.ExperimentalClientCapabilities(cc.Experimental).NewItemHereCommandId(); ok { // do something with commandId here } ``` --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/schema.md # Why and how does the language server pull provider schemas? The Terraform Language Server needs to know about the names and structure of resources, data sources, provider blocks and provider-defined functions so that they can be suggested in completions or have their syntax validated. It retrieves this information from these sources: - Terraform built-in schema - Bundled provider schemas at compile time - Obtained from locally installed providers The language server comes with a built-in Terraform schema that handles resolving core Terraform constructs like resource, data, provider, backends, etc. We keep up with the release cadence of Terraform, from `0.12` to the latest, and detect which schema to use based on the `required_version` constraint in the declared `terraform` block and the locally installed Terraform version. The language server comes with "bundled" schemas that are stored in terraform-ls at compile time (officially released HashiCorp binaries). Since they are stored in the binary, terraform-ls does not need providers to be installed locally. Only the schemas of the latest version for the most common Terraform providers (all official and partner providers) are included because it makes the binary larger in size than it would otherwise be. While this has the advantage that most providers work out of the box, it has the disadvantage that only one specific version is bundled. If a user uses an older (or newer) version of the provider, they are likely to run into schema validation errors. The language server can also use locally installed providers in the `.terraform/providers` directory to get schema information. This is usually available after a user has run `terraform init`, which installs the provider binaries from the Terraform Registry. The language server will then obtain the schemas for all installed providers by executing the `terraform providers schema -json` command. This will result in the most accurate schema representation since the provider version is an exact match. ## Multi-Root Workspaces Provider schema selection is done on a best effort basis. We always try to pick the best matching version for the given provider constraints. For complex multi-root workspaces, this is difficult to get right, especially when modules don’t have a direct link to a root module. In this example, we currently don’t support picking the correct provider version for a module and may end up using the wrong provider version: ``` environments ├── production │ └── .terraform // has hashicorp/aws version 5.68.0 installed └── staging └── .terraform // has hashicorp/aws version 5.72.1 installed modules ├── a ├── b └── c // <- which version to use here? ``` ## Unexpected Attribute Errors The language server has a feature called “Enhanced validation”, where it compares the actual content of the configuration with the internal schemas. We treat the internal schema as the source of truth, so whenever we detect an extraneous or missing attribute or block, we raise an error. But if our internal schema versions don't match the exact version of a provider in use, this can lead to false negatives. # What about module schemas? Similar to provider schemas, the language server also handles module schemas. Module schemas mainly contain the inputs and outputs of a module. They power completion inside a module block and references to module outputs. It retrieves this information from these sources: - Reading local module files from disk - Dynamically retrieving module schemas from the Terraform Registry - Obtaining from locally installed remote modules For local modules that reference a local path in their `source` attribute, the language server will parse that module directory and extract all defined variables and outputs and their types. If a module source specifies a module that’s available in the **public** Terraform Registry, the language server will use the Registry API to fetch the module’s inputs and outputs. For all module sources (Public Registry, Private Registry, Git, GitHub, …) installed locally via `terraform init`, the language server can parse the module manifest (`.terraform/modules/modules.json`) and identify the installation location. It then parses the content in a similar way to local modules. --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/syntax-highlighting.md # Syntax Highlighting Highlighting syntax is one of the key features expected of any editor. Editors typically have a few different solutions to choose from. Below is our view on how we expect editors to highlight Terraform code while using this language server. ## Static Grammar Highlighting Terraform language syntax via static grammar (such as TextMate) _accurately_ may be challenging but brings more immediate value to the end user, since starting language server may take time. Also not all language clients may implement semantic token based highlighting. HashiCorp maintains a set of grammars in https://github.com/hashicorp/syntax and we encourage you to use the available Terraform grammar as the *primary* way of highlighting the Terraform language. ## Semantic Tokens [LSP (Language Server Protocol) 3.16](https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/) introduced language server-driven highlighting. This language server is better equipped to provide more contextual and accurate highlighting as it can parse the whole AST, unlike a TextMate grammar operating on a regex-basis. LSP 3.17 does support use cases where semantic highlighting is the only way to highlight a file (through [`augmentsSyntaxTokens` client capability](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokensClientCapabilities)). However in the context of the Terraform language we recommend semantic highlighting to be used as in *addition* to a static grammar - i.e. this server does _not_ support `augmentsSyntaxTokens: false` mode and is not expected to be used in isolation to highlight configuration. There are two main use cases we're targeting with semantic tokens. ### Improving Accuracy Regex-based grammars (like TextMate) operate on line-basis, which makes it difficult to accurately highlight certain parts of the syntax, for example nested blocks occurring in the Terraform language (as below). ```hcl terraform { required_providers { } } ``` Language server can use the AST and other important context (such as Terraform version or provider schema) to fully understand the whole configuration and provide more accurate highlighting. ### Custom Theme Support Many _default_ IDE themes are intended as general-purpose themes, highlighting token types, modifiers and scopes mappable to most languages. We recognize that theme authors would benefit from token types & modifiers which more accurately reflect the Terraform language. LSP spec doesn't _explicitly_ encourage defining custom token types or modifiers, however the default token types and modifiers which are part of the spec are not well suited to express all the different constructs of a DSL (Domain Specific Language), such as Terraform language. With that in mind we use the LSP client/server capability negotiation mechanism to provide the following custom token types & modifiers with fallback to the predefined ones. #### Token Types Primary token types are preferred if deemed supported by client per `SemanticTokensClientCapabilities.TokenTypes`, fallbacks are also only reported if client claim support (using the same capability). Fallback types are chosen based on meaningful semantic mapping and default themes in VSCode. | Primary | Fallback | | ------- | -------- | | `hcl-blockType` | `type` | | `hcl-blockLabel` | `enumMember` | | `hcl-attrName` | `property` | | `hcl-bool` | `keyword` | | `hcl-number` | `number` | | `hcl-string` | `string` | | `hcl-objectKey` | `parameter` | | `hcl-mapKey` | `parameter` | | `hcl-keyword` | `variable` | | `hcl-referenceStep` | `variable` | | `hcl-typeComplex` | `function` | | `hcl-typePrimitive` | `keyword` | | `hcl-functionName` | `function` | #### Token Modifiers Modifiers which do not have fallback are not reported at all if not received within `SemanticTokensClientCapabilities.TokenModifiers` (just like fallback modifier that isn't supported). | Primary | Fallback | | ------- | -------- | | `hcl-dependent` | `defaultLibrary` | | `terraform-data` | | | `terraform-locals` | | | `terraform-module` | | | `terraform-output` | | | `terraform-provider` | | | `terraform-resource` | | | `terraform-provisioner` | | | `terraform-connection` | | | `terraform-variable` | | | `terraform-terraform` | | | `terraform-backend` | | | `terraform-name` | | | `terraform-type` | | | `terraform-requiredProviders` | | --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/telemetry.md # Telemetry The language server is capable of sending telemetry using the native LSP `telemetry/event` method. Telemetry is off by default and can enabled by passing a supported request format version as an experimental client capability. ```json { "capabilities": { "experimental": { "telemetryVersion": 1 } } } ``` Clients then implement opt-in or opt-out in the UI and should reflect the user's choice. ## Privacy Sensitive data, such as filesystem paths or addresses of providers sourced from outside the Terraform Registry are anonymized. Random UUID is generated in memory and tracked instead of a path or a private provider address. Mapping of such UUIDs is not persisted anywhere other than in memory during process lifetime. ## Request Format The only supported version is currently `1`. Version negotiation allows the server to introduce breaking changes to the format and have clients adopt gradually. ### `v1` [`telemetry/event`](https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#telemetry_event) structure ```json { "v": 1, "name": "eventName", "properties": {} } ``` `properties` may contain **any (valid) JSON types** including arrays and arbitrarily nested objects. It is client's reponsibility to serialize these properties when and if necessary. Example events: ```json { "v": 1, "name": "initialize", "properties": { "experimentalCapabilities.referenceCountCodeLens": true, "lsVersion": "0.23.0", "options.commandPrefix": true, "options.excludeModulePaths": false, "options.experimentalFeatures.prefillRequiredFields": false, "options.experimentalFeatures.validateOnSave": false, "options.rootModulePaths": false, "options.terraformExecPath": false, "options.terraformExecTimeout": "", "options.terraformLogFilePath": false, "root_uri": "dir" } } ``` ```json { "v": 1, "name": "moduleData", "properties": { "backend": "remote", "backend.remote.hostname": "app.terraform.io", "installedProviders": { "registry.terraform.io/hashicorp/aws": "3.57.0", "registry.terraform.io/hashicorp/null": "3.1.0" }, "moduleId": "8aa5a4dc-4780-2d90-b8fb-57de8288fb32", "providerRequirements": { "registry.terraform.io/hashicorp/aws": "", "registry.terraform.io/hashicorp/null": "~> 3.1" }, "tfRequirements": "~> 1.0", "tfVersion": "1.0.7" } } ``` --- # Source: https://github.com/hashicorp/terraform-ls/blob/main/docs/validation.md # Validation The language server produces various types of diagnostics as you type. ## HCL syntax Starting in `v0.8.0` we report diagnostics for invalid [HCL syntax](https://github.com/hashicorp/hcl/blob/main/spec.md), for example missing `}`, `"` or other "control characters" out of place. ![invalid HCL syntax](./images/validation-rule-hcl.png) HCL syntax alone does _not_ account for the Terraform language with all its (in)valid keywords, block or attribute names etc. nor differences between Terraform versions, that is handled elsewhere. ## Enhanced Validation Starting in `v0.32.0` we report additional diagnostics for selected invalid Terraform language constructs based on detected Terraform version and provider versions (if available). This validation is enabled by default but can be controlled via [`validation.enableEnhancedValidation`](./SETTINGS.md#enableenhancedvalidation-bool-defaults-to-true). All validation rules currently implemented are considered _universally applicable_ and _not opinionated_. If you believe a diagnostic is incorrect, this may be caused by mis-identified version of Terraform or provider version. You can temporarily disable validation in such a case and let us know by [filing a new issue](https://github.com/hashicorp/terraform-ls/issues/new/choose). See supported rules below. ### Module Files (`*.tf`) #### Incorrect Number of Block Labels ![wrong label count](./images/validation-rule-label-count.png) #### Deprecated Attribute ![deprecated attribute](./images/validation-rule-deprecated-attribute.png) #### Deprecated Block ![deprecated block](./images/validation-rule-deprecated-block.png) #### Exceeded Maximum Number of Blocks For blocks which have a maximum we check if the number was not exceeded. ![exceeded maximum blocks](./images/validation-rule-exceeded-block-max-items.png) #### Mising Required Blocks For blocks which have an expected minimum we check if the number of blocks was met. ![missing required blocks](./images/validation-rule-missing-blocks.png) #### Missing Required Attribute ![missing attribute](./images/validation-rule-missing-attribute.png) #### Unexpected Attribute ![unexpected attribute](./images/validation-rule-unexpected-attribute.png) #### Unexpected Block ![unexpected block](./images/validation-rule-unexpected-block.png) #### Reference to Undeclared Block or Attribute This validation has a limited scope to variables (`var.*` / `variable` blocks) and local values (`local.*` / `locals`) for now. ![invalid reference](./images/validation-rule-invalid-ref.png) ### Variable Files (`*.tfvars`) #### Unknown variable name Each entry in the file is checked against its corresponding `variable` declaration and entries without declaration are considered invalid. ![unknown variable name](./images/validation-rule-tfvars-unknown-var.png) #### Unexpected blocks Blocks are not considered as valid in variable files. ![unexpected blocks](./images/validation-rule-tfvars-unexpected-blocks.png)