diff --git a/src/extension.ts b/src/extension.ts index acf335f60..fe0cb082c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,6 +22,7 @@ import { ShowReferencesFeature } from './features/showReferences'; import { CustomSemanticTokens } from './features/semanticTokens'; import { ModuleProvidersFeature } from './features/moduleProviders'; import { ModuleCallsFeature } from './features/moduleCalls'; +import { TerraformVersionFeature } from './features/terraformVersion'; const id = 'terraform'; const brand = `HashiCorp Terraform`; @@ -173,6 +174,7 @@ export async function activate(context: vscode.ExtensionContext): Promise new CustomSemanticTokens(client, manifest), new ModuleProvidersFeature(client, moduleProvidersDataProvider), new ModuleCallsFeature(client, moduleCallsDataProvider), + new TerraformVersionFeature(client, reporter), ]; if (vscode.env.isTelemetryEnabled) { features.push(new TelemetryFeature(client, reporter)); diff --git a/src/features/terraformVersion.ts b/src/features/terraformVersion.ts new file mode 100644 index 000000000..d5070ea25 --- /dev/null +++ b/src/features/terraformVersion.ts @@ -0,0 +1,46 @@ +import * as terraform from '../terraform'; +import * as vscode from 'vscode'; +import { ClientCapabilities, ServerCapabilities, StaticFeature } from 'vscode-languageclient'; +import { ExperimentalClientCapabilities } from './types'; +import TelemetryReporter from '@vscode/extension-telemetry'; +import { LanguageClient } from 'vscode-languageclient/node'; +import { getActiveTextEditor } from '../utils/vscode'; + +export const CLIENT_TERRAFORM_VERSION_CMD_ID = 'client.refreshTerraformVersion'; + +export class TerraformVersionFeature implements StaticFeature { + private disposables: vscode.Disposable[] = []; + + constructor(private client: LanguageClient, private reporter: TelemetryReporter) {} + + public fillClientCapabilities(capabilities: ClientCapabilities & ExperimentalClientCapabilities): void { + if (!capabilities['experimental']) { + capabilities['experimental'] = {}; + } + capabilities['experimental']['refreshTerraformVersionCommandId'] = CLIENT_TERRAFORM_VERSION_CMD_ID; + } + + public async initialize(capabilities: ServerCapabilities): Promise { + if (!capabilities.experimental?.refreshTerraformVersion) { + console.log("Server doesn't support client.refreshTerraformVersion"); + return; + } + + await this.client.onReady(); + + const d = this.client.onRequest(CLIENT_TERRAFORM_VERSION_CMD_ID, async () => { + const editor = getActiveTextEditor(); + if (editor === undefined) { + return; + } + + terraform.getTerraformVersion(editor.document.uri, this.client, this.reporter); + }); + + this.disposables.push(d); + } + + public dispose(): void { + this.disposables.forEach((d: vscode.Disposable) => d.dispose()); + } +} diff --git a/src/features/types.ts b/src/features/types.ts index df867b012..63ab483b5 100644 --- a/src/features/types.ts +++ b/src/features/types.ts @@ -7,5 +7,6 @@ export interface ExperimentalClientCapabilities { showReferencesCommandId?: string; refreshModuleProvidersCommandId?: string; refreshModuleCallsCommandId?: string; + refreshTerraformVersionCommandId?: string; }; } diff --git a/src/status/terraform.ts b/src/status/terraform.ts new file mode 100644 index 000000000..8a3dad416 --- /dev/null +++ b/src/status/terraform.ts @@ -0,0 +1,27 @@ +import * as vscode from 'vscode'; + +const terraformStatus = vscode.languages.createLanguageStatusItem('terraform.status', [ + { language: 'terraform' }, + { language: 'terraform-vars' }, +]); +terraformStatus.name = 'Terraform'; +terraformStatus.detail = 'Terraform'; +terraformStatus.command = { + command: 'terraform.commands', + title: 'Terraform Commands', + tooltip: 'foo', +}; + +export function setTerraformState( + detail: string, + busy: boolean, + severity: vscode.LanguageStatusSeverity = vscode.LanguageStatusSeverity.Information, +) { + terraformStatus.busy = busy; + terraformStatus.detail = detail; + terraformStatus.severity = severity; +} + +export function setTerraformVersion(version: string) { + terraformStatus.text = version; +} diff --git a/src/terraform.ts b/src/terraform.ts index 6f1697a97..a8a454c82 100644 --- a/src/terraform.ts +++ b/src/terraform.ts @@ -1,6 +1,12 @@ +import * as tfStatus from './status/terraform'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as vscode from 'vscode'; -import { ExecuteCommandParams, ExecuteCommandRequest, LanguageClient } from 'vscode-languageclient/node'; +import { + ExecuteCommandParams, + ExecuteCommandRequest, + LanguageClient, + WorkDoneProgress, +} from 'vscode-languageclient/node'; import { Utils } from 'vscode-uri'; import { getActiveTextEditor } from './utils/vscode'; import { clientSupportsCommand } from './utils/clientHelpers'; @@ -10,6 +16,12 @@ export interface ModuleCaller { uri: string; } +export interface TerraformInfoResponse { + v: number; + required_version: string; + discovered_version: string; +} + export interface ModuleCallersResponse { v: number; callers: ModuleCaller[]; @@ -44,6 +56,39 @@ interface ModuleProvidersResponse { } /* eslint-enable @typescript-eslint/naming-convention */ +export async function getTerraformVersion( + moduleUri: vscode.Uri, + client: LanguageClient, + reporter: TelemetryReporter, +): Promise { + try { + const moduleDir = Utils.dirname(moduleUri); + + const response = await terraformVersion(moduleDir.toString(), client, reporter); + tfStatus.setTerraformVersion(response.discovered_version); + } catch (error) { + let message = 'Error requesting terraform version from terraform-ls'; + if (error instanceof Error) { + message = error.message; + } else if (typeof error === 'string') { + message = error; + } + + vscode.window.showErrorMessage(message); + } +} +export async function terraformVersion( + moduleUri: string, + client: LanguageClient, + reporter: TelemetryReporter, +): Promise { + const command = 'terraform-ls.module.terraform'; + + const response = await execWorkspaceLSCommand(command, moduleUri, client, reporter); + + return response; +} + export async function moduleCallers( moduleUri: string, client: LanguageClient,