Skip to content

Commit 3d0eac4

Browse files
authored
Support for Terraform Search (.tfquery.hcl) files (#2062)
* feat(tfsearch): TF-26847 TF-27259: Added: support for .tfquery.hcl file * feat(tfsearch): TF-26847 TF-27259: Added: changelog * feat(tfsearch): TF-26847 TF-27259: Refactored: linting * feat(tfsearch): TF-26847: Modified: changelog * feat(tfsearch): TF-26847 TF-27256: Modified: added test cases * feat(tfsearch): TF-26847: Refactored: using the same icon for terraform search stack test etc * feat(tfsearch): Bumped: langserver version
1 parent a1a469c commit 3d0eac4

File tree

8 files changed

+180
-9
lines changed

8 files changed

+180
-9
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: ENHANCEMENTS
2+
body: Add support for Terraform Search files (.tfquery.hcl). This provides block and attribute completion, hover, and diagnostics along with syntax validation for Terraform Search files.
3+
time: 2025-07-01T17:44:33.274267+05:30
4+
custom:
5+
Issue: "2062"
6+
Repository: vscode-terraform

package.json

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"vscode": "^1.92.2"
1919
},
2020
"langServer": {
21-
"version": "0.36.5"
21+
"version": "0.37.0"
2222
},
2323
"syntax": {
2424
"version": "0.7.1"
@@ -86,8 +86,8 @@
8686
],
8787
"configuration": "./language-configuration.json",
8888
"icon": {
89-
"dark": "assets/icons/terraform_stacks.svg",
90-
"light": "assets/icons/terraform_stacks.svg"
89+
"dark": "assets/icons/terraform_feature.svg",
90+
"light": "assets/icons/terraform_feature.svg"
9191
}
9292
},
9393
{
@@ -100,8 +100,8 @@
100100
],
101101
"configuration": "./language-configuration.json",
102102
"icon": {
103-
"dark": "assets/icons/terraform_stacks.svg",
104-
"light": "assets/icons/terraform_stacks.svg"
103+
"dark": "assets/icons/terraform_feature.svg",
104+
"light": "assets/icons/terraform_feature.svg"
105105
}
106106
},
107107
{
@@ -127,8 +127,8 @@
127127
],
128128
"configuration": "./language-configuration.json",
129129
"icon": {
130-
"dark": "assets/icons/terraform_stacks.svg",
131-
"light": "assets/icons/terraform_stacks.svg"
130+
"dark": "assets/icons/terraform_feature.svg",
131+
"light": "assets/icons/terraform_feature.svg"
132132
}
133133
},
134134
{
@@ -141,8 +141,22 @@
141141
],
142142
"configuration": "./language-configuration.json",
143143
"icon": {
144-
"dark": "assets/icons/terraform_stacks.svg",
145-
"light": "assets/icons/terraform_stacks.svg"
144+
"dark": "assets/icons/terraform_feature.svg",
145+
"light": "assets/icons/terraform_feature.svg"
146+
}
147+
},
148+
{
149+
"id": "terraform-search",
150+
"aliases": [
151+
"Terraform Search"
152+
],
153+
"extensions": [
154+
".tfquery.hcl"
155+
],
156+
"configuration": "./language-configuration.json",
157+
"icon": {
158+
"dark": "assets/icons/terraform_feature.svg",
159+
"light": "assets/icons/terraform_feature.svg"
146160
}
147161
},
148162
{
@@ -182,6 +196,11 @@
182196
"language": "terraform-mock",
183197
"scopeName": "source.hcl",
184198
"path": "./syntaxes/hcl.tmGrammar.json"
199+
},
200+
{
201+
"language": "terraform-search",
202+
"scopeName": "source.hcl",
203+
"path": "./syntaxes/hcl.tmGrammar.json"
185204
}
186205
],
187206
"semanticTokenTypes": [

src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const documentSelector: DocumentSelector = [
4646
{ scheme: 'file', language: 'terraform-deploy' },
4747
{ scheme: 'file', language: 'terraform-test' },
4848
{ scheme: 'file', language: 'terraform-mock' },
49+
{ scheme: 'file', language: 'terraform-search' },
4950
];
5051
const outputChannel = vscode.window.createOutputChannel(brand);
5152
const tfcOutputChannel = vscode.window.createOutputChannel('HCP Terraform');
@@ -94,6 +95,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
9495
vscode.workspace.createFileSystemWatcher('**/*.tfdeploy.hcl'),
9596
vscode.workspace.createFileSystemWatcher('**/*.tftest.hcl'),
9697
vscode.workspace.createFileSystemWatcher('**/*.tfmock.hcl'),
98+
vscode.workspace.createFileSystemWatcher('**/*.tfquery.hcl'),
9799
],
98100
},
99101
diagnosticCollectionName: 'HashiCorpTerraform',

src/status/language.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const lsStatus = vscode.languages.createLanguageStatusItem('terraform-ls.status'
1212
{ language: 'terraform-deploy' },
1313
{ language: 'terraform-test' },
1414
{ language: 'terraform-mock' },
15+
{ language: 'terraform-search' },
1516
]);
1617
lsStatus.name = 'Terraform LS';
1718
lsStatus.detail = 'Terraform LS';
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* Copyright (c) HashiCorp, Inc.
3+
* SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
import * as vscode from 'vscode';
7+
import { assert } from 'chai';
8+
import { activateExtension, getDocUri, open, testCompletion } from '../../helper';
9+
10+
suite('search (.tfquery.hcl)', () => {
11+
suite('root', function suite() {
12+
const docUri = getDocUri('main.tfquery.hcl');
13+
14+
this.beforeAll(async () => {
15+
await open(docUri);
16+
await activateExtension();
17+
});
18+
19+
this.afterAll(async () => {
20+
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
21+
});
22+
23+
test('language is registered', async () => {
24+
const doc = await vscode.workspace.openTextDocument(docUri);
25+
assert.equal(doc.languageId, 'terraform-search', 'document language should be `terraform-search`');
26+
});
27+
28+
test('completes blocks available for search files', async () => {
29+
const expected = [
30+
new vscode.CompletionItem('list', vscode.CompletionItemKind.Class),
31+
new vscode.CompletionItem('locals', vscode.CompletionItemKind.Class),
32+
new vscode.CompletionItem('provider', vscode.CompletionItemKind.Class),
33+
new vscode.CompletionItem('variable', vscode.CompletionItemKind.Class),
34+
];
35+
36+
await testCompletion(docUri, new vscode.Position(1, 0), {
37+
items: expected,
38+
});
39+
});
40+
});
41+
42+
suite('list', function suite() {
43+
const docUri = getDocUri('main.tfquery.hcl');
44+
45+
this.beforeAll(async () => {
46+
await open(docUri);
47+
await activateExtension();
48+
});
49+
50+
this.afterAll(async () => {
51+
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
52+
});
53+
54+
this.afterEach(async () => {
55+
// revert any changes made to the document after each test
56+
await vscode.commands.executeCommand('workbench.action.files.revert');
57+
});
58+
59+
test('language is registered', async () => {
60+
const doc = await vscode.workspace.openTextDocument(docUri);
61+
assert.equal(doc.languageId, 'terraform-search', 'document language should be `terraform-search`');
62+
});
63+
64+
test('completes attributes of list block - provider', async () => {
65+
const expected = [
66+
new vscode.CompletionItem('aws.this', vscode.CompletionItemKind.Variable),
67+
new vscode.CompletionItem('azurerm.this', vscode.CompletionItemKind.Variable),
68+
];
69+
70+
await testCompletion(docUri, new vscode.Position(24, 21), {
71+
items: expected,
72+
});
73+
});
74+
75+
test('completes attributes of list block - number variable', async () => {
76+
const expected = [
77+
new vscode.CompletionItem('count.index', vscode.CompletionItemKind.Variable),
78+
new vscode.CompletionItem('local.number_local', vscode.CompletionItemKind.Variable),
79+
new vscode.CompletionItem('var.number_variable', vscode.CompletionItemKind.Variable),
80+
];
81+
82+
await testCompletion(docUri, new vscode.Position(25, 21), {
83+
items: expected,
84+
});
85+
});
86+
87+
test('completes attributes of list block - boolean variable', async () => {
88+
const expected = [
89+
new vscode.CompletionItem('false', vscode.CompletionItemKind.EnumMember),
90+
new vscode.CompletionItem('true', vscode.CompletionItemKind.EnumMember),
91+
new vscode.CompletionItem('var.boolean_variable', vscode.CompletionItemKind.Variable),
92+
];
93+
94+
await testCompletion(docUri, new vscode.Position(26, 21), {
95+
items: expected,
96+
});
97+
});
98+
});
99+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "~> 5.0"
6+
}
7+
azurerm = {
8+
source = "hashicorp/azurerm"
9+
version = "=3.0.0"
10+
}
11+
}
12+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
provider "aws" {
3+
alias = "this"
4+
}
5+
6+
provider "azurerm" {
7+
alias = "this"
8+
}
9+
10+
variable "boolean_variable" {
11+
default = true
12+
type = bool
13+
}
14+
15+
locals {
16+
number_local = 500
17+
}
18+
19+
variable "number_variable" {
20+
default = 10
21+
type = number
22+
}
23+
24+
list "concept_pet" "name_1" {
25+
provider =
26+
limit =
27+
include_resource =
28+
count = 10
29+
config {
30+
31+
}
32+
}

0 commit comments

Comments
 (0)