Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Terraform Version LanguageStatusItem #1325

Merged
merged 4 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { getInitializationOptions, migrateLegacySettings, previewExtensionPresen
import { TerraformLSCommands } from './commands/terraformls';
import { TerraformCommands } from './commands/terraform';
import { ExtensionErrorHandler } from './handlers/errorHandler';
import { TerraformVersionFeature } from './features/terraformVersion';

const id = 'terraform';
const brand = `HashiCorp Terraform`;
Expand Down Expand Up @@ -94,6 +95,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
new ModuleCallsFeature(client, new ModuleCallsDataProvider(context, client, reporter)),
new TelemetryFeature(client, reporter),
new ShowReferencesFeature(client),
new TerraformVersionFeature(client, reporter, outputChannel),
]);

// these need the LS to function, so are only registered if enabled
Expand Down
92 changes: 92 additions & 0 deletions src/features/terraformVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as vscode from 'vscode';
import * as terraform from '../terraform';
import { ClientCapabilities, ServerCapabilities, StaticFeature } from 'vscode-languageclient';
import { getActiveTextEditor } from '../utils/vscode';
import { ExperimentalClientCapabilities } from './types';
import { Utils } from 'vscode-uri';
import TelemetryReporter from '@vscode/extension-telemetry';
import { LanguageClient } from 'vscode-languageclient/node';

export class TerraformVersionFeature implements StaticFeature {
private disposables: vscode.Disposable[] = [];

private clientTerraformVersionCommandId = 'client.refreshTerraformVersion';

private installedVersion = vscode.languages.createLanguageStatusItem('terraform.installedVersion', [
{ language: 'terraform' },
{ language: 'terraform-vars' },
]);
private requiredVersion = vscode.languages.createLanguageStatusItem('terraform.requiredVersion', [
{ language: 'terraform' },
{ language: 'terraform-vars' },
]);

constructor(
private client: LanguageClient,
private reporter: TelemetryReporter,
private outputChannel: vscode.OutputChannel,
) {
this.installedVersion.name = 'TerraformInstalledVersion';
this.installedVersion.detail = 'Installed Version';

this.requiredVersion.name = 'TerraformRequiredVersion';
this.requiredVersion.detail = 'Required Version';

this.disposables.push(this.installedVersion);
this.disposables.push(this.requiredVersion);
}

public fillClientCapabilities(capabilities: ClientCapabilities & ExperimentalClientCapabilities): void {
capabilities.experimental = capabilities.experimental || {};
capabilities.experimental.refreshTerraformVersionCommandId = this.clientTerraformVersionCommandId;
}

public async initialize(capabilities: ServerCapabilities): Promise<void> {
if (!capabilities.experimental?.refreshTerraformVersion) {
this.outputChannel.appendLine("Server doesn't support client.refreshTerraformVersion");
return;
}

await this.client.onReady();

const handler = this.client.onRequest(this.clientTerraformVersionCommandId, async () => {
const editor = getActiveTextEditor();
if (editor === undefined) {
return;
}

const moduleDir = Utils.dirname(editor.document.uri);

try {
const response = await terraform.terraformVersion(moduleDir.toString(), this.client, this.reporter);
this.installedVersion.text = response.discovered_version || 'unknown';
this.requiredVersion.text = response.required_version || 'any';
} catch (error) {
let message = 'Unknown Error';
if (error instanceof Error) {
message = error.message;
} else if (typeof error === 'string') {
message = error;
}

/*
We do not want to pop an error window because the user cannot do anything
at this point. An error here likely means we cannot communicate with the LS,
which means it's already shut down.
Instead we log to the outputchannel so when the user copies the log we can
see this errored here.
*/
this.outputChannel.appendLine(message);
}
});

this.disposables.push(handler);
}

public dispose(): void {
this.disposables.forEach((d: vscode.Disposable, index, things) => {
d.dispose();
things.splice(index, 1);
});
}
}
1 change: 1 addition & 0 deletions src/features/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export interface ExperimentalClientCapabilities {
showReferencesCommandId?: string;
refreshModuleProvidersCommandId?: string;
refreshModuleCallsCommandId?: string;
refreshTerraformVersionCommandId?: string;
};
}
18 changes: 18 additions & 0 deletions src/terraform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,26 @@ interface ModuleProvidersResponse {
[provider: string]: string;
};
}

export interface TerraformInfoResponse {
v: number;
required_version?: string;
discovered_version?: string;
}
/* eslint-enable @typescript-eslint/naming-convention */

export async function terraformVersion(
moduleUri: string,
client: LanguageClient,
reporter: TelemetryReporter,
): Promise<TerraformInfoResponse> {
const command = 'terraform-ls.module.terraform';

const response = await execWorkspaceLSCommand<TerraformInfoResponse>(command, moduleUri, client, reporter);

return response;
}

export async function moduleCallers(
moduleUri: string,
client: LanguageClient,
Expand Down