Skip to content

Commit

Permalink
Terminal Link Provider
Browse files Browse the repository at this point in the history
This feature allows the user to click on a resource addess inside terraform plan terminal output and have it open the file in the editor.
  • Loading branch information
jpogran committed Jan 26, 2024
1 parent b29c824 commit 701ecce
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { TerraformLSCommands } from './commands/terraformls';
import { TerraformCommands } from './commands/terraform';
import * as lsStatus from './status/language';
import { TerraformCloudFeature } from './features/terraformCloud';
import { TerminalLinkProvider } from './providers/terminalLinkProvider';

const id = 'terraform';
const brand = `HashiCorp Terraform`;
Expand Down Expand Up @@ -216,6 +217,9 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
} catch (error) {
await handleLanguageClientStartError(error, context, reporter);
}

const provider = new TerminalLinkProvider(client);
context.subscriptions.push(vscode.window.registerTerminalLinkProvider(provider));
}

export async function deactivate(): Promise<void> {
Expand Down
95 changes: 95 additions & 0 deletions src/providers/terminalLinkProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import * as vscode from 'vscode';
import { ExecuteCommandParams, ExecuteCommandRequest } from 'vscode-languageclient';
import { LanguageClient } from 'vscode-languageclient/node';

export class TerminalLinkProvider implements vscode.TerminalLinkProvider {
constructor(private client: LanguageClient) {}

async handleTerminalLink(link: TerraformTerminalLink): Promise<void> {
const document = await vscode.workspace.openTextDocument(link.uri);
await vscode.window.showTextDocument(document, { selection: link.range });
}

async provideTerminalLinks(
context: vscode.TerminalLinkContext,
token: vscode.CancellationToken,

Check warning on line 15 in src/providers/terminalLinkProvider.ts

View workflow job for this annotation

GitHub Actions / lint

'token' is defined but never used
): Promise<vscode.TerminalLink[]> {
const links: vscode.TerminalLink[] = [];

// # docker_image.nginx will be created
const potential = context.line.trimStart();
if (!potential.startsWith('#')) {
return links;
}
let address = potential.split(' ')[1];
const startIndex = potential.indexOf(address);
const endIndex = startIndex + address.length;

if (address.startsWith('module')) {
address = 'module.' + address.split('.')[1];
}

// TODO: Figure out cwd to send as moduleDir
// this isn't ideal, but it's the only way to get the folder for the terminal
// right now. terminal doesn't reliably expose the cwd in all cases
let folder: vscode.Uri | vscode.WorkspaceFolder | undefined;
if ('cwd' in context.terminal.creationOptions && context.terminal.creationOptions.cwd) {
folder = vscode.workspace.getWorkspaceFolder(
typeof context.terminal.creationOptions.cwd === 'string'
? vscode.Uri.file(context.terminal.creationOptions.cwd)
: context.terminal.creationOptions.cwd,
);
} else {
folder = vscode.workspace.workspaceFolders?.[0].uri;
}
const modDir = vscode.Uri.parse(folder?.toString() ?? '');

const params: ExecuteCommandParams = {
command: 'terraform-ls.terraform.plan.lookup',
arguments: [`uri=${modDir.toString()}`, `line=${address}`],
};

const response = await this.client.sendRequest<ExecuteCommandParams, TerraformPlanLookupResponse, void>(
ExecuteCommandRequest.type,
params,
);
if (!response || !response.fileUri || !response.range) {
return links;
}

const uri = vscode.Uri.parse(response.fileUri);
const range = new vscode.Range(
response.range.startLine,
response.range.startCharacter,
response.range.endLine,
response.range.endCharacter,
);

links.push(new TerraformTerminalLink(startIndex, endIndex, 'Open the file', uri, range));

return links;
}
}

class TerraformTerminalLink extends vscode.TerminalLink {
constructor(
startIndex: number,
endIndex: number,
tooltip: string,
public uri: vscode.Uri,
public range: vscode.Range,
) {
super(startIndex, endIndex, tooltip);
}
}

interface TerraformPlanLookupResponse {
v: number;
range: {
startLine: number;
startCharacter: number;
endLine: number;
endCharacter: number;
};
fileUri: string;
}

0 comments on commit 701ecce

Please sign in to comment.