2019-12-30 19:07:04 +00:00
|
|
|
import * as vscode from 'vscode';
|
|
|
|
import * as lc from 'vscode-languageclient';
|
2020-02-24 22:50:57 +00:00
|
|
|
import * as ra from '../rust-analyzer-api';
|
2019-12-30 19:07:04 +00:00
|
|
|
|
2019-12-30 17:31:08 +00:00
|
|
|
import { Ctx, Cmd } from '../ctx';
|
2020-05-25 08:59:54 +00:00
|
|
|
import { applySnippetWorkspaceEdit } from '../snippets';
|
2020-05-25 09:10:31 +00:00
|
|
|
import { spawnSync } from 'child_process';
|
2019-12-30 13:53:43 +00:00
|
|
|
|
2020-02-02 19:37:22 +00:00
|
|
|
export * from './syntax_tree';
|
|
|
|
export * from './runnables';
|
2020-05-25 09:10:31 +00:00
|
|
|
|
|
|
|
export function analyzerStatus(ctx: Ctx): Cmd {
|
|
|
|
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
|
|
|
readonly uri = vscode.Uri.parse('rust-analyzer-status://status');
|
|
|
|
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
|
|
|
|
|
|
|
provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
|
|
|
|
if (!vscode.window.activeTextEditor) return '';
|
|
|
|
|
|
|
|
return ctx.client.sendRequest(ra.analyzerStatus, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
get onDidChange(): vscode.Event<vscode.Uri> {
|
|
|
|
return this.eventEmitter.event;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
let poller: NodeJS.Timer | undefined = undefined;
|
|
|
|
|
|
|
|
ctx.pushCleanup(
|
|
|
|
vscode.workspace.registerTextDocumentContentProvider(
|
|
|
|
'rust-analyzer-status',
|
|
|
|
tdcp,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
ctx.pushCleanup({
|
|
|
|
dispose() {
|
|
|
|
if (poller !== undefined) {
|
|
|
|
clearInterval(poller);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return async () => {
|
|
|
|
if (poller === undefined) {
|
|
|
|
poller = setInterval(() => tdcp.eventEmitter.fire(tdcp.uri), 1000);
|
|
|
|
}
|
|
|
|
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
|
|
|
return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function matchingBrace(ctx: Ctx): Cmd {
|
|
|
|
return async () => {
|
|
|
|
const editor = ctx.activeRustEditor;
|
|
|
|
const client = ctx.client;
|
|
|
|
if (!editor || !client) return;
|
|
|
|
|
|
|
|
const response = await client.sendRequest(ra.matchingBrace, {
|
|
|
|
textDocument: { uri: editor.document.uri.toString() },
|
|
|
|
positions: editor.selections.map(s =>
|
|
|
|
client.code2ProtocolConverter.asPosition(s.active),
|
|
|
|
),
|
|
|
|
});
|
|
|
|
editor.selections = editor.selections.map((sel, idx) => {
|
|
|
|
const active = client.protocol2CodeConverter.asPosition(
|
|
|
|
response[idx],
|
|
|
|
);
|
|
|
|
const anchor = sel.isEmpty ? active : sel.anchor;
|
|
|
|
return new vscode.Selection(anchor, active);
|
|
|
|
});
|
|
|
|
editor.revealRange(editor.selection);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function joinLines(ctx: Ctx): Cmd {
|
|
|
|
return async () => {
|
|
|
|
const editor = ctx.activeRustEditor;
|
|
|
|
const client = ctx.client;
|
|
|
|
if (!editor || !client) return;
|
|
|
|
|
|
|
|
const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
|
|
|
|
ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
|
|
|
|
textDocument: { uri: editor.document.uri.toString() },
|
|
|
|
});
|
|
|
|
editor.edit((builder) => {
|
|
|
|
client.protocol2CodeConverter.asTextEdits(items).forEach((edit) => {
|
|
|
|
builder.replace(edit.range, edit.newText);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function onEnter(ctx: Ctx): Cmd {
|
|
|
|
async function handleKeypress() {
|
|
|
|
const editor = ctx.activeRustEditor;
|
|
|
|
const client = ctx.client;
|
|
|
|
|
|
|
|
if (!editor || !client) return false;
|
|
|
|
|
|
|
|
const change = await client.sendRequest(ra.onEnter, {
|
|
|
|
textDocument: { uri: editor.document.uri.toString() },
|
|
|
|
position: client.code2ProtocolConverter.asPosition(
|
|
|
|
editor.selection.active,
|
|
|
|
),
|
|
|
|
}).catch(_error => {
|
|
|
|
// client.logFailedRequest(OnEnterRequest.type, error);
|
|
|
|
return null;
|
|
|
|
});
|
|
|
|
if (!change) return false;
|
|
|
|
|
|
|
|
const workspaceEdit = client.protocol2CodeConverter.asWorkspaceEdit(change);
|
|
|
|
await applySnippetWorkspaceEdit(workspaceEdit);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return async () => {
|
|
|
|
if (await handleKeypress()) return;
|
|
|
|
|
|
|
|
await vscode.commands.executeCommand('default:type', { text: '\n' });
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function parentModule(ctx: Ctx): Cmd {
|
|
|
|
return async () => {
|
|
|
|
const editor = ctx.activeRustEditor;
|
|
|
|
const client = ctx.client;
|
|
|
|
if (!editor || !client) return;
|
|
|
|
|
|
|
|
const response = await client.sendRequest(ra.parentModule, {
|
|
|
|
textDocument: { uri: editor.document.uri.toString() },
|
|
|
|
position: client.code2ProtocolConverter.asPosition(
|
|
|
|
editor.selection.active,
|
|
|
|
),
|
|
|
|
});
|
|
|
|
const loc = response[0];
|
|
|
|
if (loc == null) return;
|
|
|
|
|
|
|
|
const uri = client.protocol2CodeConverter.asUri(loc.uri);
|
|
|
|
const range = client.protocol2CodeConverter.asRange(loc.range);
|
|
|
|
|
|
|
|
const doc = await vscode.workspace.openTextDocument(uri);
|
|
|
|
const e = await vscode.window.showTextDocument(doc);
|
|
|
|
e.selection = new vscode.Selection(range.start, range.start);
|
|
|
|
e.revealRange(range, vscode.TextEditorRevealType.InCenter);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function ssr(ctx: Ctx): Cmd {
|
|
|
|
return async () => {
|
|
|
|
const client = ctx.client;
|
|
|
|
if (!client) return;
|
|
|
|
|
|
|
|
const options: vscode.InputBoxOptions = {
|
|
|
|
value: "() ==>> ()",
|
|
|
|
prompt: "Enter request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
|
|
|
|
validateInput: async (x: string) => {
|
|
|
|
try {
|
|
|
|
await client.sendRequest(ra.ssr, { query: x, parseOnly: true });
|
|
|
|
} catch (e) {
|
|
|
|
return e.toString();
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const request = await vscode.window.showInputBox(options);
|
|
|
|
if (!request) return;
|
|
|
|
|
|
|
|
const edit = await client.sendRequest(ra.ssr, { query: request, parseOnly: false });
|
|
|
|
|
|
|
|
await vscode.workspace.applyEdit(client.protocol2CodeConverter.asWorkspaceEdit(edit));
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function serverVersion(ctx: Ctx): Cmd {
|
|
|
|
return async () => {
|
|
|
|
const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
|
|
|
|
const commitHash = stdout.slice(`rust-analyzer `.length).trim();
|
|
|
|
const { releaseTag } = ctx.config.package;
|
|
|
|
|
|
|
|
void vscode.window.showInformationMessage(
|
|
|
|
`rust-analyzer version: ${releaseTag ?? "unreleased"} (${commitHash})`
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function toggleInlayHints(ctx: Ctx): Cmd {
|
|
|
|
return async () => {
|
|
|
|
await vscode
|
|
|
|
.workspace
|
|
|
|
.getConfiguration(`${ctx.config.rootSection}.inlayHints`)
|
|
|
|
.update('enable', !ctx.config.inlayHints.enable, vscode.ConfigurationTarget.Workspace);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Opens the virtual file that will show the syntax tree
|
|
|
|
//
|
|
|
|
// The contents of the file come from the `TextDocumentContentProvider`
|
|
|
|
export function expandMacro(ctx: Ctx): Cmd {
|
|
|
|
function codeFormat(expanded: ra.ExpandedMacro): string {
|
|
|
|
let result = `// Recursive expansion of ${expanded.name}! macro\n`;
|
|
|
|
result += '// ' + '='.repeat(result.length - 3);
|
|
|
|
result += '\n\n';
|
|
|
|
result += expanded.expansion;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
|
|
|
uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
|
|
|
|
eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
|
|
|
async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> {
|
|
|
|
const editor = vscode.window.activeTextEditor;
|
|
|
|
const client = ctx.client;
|
|
|
|
if (!editor || !client) return '';
|
|
|
|
|
|
|
|
const position = editor.selection.active;
|
|
|
|
|
|
|
|
const expanded = await client.sendRequest(ra.expandMacro, {
|
|
|
|
textDocument: { uri: editor.document.uri.toString() },
|
|
|
|
position,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (expanded == null) return 'Not available';
|
|
|
|
|
|
|
|
return codeFormat(expanded);
|
|
|
|
}
|
|
|
|
|
|
|
|
get onDidChange(): vscode.Event<vscode.Uri> {
|
|
|
|
return this.eventEmitter.event;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
ctx.pushCleanup(
|
|
|
|
vscode.workspace.registerTextDocumentContentProvider(
|
|
|
|
'rust-analyzer',
|
|
|
|
tdcp,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
return async () => {
|
|
|
|
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
|
|
|
tdcp.eventEmitter.fire(tdcp.uri);
|
|
|
|
return vscode.window.showTextDocument(
|
|
|
|
document,
|
|
|
|
vscode.ViewColumn.Two,
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
2020-02-02 19:37:22 +00:00
|
|
|
|
|
|
|
export function collectGarbage(ctx: Ctx): Cmd {
|
2020-02-24 22:50:57 +00:00
|
|
|
return async () => ctx.client.sendRequest(ra.collectGarbage, null);
|
2019-12-30 13:53:43 +00:00
|
|
|
}
|
|
|
|
|
2020-02-02 19:37:22 +00:00
|
|
|
export function showReferences(ctx: Ctx): Cmd {
|
2019-12-30 19:07:04 +00:00
|
|
|
return (uri: string, position: lc.Position, locations: lc.Location[]) => {
|
2020-02-02 19:12:59 +00:00
|
|
|
const client = ctx.client;
|
2019-12-31 17:14:00 +00:00
|
|
|
if (client) {
|
|
|
|
vscode.commands.executeCommand(
|
|
|
|
'editor.action.showReferences',
|
|
|
|
vscode.Uri.parse(uri),
|
|
|
|
client.protocol2CodeConverter.asPosition(position),
|
|
|
|
locations.map(client.protocol2CodeConverter.asLocation),
|
|
|
|
);
|
|
|
|
}
|
2019-12-30 19:07:04 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-05-22 15:29:55 +00:00
|
|
|
export function applyActionGroup(_ctx: Ctx): Cmd {
|
|
|
|
return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => {
|
|
|
|
const selectedAction = await vscode.window.showQuickPick(actions);
|
|
|
|
if (!selectedAction) return;
|
|
|
|
await applySnippetWorkspaceEdit(selectedAction.edit);
|
2019-12-31 17:55:34 +00:00
|
|
|
};
|
2019-12-30 22:45:50 +00:00
|
|
|
}
|
2020-05-17 23:53:55 +00:00
|
|
|
|
2020-05-21 12:26:44 +00:00
|
|
|
export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd {
|
2020-05-17 23:53:55 +00:00
|
|
|
return async (edit: vscode.WorkspaceEdit) => {
|
2020-05-21 12:26:44 +00:00
|
|
|
await applySnippetWorkspaceEdit(edit);
|
|
|
|
};
|
|
|
|
}
|