mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 06:51:58 +00:00
prettier run
This commit is contained in:
parent
8e9f54f238
commit
f247090558
@ -1,41 +1,36 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
env: {
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
"extends": ["prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.eslint.json",
|
||||
"tsconfigRootDir": __dirname,
|
||||
"sourceType": "module"
|
||||
extends: ["prettier"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: "tsconfig.eslint.json",
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: "module",
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"camelcase": ["error"],
|
||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||
plugins: ["@typescript-eslint"],
|
||||
rules: {
|
||||
camelcase: ["error"],
|
||||
eqeqeq: ["error", "always", { null: "ignore" }],
|
||||
"no-console": ["error", { allow: ["warn", "error"] }],
|
||||
"prefer-const": "error",
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"error",
|
||||
{
|
||||
"multiline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": true
|
||||
multiline: {
|
||||
delimiter: "semi",
|
||||
requireLast: true,
|
||||
},
|
||||
"singleline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"error",
|
||||
"always"
|
||||
singleline: {
|
||||
delimiter: "semi",
|
||||
requireLast: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/semi": ["error", "always"],
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||
"@typescript-eslint/no-floating-promises": "error"
|
||||
}
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
},
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "//",
|
||||
"blockComment": [ "/*", "*/" ]
|
||||
"blockComment": ["/*", "*/"]
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
@ -9,10 +9,10 @@
|
||||
["(", ")"]
|
||||
],
|
||||
"colorizedBracketPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
],
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
{ "open": "{", "close": "}" },
|
||||
{ "open": "[", "close": "]" },
|
||||
|
@ -25,7 +25,5 @@
|
||||
"name": "string"
|
||||
}
|
||||
},
|
||||
"fileTypes": [
|
||||
"rast"
|
||||
]
|
||||
"fileTypes": ["rast"]
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import { Ctx, Disposable } from './ctx';
|
||||
import { RustEditor, isRustEditor } from './util';
|
||||
import { Ctx, Disposable } from "./ctx";
|
||||
import { RustEditor, isRustEditor } from "./util";
|
||||
|
||||
// FIXME: consider implementing this via the Tree View API?
|
||||
// https://code.visualstudio.com/api/extension-guides/tree-view
|
||||
export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
|
||||
private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
|
||||
borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'),
|
||||
borderColor: new vscode.ThemeColor("rust_analyzer.syntaxTreeBorder"),
|
||||
borderStyle: "solid",
|
||||
borderWidth: "2px",
|
||||
});
|
||||
@ -35,11 +35,23 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||
});
|
||||
|
||||
constructor(ctx: Ctx) {
|
||||
ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: 'rust-analyzer' }, this));
|
||||
ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: "rust-analyzer" }, this));
|
||||
ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
|
||||
vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
|
||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
||||
vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions);
|
||||
vscode.workspace.onDidCloseTextDocument(
|
||||
this.onDidCloseTextDocument,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
vscode.workspace.onDidChangeTextDocument(
|
||||
this.onDidChangeTextDocument,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
vscode.window.onDidChangeVisibleTextEditors(
|
||||
this.onDidChangeVisibleTextEditors,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
|
||||
ctx.pushCleanup(this);
|
||||
}
|
||||
@ -48,7 +60,10 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||
}
|
||||
|
||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||
if (this.rustEditor && event.document.uri.toString() === this.rustEditor.document.uri.toString()) {
|
||||
if (
|
||||
this.rustEditor &&
|
||||
event.document.uri.toString() === this.rustEditor.document.uri.toString()
|
||||
) {
|
||||
this.rust2Ast.reset();
|
||||
}
|
||||
}
|
||||
@ -68,7 +83,9 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||
}
|
||||
|
||||
private findAstTextEditor(): undefined | vscode.TextEditor {
|
||||
return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === 'rust-analyzer');
|
||||
return vscode.window.visibleTextEditors.find(
|
||||
(it) => it.document.uri.scheme === "rust-analyzer"
|
||||
);
|
||||
}
|
||||
|
||||
private setRustEditor(newRustEditor: undefined | RustEditor) {
|
||||
@ -80,13 +97,19 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||
}
|
||||
|
||||
// additional positional params are omitted
|
||||
provideDefinition(doc: vscode.TextDocument, pos: vscode.Position): vscode.ProviderResult<vscode.DefinitionLink[]> {
|
||||
if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) return;
|
||||
provideDefinition(
|
||||
doc: vscode.TextDocument,
|
||||
pos: vscode.Position
|
||||
): vscode.ProviderResult<vscode.DefinitionLink[]> {
|
||||
if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString())
|
||||
return;
|
||||
|
||||
const astEditor = this.findAstTextEditor();
|
||||
if (!astEditor) return;
|
||||
|
||||
const rust2AstRanges = this.rust2Ast.get()?.find(([rustRange, _]) => rustRange.contains(pos));
|
||||
const rust2AstRanges = this.rust2Ast
|
||||
.get()
|
||||
?.find(([rustRange, _]) => rustRange.contains(pos));
|
||||
if (!rust2AstRanges) return;
|
||||
|
||||
const [rustFileRange, astFileRange] = rust2AstRanges;
|
||||
@ -94,16 +117,21 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||
astEditor.revealRange(astFileRange);
|
||||
astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end);
|
||||
|
||||
return [{
|
||||
targetRange: astFileRange,
|
||||
targetUri: astEditor.document.uri,
|
||||
originSelectionRange: rustFileRange,
|
||||
targetSelectionRange: astFileRange,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
targetRange: astFileRange,
|
||||
targetUri: astEditor.document.uri,
|
||||
originSelectionRange: rustFileRange,
|
||||
targetSelectionRange: astFileRange,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// additional positional params are omitted
|
||||
provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> {
|
||||
provideHover(
|
||||
doc: vscode.TextDocument,
|
||||
hoverPosition: vscode.Position
|
||||
): vscode.ProviderResult<vscode.Hover> {
|
||||
if (!this.rustEditor) return;
|
||||
|
||||
const astFileLine = doc.lineAt(hoverPosition.line);
|
||||
@ -127,13 +155,14 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||
return new vscode.Range(begin, end);
|
||||
}
|
||||
|
||||
private parseRustTextRange(doc: vscode.TextDocument, astLine: string): undefined | vscode.Range {
|
||||
private parseRustTextRange(
|
||||
doc: vscode.TextDocument,
|
||||
astLine: string
|
||||
): undefined | vscode.Range {
|
||||
const parsedRange = /(\d+)\.\.(\d+)/.exec(astLine);
|
||||
if (!parsedRange) return;
|
||||
|
||||
const [begin, end] = parsedRange
|
||||
.slice(1)
|
||||
.map(off => this.positionAt(doc, +off));
|
||||
const [begin, end] = parsedRange.slice(1).map((off) => this.positionAt(doc, +off));
|
||||
|
||||
return new vscode.Range(begin, end);
|
||||
}
|
||||
@ -173,7 +202,7 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||
class Lazy<T> {
|
||||
val: undefined | T;
|
||||
|
||||
constructor(private readonly compute: () => undefined | T) { }
|
||||
constructor(private readonly compute: () => undefined | T) {}
|
||||
|
||||
get() {
|
||||
return this.val ?? (this.val = this.compute());
|
||||
|
@ -1,39 +1,47 @@
|
||||
import * as lc from 'vscode-languageclient/node';
|
||||
import * as vscode from 'vscode';
|
||||
import * as ra from '../src/lsp_ext';
|
||||
import * as Is from 'vscode-languageclient/lib/common/utils/is';
|
||||
import { assert } from './util';
|
||||
import { WorkspaceEdit } from 'vscode';
|
||||
import { Workspace } from './ctx';
|
||||
import { updateConfig } from './config';
|
||||
import { substituteVariablesInEnv } from './config';
|
||||
import * as lc from "vscode-languageclient/node";
|
||||
import * as vscode from "vscode";
|
||||
import * as ra from "../src/lsp_ext";
|
||||
import * as Is from "vscode-languageclient/lib/common/utils/is";
|
||||
import { assert } from "./util";
|
||||
import { WorkspaceEdit } from "vscode";
|
||||
import { Workspace } from "./ctx";
|
||||
import { updateConfig } from "./config";
|
||||
import { substituteVariablesInEnv } from "./config";
|
||||
|
||||
export interface Env {
|
||||
[name: string]: string;
|
||||
}
|
||||
|
||||
function renderCommand(cmd: ra.CommandLink) {
|
||||
return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip}')`;
|
||||
return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(
|
||||
JSON.stringify(cmd.arguments)
|
||||
)} '${cmd.tooltip}')`;
|
||||
}
|
||||
|
||||
function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString {
|
||||
const text = actions.map(group =>
|
||||
(group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ')
|
||||
).join('___');
|
||||
const text = actions
|
||||
.map(
|
||||
(group) =>
|
||||
(group.title ? group.title + " " : "") +
|
||||
group.commands.map(renderCommand).join(" | ")
|
||||
)
|
||||
.join("___");
|
||||
|
||||
const result = new vscode.MarkdownString(text);
|
||||
result.isTrusted = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function createClient(serverPath: string, workspace: Workspace, extraEnv: Env): Promise<lc.LanguageClient> {
|
||||
export async function createClient(
|
||||
serverPath: string,
|
||||
workspace: Workspace,
|
||||
extraEnv: Env
|
||||
): Promise<lc.LanguageClient> {
|
||||
// '.' Is the fallback if no folder is open
|
||||
// TODO?: Workspace folders support Uri's (eg: file://test.txt).
|
||||
// It might be a good idea to test if the uri points to a file.
|
||||
|
||||
const newEnv = substituteVariablesInEnv(Object.assign(
|
||||
{}, process.env, extraEnv
|
||||
));
|
||||
const newEnv = substituteVariablesInEnv(Object.assign({}, process.env, extraEnv));
|
||||
const run: lc.Executable = {
|
||||
command: serverPath,
|
||||
options: { env: newEnv },
|
||||
@ -43,137 +51,176 @@ export async function createClient(serverPath: string, workspace: Workspace, ext
|
||||
debug: run,
|
||||
};
|
||||
const traceOutputChannel = vscode.window.createOutputChannel(
|
||||
'Rust Analyzer Language Server Trace',
|
||||
"Rust Analyzer Language Server Trace"
|
||||
);
|
||||
|
||||
let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
|
||||
|
||||
// Update outdated user configs
|
||||
await updateConfig(initializationOptions).catch(err => {
|
||||
await updateConfig(initializationOptions).catch((err) => {
|
||||
void vscode.window.showErrorMessage(`Failed updating old config keys: ${err.message}`);
|
||||
});
|
||||
|
||||
if (workspace.kind === "Detached Files") {
|
||||
initializationOptions = { "detachedFiles": workspace.files.map(file => file.uri.fsPath), ...initializationOptions };
|
||||
initializationOptions = {
|
||||
detachedFiles: workspace.files.map((file) => file.uri.fsPath),
|
||||
...initializationOptions,
|
||||
};
|
||||
}
|
||||
|
||||
const clientOptions: lc.LanguageClientOptions = {
|
||||
documentSelector: [{ scheme: 'file', language: 'rust' }],
|
||||
documentSelector: [{ scheme: "file", language: "rust" }],
|
||||
initializationOptions,
|
||||
diagnosticCollectionName: "rustc",
|
||||
traceOutputChannel,
|
||||
middleware: {
|
||||
async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) {
|
||||
async provideHover(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
token: vscode.CancellationToken,
|
||||
_next: lc.ProvideHoverSignature
|
||||
) {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const positionOrRange = editor?.selection?.contains(position) ? client.code2ProtocolConverter.asRange(editor.selection) : client.code2ProtocolConverter.asPosition(position);
|
||||
return client.sendRequest(ra.hover, {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||
position: positionOrRange
|
||||
}, token).then(
|
||||
(result) => {
|
||||
const hover =
|
||||
client.protocol2CodeConverter.asHover(result);
|
||||
if (hover) {
|
||||
const actions = (<any>result).actions;
|
||||
if (actions) {
|
||||
hover.contents.push(renderHoverActions(actions));
|
||||
const positionOrRange = editor?.selection?.contains(position)
|
||||
? client.code2ProtocolConverter.asRange(editor.selection)
|
||||
: client.code2ProtocolConverter.asPosition(position);
|
||||
return client
|
||||
.sendRequest(
|
||||
ra.hover,
|
||||
{
|
||||
textDocument:
|
||||
client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||
position: positionOrRange,
|
||||
},
|
||||
token
|
||||
)
|
||||
.then(
|
||||
(result) => {
|
||||
const hover = client.protocol2CodeConverter.asHover(result);
|
||||
if (hover) {
|
||||
const actions = (<any>result).actions;
|
||||
if (actions) {
|
||||
hover.contents.push(renderHoverActions(actions));
|
||||
}
|
||||
}
|
||||
return hover;
|
||||
},
|
||||
(error) => {
|
||||
client.handleFailedRequest(lc.HoverRequest.type, token, error, null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return hover;
|
||||
},
|
||||
(error) => {
|
||||
client.handleFailedRequest(
|
||||
lc.HoverRequest.type,
|
||||
token,
|
||||
error,
|
||||
null
|
||||
);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
);
|
||||
);
|
||||
},
|
||||
// Using custom handling of CodeActions to support action groups and snippet edits.
|
||||
// Note that this means we have to re-implement lazy edit resolving ourselves as well.
|
||||
async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
|
||||
async provideCodeActions(
|
||||
document: vscode.TextDocument,
|
||||
range: vscode.Range,
|
||||
context: vscode.CodeActionContext,
|
||||
token: vscode.CancellationToken,
|
||||
_next: lc.ProvideCodeActionsSignature
|
||||
) {
|
||||
const params: lc.CodeActionParams = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||
range: client.code2ProtocolConverter.asRange(range),
|
||||
context: await client.code2ProtocolConverter.asCodeActionContext(context, token)
|
||||
context: await client.code2ProtocolConverter.asCodeActionContext(
|
||||
context,
|
||||
token
|
||||
),
|
||||
};
|
||||
return client.sendRequest(lc.CodeActionRequest.type, params, token).then(async (values) => {
|
||||
if (values === null) return undefined;
|
||||
const result: (vscode.CodeAction | vscode.Command)[] = [];
|
||||
const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>();
|
||||
for (const item of values) {
|
||||
// In our case we expect to get code edits only from diagnostics
|
||||
if (lc.CodeAction.is(item)) {
|
||||
assert(!item.command, "We don't expect to receive commands in CodeActions");
|
||||
const action = await client.protocol2CodeConverter.asCodeAction(item, token);
|
||||
result.push(action);
|
||||
continue;
|
||||
}
|
||||
assert(isCodeActionWithoutEditsAndCommands(item), "We don't expect edits or commands here");
|
||||
const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind);
|
||||
const action = new vscode.CodeAction(item.title, kind);
|
||||
const group = (item as any).group;
|
||||
action.command = {
|
||||
command: "rust-analyzer.resolveCodeAction",
|
||||
title: item.title,
|
||||
arguments: [item],
|
||||
};
|
||||
|
||||
// Set a dummy edit, so that VS Code doesn't try to resolve this.
|
||||
action.edit = new WorkspaceEdit();
|
||||
|
||||
if (group) {
|
||||
let entry = groups.get(group);
|
||||
if (!entry) {
|
||||
entry = { index: result.length, items: [] };
|
||||
groups.set(group, entry);
|
||||
return client.sendRequest(lc.CodeActionRequest.type, params, token).then(
|
||||
async (values) => {
|
||||
if (values === null) return undefined;
|
||||
const result: (vscode.CodeAction | vscode.Command)[] = [];
|
||||
const groups = new Map<
|
||||
string,
|
||||
{ index: number; items: vscode.CodeAction[] }
|
||||
>();
|
||||
for (const item of values) {
|
||||
// In our case we expect to get code edits only from diagnostics
|
||||
if (lc.CodeAction.is(item)) {
|
||||
assert(
|
||||
!item.command,
|
||||
"We don't expect to receive commands in CodeActions"
|
||||
);
|
||||
const action = await client.protocol2CodeConverter.asCodeAction(
|
||||
item,
|
||||
token
|
||||
);
|
||||
result.push(action);
|
||||
continue;
|
||||
}
|
||||
entry.items.push(action);
|
||||
} else {
|
||||
result.push(action);
|
||||
}
|
||||
}
|
||||
for (const [group, { index, items }] of groups) {
|
||||
if (items.length === 1) {
|
||||
result[index] = items[0];
|
||||
} else {
|
||||
const action = new vscode.CodeAction(group);
|
||||
action.kind = items[0].kind;
|
||||
assert(
|
||||
isCodeActionWithoutEditsAndCommands(item),
|
||||
"We don't expect edits or commands here"
|
||||
);
|
||||
const kind = client.protocol2CodeConverter.asCodeActionKind(
|
||||
(item as any).kind
|
||||
);
|
||||
const action = new vscode.CodeAction(item.title, kind);
|
||||
const group = (item as any).group;
|
||||
action.command = {
|
||||
command: "rust-analyzer.applyActionGroup",
|
||||
title: "",
|
||||
arguments: [items.map((item) => {
|
||||
return { label: item.title, arguments: item.command!.arguments![0] };
|
||||
})],
|
||||
command: "rust-analyzer.resolveCodeAction",
|
||||
title: item.title,
|
||||
arguments: [item],
|
||||
};
|
||||
|
||||
// Set a dummy edit, so that VS Code doesn't try to resolve this.
|
||||
action.edit = new WorkspaceEdit();
|
||||
|
||||
result[index] = action;
|
||||
if (group) {
|
||||
let entry = groups.get(group);
|
||||
if (!entry) {
|
||||
entry = { index: result.length, items: [] };
|
||||
groups.set(group, entry);
|
||||
result.push(action);
|
||||
}
|
||||
entry.items.push(action);
|
||||
} else {
|
||||
result.push(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
for (const [group, { index, items }] of groups) {
|
||||
if (items.length === 1) {
|
||||
result[index] = items[0];
|
||||
} else {
|
||||
const action = new vscode.CodeAction(group);
|
||||
action.kind = items[0].kind;
|
||||
action.command = {
|
||||
command: "rust-analyzer.applyActionGroup",
|
||||
title: "",
|
||||
arguments: [
|
||||
items.map((item) => {
|
||||
return {
|
||||
label: item.title,
|
||||
arguments: item.command!.arguments![0],
|
||||
};
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
// Set a dummy edit, so that VS Code doesn't try to resolve this.
|
||||
action.edit = new WorkspaceEdit();
|
||||
|
||||
result[index] = action;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
(_error) => undefined
|
||||
);
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
markdown: {
|
||||
supportHtml: true,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const client = new lc.LanguageClient(
|
||||
'rust-analyzer',
|
||||
'Rust Analyzer Language Server',
|
||||
"rust-analyzer",
|
||||
"Rust Analyzer Language Server",
|
||||
serverOptions,
|
||||
clientOptions,
|
||||
clientOptions
|
||||
);
|
||||
|
||||
// To turn on all proposed features use: client.registerProposedFeatures();
|
||||
@ -196,20 +243,26 @@ class ExperimentalFeatures implements lc.StaticFeature {
|
||||
"rust-analyzer.showReferences",
|
||||
"rust-analyzer.gotoLocation",
|
||||
"editor.action.triggerParameterHints",
|
||||
]
|
||||
],
|
||||
};
|
||||
capabilities.experimental = caps;
|
||||
}
|
||||
initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
|
||||
}
|
||||
dispose(): void {
|
||||
}
|
||||
initialize(
|
||||
_capabilities: lc.ServerCapabilities<any>,
|
||||
_documentSelector: lc.DocumentSelector | undefined
|
||||
): void {}
|
||||
dispose(): void {}
|
||||
}
|
||||
|
||||
function isCodeActionWithoutEditsAndCommands(value: any): boolean {
|
||||
const candidate: lc.CodeAction = value;
|
||||
return candidate && Is.string(candidate.title) &&
|
||||
(candidate.diagnostics === void 0 || Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
|
||||
return (
|
||||
candidate &&
|
||||
Is.string(candidate.title) &&
|
||||
(candidate.diagnostics === void 0 ||
|
||||
Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
|
||||
(candidate.kind === void 0 || Is.string(candidate.kind)) &&
|
||||
(candidate.edit === void 0 && candidate.command === void 0);
|
||||
candidate.edit === void 0 &&
|
||||
candidate.command === void 0
|
||||
);
|
||||
}
|
||||
|
@ -1,32 +1,33 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as lc from 'vscode-languageclient';
|
||||
import * as ra from './lsp_ext';
|
||||
import * as path from 'path';
|
||||
import * as vscode from "vscode";
|
||||
import * as lc from "vscode-languageclient";
|
||||
import * as ra from "./lsp_ext";
|
||||
import * as path from "path";
|
||||
|
||||
import { Ctx, Cmd } from './ctx';
|
||||
import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets';
|
||||
import { spawnSync } from 'child_process';
|
||||
import { RunnableQuickPick, selectRunnable, createTask, createArgs } from './run';
|
||||
import { AstInspector } from './ast_inspector';
|
||||
import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from './util';
|
||||
import { startDebugSession, makeDebugConfig } from './debug';
|
||||
import { LanguageClient } from 'vscode-languageclient/node';
|
||||
import { Ctx, Cmd } from "./ctx";
|
||||
import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets";
|
||||
import { spawnSync } from "child_process";
|
||||
import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run";
|
||||
import { AstInspector } from "./ast_inspector";
|
||||
import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from "./util";
|
||||
import { startDebugSession, makeDebugConfig } from "./debug";
|
||||
import { LanguageClient } from "vscode-languageclient/node";
|
||||
|
||||
export * from './ast_inspector';
|
||||
export * from './run';
|
||||
export * from "./ast_inspector";
|
||||
export * from "./run";
|
||||
|
||||
export function analyzerStatus(ctx: Ctx): Cmd {
|
||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse('rust-analyzer-status://status');
|
||||
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 '';
|
||||
if (!vscode.window.activeTextEditor) return "";
|
||||
|
||||
const params: ra.AnalyzerStatusParams = {};
|
||||
const doc = ctx.activeRustEditor?.document;
|
||||
if (doc != null) {
|
||||
params.textDocument = ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(doc);
|
||||
params.textDocument =
|
||||
ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(doc);
|
||||
}
|
||||
return ctx.client.sendRequest(ra.analyzerStatus, params);
|
||||
}
|
||||
@ -34,48 +35,42 @@ export function analyzerStatus(ctx: Ctx): Cmd {
|
||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||
return this.eventEmitter.event;
|
||||
}
|
||||
}();
|
||||
})();
|
||||
|
||||
ctx.pushCleanup(
|
||||
vscode.workspace.registerTextDocumentContentProvider(
|
||||
'rust-analyzer-status',
|
||||
tdcp,
|
||||
),
|
||||
vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-status", tdcp)
|
||||
);
|
||||
|
||||
return async () => {
|
||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||
tdcp.eventEmitter.fire(tdcp.uri);
|
||||
void await vscode.window.showTextDocument(document, {
|
||||
void (await vscode.window.showTextDocument(document, {
|
||||
viewColumn: vscode.ViewColumn.Two,
|
||||
preserveFocus: true
|
||||
});
|
||||
preserveFocus: true,
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
export function memoryUsage(ctx: Ctx): Cmd {
|
||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse('rust-analyzer-memory://memory');
|
||||
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse("rust-analyzer-memory://memory");
|
||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||
|
||||
provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
|
||||
if (!vscode.window.activeTextEditor) return '';
|
||||
if (!vscode.window.activeTextEditor) return "";
|
||||
|
||||
return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => {
|
||||
return 'Per-query memory usage:\n' + mem + '\n(note: database has been cleared)';
|
||||
return "Per-query memory usage:\n" + mem + "\n(note: database has been cleared)";
|
||||
});
|
||||
}
|
||||
|
||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||
return this.eventEmitter.event;
|
||||
}
|
||||
}();
|
||||
})();
|
||||
|
||||
ctx.pushCleanup(
|
||||
vscode.workspace.registerTextDocumentContentProvider(
|
||||
'rust-analyzer-memory',
|
||||
tdcp,
|
||||
),
|
||||
vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-memory", tdcp)
|
||||
);
|
||||
|
||||
return async () => {
|
||||
@ -101,15 +96,15 @@ export function matchingBrace(ctx: Ctx): Cmd {
|
||||
if (!editor || !client) return;
|
||||
|
||||
const response = await client.sendRequest(ra.matchingBrace, {
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
||||
positions: editor.selections.map(s =>
|
||||
client.code2ProtocolConverter.asPosition(s.active),
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
editor.document
|
||||
),
|
||||
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 active = client.protocol2CodeConverter.asPosition(response[idx]);
|
||||
const anchor = sel.isEmpty ? active : sel.anchor;
|
||||
return new vscode.Selection(anchor, active);
|
||||
});
|
||||
@ -125,7 +120,9 @@ export function joinLines(ctx: Ctx): Cmd {
|
||||
|
||||
const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
|
||||
ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
editor.document
|
||||
),
|
||||
});
|
||||
await editor.edit(async (builder) => {
|
||||
(await client.protocol2CodeConverter.asTextEdits(items)).forEach((edit: any) => {
|
||||
@ -151,8 +148,10 @@ export function moveItem(ctx: Ctx, direction: ra.Direction): Cmd {
|
||||
|
||||
const lcEdits = await client.sendRequest(ra.moveItem, {
|
||||
range: client.code2ProtocolConverter.asRange(editor.selection),
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
||||
direction
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
editor.document
|
||||
),
|
||||
direction,
|
||||
});
|
||||
|
||||
if (!lcEdits) return;
|
||||
@ -169,15 +168,17 @@ export function onEnter(ctx: Ctx): Cmd {
|
||||
|
||||
if (!editor || !client) return false;
|
||||
|
||||
const lcEdits = await client.sendRequest(ra.onEnter, {
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
||||
position: client.code2ProtocolConverter.asPosition(
|
||||
editor.selection.active,
|
||||
),
|
||||
}).catch((_error: any) => {
|
||||
// client.handleFailedRequest(OnEnterRequest.type, error, null);
|
||||
return null;
|
||||
});
|
||||
const lcEdits = await client
|
||||
.sendRequest(ra.onEnter, {
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
editor.document
|
||||
),
|
||||
position: client.code2ProtocolConverter.asPosition(editor.selection.active),
|
||||
})
|
||||
.catch((_error: any) => {
|
||||
// client.handleFailedRequest(OnEnterRequest.type, error, null);
|
||||
return null;
|
||||
});
|
||||
if (!lcEdits) return false;
|
||||
|
||||
const edits = await client.protocol2CodeConverter.asTextEdits(lcEdits);
|
||||
@ -188,7 +189,7 @@ export function onEnter(ctx: Ctx): Cmd {
|
||||
return async () => {
|
||||
if (await handleKeypress()) return;
|
||||
|
||||
await vscode.commands.executeCommand('default:type', { text: '\n' });
|
||||
await vscode.commands.executeCommand("default:type", { text: "\n" });
|
||||
};
|
||||
}
|
||||
|
||||
@ -200,10 +201,10 @@ export function parentModule(ctx: Ctx): Cmd {
|
||||
if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return;
|
||||
|
||||
const locations = await client.sendRequest(ra.parentModule, {
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
||||
position: client.code2ProtocolConverter.asPosition(
|
||||
editor.selection.active,
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
editor.document
|
||||
),
|
||||
position: client.code2ProtocolConverter.asPosition(editor.selection.active),
|
||||
});
|
||||
if (!locations) return;
|
||||
|
||||
@ -220,7 +221,12 @@ export function parentModule(ctx: Ctx): Cmd {
|
||||
} else {
|
||||
const uri = editor.document.uri.toString();
|
||||
const position = client.code2ProtocolConverter.asPosition(editor.selection.active);
|
||||
await showReferencesImpl(client, uri, position, locations.map(loc => lc.Location.create(loc.targetUri, loc.targetRange)));
|
||||
await showReferencesImpl(
|
||||
client,
|
||||
uri,
|
||||
position,
|
||||
locations.map((loc) => lc.Location.create(loc.targetUri, loc.targetRange))
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -232,7 +238,9 @@ export function openCargoToml(ctx: Ctx): Cmd {
|
||||
if (!editor || !client) return;
|
||||
|
||||
const response = await client.sendRequest(ra.openCargoToml, {
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
editor.document
|
||||
),
|
||||
});
|
||||
if (!response) return;
|
||||
|
||||
@ -254,7 +262,9 @@ export function ssr(ctx: Ctx): Cmd {
|
||||
|
||||
const position = editor.selection.active;
|
||||
const selections = editor.selections;
|
||||
const textDocument = ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document);
|
||||
const textDocument = ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
editor.document
|
||||
);
|
||||
|
||||
const options: vscode.InputBoxOptions = {
|
||||
value: "() ==>> ()",
|
||||
@ -262,28 +272,41 @@ export function ssr(ctx: Ctx): Cmd {
|
||||
validateInput: async (x: string) => {
|
||||
try {
|
||||
await client.sendRequest(ra.ssr, {
|
||||
query: x, parseOnly: true, textDocument, position, selections,
|
||||
query: x,
|
||||
parseOnly: true,
|
||||
textDocument,
|
||||
position,
|
||||
selections,
|
||||
});
|
||||
} catch (e) {
|
||||
return e.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
const request = await vscode.window.showInputBox(options);
|
||||
if (!request) return;
|
||||
|
||||
await vscode.window.withProgress({
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Structured search replace in progress...",
|
||||
cancellable: false,
|
||||
}, async (_progress, token) => {
|
||||
const edit = await client.sendRequest(ra.ssr, {
|
||||
query: request, parseOnly: false, textDocument, position, selections,
|
||||
});
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Structured search replace in progress...",
|
||||
cancellable: false,
|
||||
},
|
||||
async (_progress, token) => {
|
||||
const edit = await client.sendRequest(ra.ssr, {
|
||||
query: request,
|
||||
parseOnly: false,
|
||||
textDocument,
|
||||
position,
|
||||
selections,
|
||||
});
|
||||
|
||||
await vscode.workspace.applyEdit(await client.protocol2CodeConverter.asWorkspaceEdit(edit, token));
|
||||
});
|
||||
await vscode.workspace.applyEdit(
|
||||
await client.protocol2CodeConverter.asWorkspaceEdit(edit, token)
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@ -292,17 +315,17 @@ export function serverVersion(ctx: Ctx): Cmd {
|
||||
const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
|
||||
const versionString = stdout.slice(`rust-analyzer `.length).trim();
|
||||
|
||||
void vscode.window.showInformationMessage(
|
||||
`rust-analyzer version: ${versionString}`
|
||||
);
|
||||
void vscode.window.showInformationMessage(`rust-analyzer version: ${versionString}`);
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleInlayHints(_ctx: Ctx): Cmd {
|
||||
return async () => {
|
||||
const config = vscode.workspace.getConfiguration("editor.inlayHints", { languageId: "rust" });
|
||||
const config = vscode.workspace.getConfiguration("editor.inlayHints", {
|
||||
languageId: "rust",
|
||||
});
|
||||
const value = !config.get("enabled");
|
||||
await config.update('enabled', value, vscode.ConfigurationTarget.Global);
|
||||
await config.update("enabled", value, vscode.ConfigurationTarget.Global);
|
||||
};
|
||||
}
|
||||
|
||||
@ -310,12 +333,20 @@ export function toggleInlayHints(_ctx: Ctx): Cmd {
|
||||
//
|
||||
// The contents of the file come from the `TextDocumentContentProvider`
|
||||
export function syntaxTree(ctx: Ctx): Cmd {
|
||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast');
|
||||
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse("rust-analyzer://syntaxtree/tree.rast");
|
||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||
constructor() {
|
||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
||||
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
|
||||
vscode.workspace.onDidChangeTextDocument(
|
||||
this.onDidChangeTextDocument,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
vscode.window.onDidChangeActiveTextEditor(
|
||||
this.onDidChangeActiveTextEditor,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
}
|
||||
|
||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||
@ -331,47 +362,51 @@ export function syntaxTree(ctx: Ctx): Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
provideTextDocumentContent(uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
|
||||
provideTextDocumentContent(
|
||||
uri: vscode.Uri,
|
||||
ct: vscode.CancellationToken
|
||||
): vscode.ProviderResult<string> {
|
||||
const rustEditor = ctx.activeRustEditor;
|
||||
if (!rustEditor) return '';
|
||||
if (!rustEditor) return "";
|
||||
|
||||
// When the range based query is enabled we take the range of the selection
|
||||
const range = uri.query === 'range=true' && !rustEditor.selection.isEmpty
|
||||
? ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
|
||||
: null;
|
||||
const range =
|
||||
uri.query === "range=true" && !rustEditor.selection.isEmpty
|
||||
? ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
|
||||
: null;
|
||||
|
||||
const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range, };
|
||||
const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range };
|
||||
return ctx.client.sendRequest(ra.syntaxTree, params, ct);
|
||||
}
|
||||
|
||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||
return this.eventEmitter.event;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
void new AstInspector(ctx);
|
||||
|
||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
|
||||
ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
|
||||
brackets: [["[", ")"]],
|
||||
}));
|
||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider("rust-analyzer", tdcp));
|
||||
ctx.pushCleanup(
|
||||
vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
|
||||
brackets: [["[", ")"]],
|
||||
})
|
||||
);
|
||||
|
||||
return async () => {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const rangeEnabled = !!editor && !editor.selection.isEmpty;
|
||||
|
||||
const uri = rangeEnabled
|
||||
? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
|
||||
: tdcp.uri;
|
||||
const uri = rangeEnabled ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`) : tdcp.uri;
|
||||
|
||||
const document = await vscode.workspace.openTextDocument(uri);
|
||||
|
||||
tdcp.eventEmitter.fire(uri);
|
||||
|
||||
void await vscode.window.showTextDocument(document, {
|
||||
void (await vscode.window.showTextDocument(document, {
|
||||
viewColumn: vscode.ViewColumn.Two,
|
||||
preserveFocus: true
|
||||
});
|
||||
preserveFocus: true,
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
@ -379,12 +414,20 @@ export function syntaxTree(ctx: Ctx): Cmd {
|
||||
//
|
||||
// The contents of the file come from the `TextDocumentContentProvider`
|
||||
export function viewHir(ctx: Ctx): Cmd {
|
||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse('rust-analyzer://viewHir/hir.txt');
|
||||
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse("rust-analyzer://viewHir/hir.txt");
|
||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||
constructor() {
|
||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
||||
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
|
||||
vscode.workspace.onDidChangeTextDocument(
|
||||
this.onDidChangeTextDocument,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
vscode.window.onDidChangeActiveTextEditor(
|
||||
this.onDidChangeActiveTextEditor,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
}
|
||||
|
||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||
@ -400,16 +443,19 @@ export function viewHir(ctx: Ctx): Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
|
||||
provideTextDocumentContent(
|
||||
_uri: vscode.Uri,
|
||||
ct: vscode.CancellationToken
|
||||
): vscode.ProviderResult<string> {
|
||||
const rustEditor = ctx.activeRustEditor;
|
||||
const client = ctx.client;
|
||||
if (!rustEditor || !client) return '';
|
||||
if (!rustEditor || !client) return "";
|
||||
|
||||
const params = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document),
|
||||
position: client.code2ProtocolConverter.asPosition(
|
||||
rustEditor.selection.active,
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
rustEditor.document
|
||||
),
|
||||
position: client.code2ProtocolConverter.asPosition(rustEditor.selection.active),
|
||||
};
|
||||
return client.sendRequest(ra.viewHir, params, ct);
|
||||
}
|
||||
@ -417,27 +463,35 @@ export function viewHir(ctx: Ctx): Cmd {
|
||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||
return this.eventEmitter.event;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
|
||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider("rust-analyzer", tdcp));
|
||||
|
||||
return async () => {
|
||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||
tdcp.eventEmitter.fire(tdcp.uri);
|
||||
void await vscode.window.showTextDocument(document, {
|
||||
void (await vscode.window.showTextDocument(document, {
|
||||
viewColumn: vscode.ViewColumn.Two,
|
||||
preserveFocus: true
|
||||
});
|
||||
preserveFocus: true,
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
export function viewFileText(ctx: Ctx): Cmd {
|
||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse('rust-analyzer://viewFileText/file.rs');
|
||||
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse("rust-analyzer://viewFileText/file.rs");
|
||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||
constructor() {
|
||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
||||
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
|
||||
vscode.workspace.onDidChangeTextDocument(
|
||||
this.onDidChangeTextDocument,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
vscode.window.onDidChangeActiveTextEditor(
|
||||
this.onDidChangeActiveTextEditor,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
}
|
||||
|
||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||
@ -453,39 +507,52 @@ export function viewFileText(ctx: Ctx): Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
|
||||
provideTextDocumentContent(
|
||||
_uri: vscode.Uri,
|
||||
ct: vscode.CancellationToken
|
||||
): vscode.ProviderResult<string> {
|
||||
const rustEditor = ctx.activeRustEditor;
|
||||
const client = ctx.client;
|
||||
if (!rustEditor || !client) return '';
|
||||
if (!rustEditor || !client) return "";
|
||||
|
||||
const params = client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document);
|
||||
const params = client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
rustEditor.document
|
||||
);
|
||||
return client.sendRequest(ra.viewFileText, params, ct);
|
||||
}
|
||||
|
||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||
return this.eventEmitter.event;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
|
||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider("rust-analyzer", tdcp));
|
||||
|
||||
return async () => {
|
||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||
tdcp.eventEmitter.fire(tdcp.uri);
|
||||
void await vscode.window.showTextDocument(document, {
|
||||
void (await vscode.window.showTextDocument(document, {
|
||||
viewColumn: vscode.ViewColumn.Two,
|
||||
preserveFocus: true
|
||||
});
|
||||
preserveFocus: true,
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
export function viewItemTree(ctx: Ctx): Cmd {
|
||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse('rust-analyzer://viewItemTree/itemtree.rs');
|
||||
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse("rust-analyzer://viewItemTree/itemtree.rs");
|
||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||
constructor() {
|
||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
||||
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
|
||||
vscode.workspace.onDidChangeTextDocument(
|
||||
this.onDidChangeTextDocument,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
vscode.window.onDidChangeActiveTextEditor(
|
||||
this.onDidChangeActiveTextEditor,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
}
|
||||
|
||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||
@ -501,13 +568,18 @@ export function viewItemTree(ctx: Ctx): Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
|
||||
provideTextDocumentContent(
|
||||
_uri: vscode.Uri,
|
||||
ct: vscode.CancellationToken
|
||||
): vscode.ProviderResult<string> {
|
||||
const rustEditor = ctx.activeRustEditor;
|
||||
const client = ctx.client;
|
||||
if (!rustEditor || !client) return '';
|
||||
if (!rustEditor || !client) return "";
|
||||
|
||||
const params = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document),
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
rustEditor.document
|
||||
),
|
||||
};
|
||||
return client.sendRequest(ra.viewItemTree, params, ct);
|
||||
}
|
||||
@ -515,17 +587,17 @@ export function viewItemTree(ctx: Ctx): Cmd {
|
||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||
return this.eventEmitter.event;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
|
||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider("rust-analyzer", tdcp));
|
||||
|
||||
return async () => {
|
||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||
tdcp.eventEmitter.fire(tdcp.uri);
|
||||
void await vscode.window.showTextDocument(document, {
|
||||
void (await vscode.window.showTextDocument(document, {
|
||||
viewColumn: vscode.ViewColumn.Two,
|
||||
preserveFocus: true
|
||||
});
|
||||
preserveFocus: true,
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
@ -533,11 +605,16 @@ function crateGraph(ctx: Ctx, full: boolean): Cmd {
|
||||
return async () => {
|
||||
const nodeModulesPath = vscode.Uri.file(path.join(ctx.extensionPath, "node_modules"));
|
||||
|
||||
const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two, {
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
localResourceRoots: [nodeModulesPath]
|
||||
});
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
"rust-analyzer.crate-graph",
|
||||
"rust-analyzer crate graph",
|
||||
vscode.ViewColumn.Two,
|
||||
{
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
localResourceRoots: [nodeModulesPath],
|
||||
}
|
||||
);
|
||||
const params = {
|
||||
full: full,
|
||||
};
|
||||
@ -601,29 +678,31 @@ export function viewFullCrateGraph(ctx: Ctx): Cmd {
|
||||
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 += "// " + "=".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');
|
||||
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 '';
|
||||
if (!editor || !client) return "";
|
||||
|
||||
const position = editor.selection.active;
|
||||
|
||||
const expanded = await client.sendRequest(ra.expandMacro, {
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
editor.document
|
||||
),
|
||||
position,
|
||||
});
|
||||
|
||||
if (expanded == null) return 'Not available';
|
||||
if (expanded == null) return "Not available";
|
||||
|
||||
return codeFormat(expanded);
|
||||
}
|
||||
@ -631,23 +710,14 @@ export function expandMacro(ctx: Ctx): Cmd {
|
||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||
return this.eventEmitter.event;
|
||||
}
|
||||
}();
|
||||
})();
|
||||
|
||||
ctx.pushCleanup(
|
||||
vscode.workspace.registerTextDocumentContentProvider(
|
||||
'rust-analyzer',
|
||||
tdcp,
|
||||
),
|
||||
);
|
||||
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,
|
||||
);
|
||||
return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
|
||||
};
|
||||
}
|
||||
|
||||
@ -655,13 +725,18 @@ export function reloadWorkspace(ctx: Ctx): Cmd {
|
||||
return async () => ctx.client.sendRequest(ra.reloadWorkspace);
|
||||
}
|
||||
|
||||
async function showReferencesImpl(client: LanguageClient, uri: string, position: lc.Position, locations: lc.Location[]) {
|
||||
async function showReferencesImpl(
|
||||
client: LanguageClient,
|
||||
uri: string,
|
||||
position: lc.Position,
|
||||
locations: lc.Location[]
|
||||
) {
|
||||
if (client) {
|
||||
await vscode.commands.executeCommand(
|
||||
'editor.action.showReferences',
|
||||
"editor.action.showReferences",
|
||||
vscode.Uri.parse(uri),
|
||||
client.protocol2CodeConverter.asPosition(position),
|
||||
locations.map(client.protocol2CodeConverter.asLocation),
|
||||
locations.map(client.protocol2CodeConverter.asLocation)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -677,8 +752,8 @@ export function applyActionGroup(_ctx: Ctx): Cmd {
|
||||
const selectedAction = await vscode.window.showQuickPick(actions);
|
||||
if (!selectedAction) return;
|
||||
await vscode.commands.executeCommand(
|
||||
'rust-analyzer.resolveCodeAction',
|
||||
selectedAction.arguments,
|
||||
"rust-analyzer.resolveCodeAction",
|
||||
selectedAction.arguments
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -699,12 +774,11 @@ export function gotoLocation(ctx: Ctx): Cmd {
|
||||
|
||||
export function openDocs(ctx: Ctx): Cmd {
|
||||
return async () => {
|
||||
|
||||
const client = ctx.client;
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || !client) {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
const position = editor.selection.active;
|
||||
const textDocument = { uri: editor.document.uri.toString() };
|
||||
@ -715,7 +789,6 @@ export function openDocs(ctx: Ctx): Cmd {
|
||||
await vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(doclink));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export function resolveCodeAction(ctx: Ctx): Cmd {
|
||||
@ -730,8 +803,13 @@ export function resolveCodeAction(ctx: Ctx): Cmd {
|
||||
const edit = await client.protocol2CodeConverter.asWorkspaceEdit(itemEdit);
|
||||
// filter out all text edits and recreate the WorkspaceEdit without them so we can apply
|
||||
// snippet edits on our own
|
||||
const lcFileSystemEdit = { ...itemEdit, documentChanges: itemEdit.documentChanges?.filter(change => "kind" in change) };
|
||||
const fileSystemEdit = await client.protocol2CodeConverter.asWorkspaceEdit(lcFileSystemEdit);
|
||||
const lcFileSystemEdit = {
|
||||
...itemEdit,
|
||||
documentChanges: itemEdit.documentChanges?.filter((change) => "kind" in change),
|
||||
};
|
||||
const fileSystemEdit = await client.protocol2CodeConverter.asWorkspaceEdit(
|
||||
lcFileSystemEdit
|
||||
);
|
||||
await vscode.workspace.applyEdit(fileSystemEdit);
|
||||
await applySnippetWorkspaceEdit(edit);
|
||||
if (item.command != null) {
|
||||
@ -753,7 +831,7 @@ export function run(ctx: Ctx): Cmd {
|
||||
const item = await selectRunnable(ctx, prevRunnable);
|
||||
if (!item) return;
|
||||
|
||||
item.detail = 'rerun';
|
||||
item.detail = "rerun";
|
||||
prevRunnable = item;
|
||||
const task = await createTask(item.runnable, ctx.config);
|
||||
return await vscode.tasks.executeTask(task);
|
||||
@ -767,29 +845,33 @@ export function peekTests(ctx: Ctx): Cmd {
|
||||
const editor = ctx.activeRustEditor;
|
||||
if (!editor || !client) return;
|
||||
|
||||
await vscode.window.withProgress({
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Looking for tests...",
|
||||
cancellable: false,
|
||||
}, async (_progress, _token) => {
|
||||
const uri = editor.document.uri.toString();
|
||||
const position = client.code2ProtocolConverter.asPosition(
|
||||
editor.selection.active,
|
||||
);
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Looking for tests...",
|
||||
cancellable: false,
|
||||
},
|
||||
async (_progress, _token) => {
|
||||
const uri = editor.document.uri.toString();
|
||||
const position = client.code2ProtocolConverter.asPosition(editor.selection.active);
|
||||
|
||||
const tests = await client.sendRequest(ra.relatedTests, {
|
||||
textDocument: { uri: uri },
|
||||
position: position,
|
||||
});
|
||||
const locations: lc.Location[] = tests.map(it =>
|
||||
lc.Location.create(it.runnable.location!.targetUri, it.runnable.location!.targetSelectionRange));
|
||||
const tests = await client.sendRequest(ra.relatedTests, {
|
||||
textDocument: { uri: uri },
|
||||
position: position,
|
||||
});
|
||||
const locations: lc.Location[] = tests.map((it) =>
|
||||
lc.Location.create(
|
||||
it.runnable.location!.targetUri,
|
||||
it.runnable.location!.targetSelectionRange
|
||||
)
|
||||
);
|
||||
|
||||
await showReferencesImpl(client, uri, position, locations);
|
||||
});
|
||||
await showReferencesImpl(client, uri, position, locations);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function runSingle(ctx: Ctx): Cmd {
|
||||
return async (runnable: ra.Runnable) => {
|
||||
const editor = ctx.activeRustEditor;
|
||||
@ -826,7 +908,7 @@ export function debug(ctx: Ctx): Cmd {
|
||||
const item = await selectRunnable(ctx, prevDebuggee, true);
|
||||
if (!item) return;
|
||||
|
||||
item.detail = 'restart';
|
||||
item.detail = "restart";
|
||||
prevDebuggee = item;
|
||||
return await startDebugSession(ctx, item.runnable);
|
||||
};
|
||||
|
@ -1,13 +1,16 @@
|
||||
import path = require('path');
|
||||
import * as vscode from 'vscode';
|
||||
import { Env } from './client';
|
||||
import path = require("path");
|
||||
import * as vscode from "vscode";
|
||||
import { Env } from "./client";
|
||||
import { log } from "./util";
|
||||
|
||||
export type UpdatesChannel = "stable" | "nightly";
|
||||
|
||||
const NIGHTLY_TAG = "nightly";
|
||||
|
||||
export type RunnableEnvCfg = undefined | Record<string, string> | { mask?: string; env: Record<string, string> }[];
|
||||
export type RunnableEnvCfg =
|
||||
| undefined
|
||||
| Record<string, string>
|
||||
| { mask?: string; env: Record<string, string> }[];
|
||||
|
||||
export class Config {
|
||||
readonly extensionId = "rust-lang.rust-analyzer";
|
||||
@ -20,8 +23,7 @@ export class Config {
|
||||
"procMacro",
|
||||
"files",
|
||||
"lens", // works as lens.*
|
||||
]
|
||||
.map(opt => `${this.rootSection}.${opt}`);
|
||||
].map((opt) => `${this.rootSection}.${opt}`);
|
||||
|
||||
readonly package: {
|
||||
version: string;
|
||||
@ -33,7 +35,11 @@ export class Config {
|
||||
|
||||
constructor(ctx: vscode.ExtensionContext) {
|
||||
this.globalStorageUri = ctx.globalStorageUri;
|
||||
vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions);
|
||||
vscode.workspace.onDidChangeConfiguration(
|
||||
this.onDidChangeConfiguration,
|
||||
this,
|
||||
ctx.subscriptions
|
||||
);
|
||||
this.refreshLogging();
|
||||
}
|
||||
|
||||
@ -48,8 +54,8 @@ export class Config {
|
||||
private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) {
|
||||
this.refreshLogging();
|
||||
|
||||
const requiresReloadOpt = this.requiresReloadOpts.find(
|
||||
opt => event.affectsConfiguration(opt)
|
||||
const requiresReloadOpt = this.requiresReloadOpts.find((opt) =>
|
||||
event.affectsConfiguration(opt)
|
||||
);
|
||||
|
||||
if (!requiresReloadOpt) return;
|
||||
@ -94,8 +100,12 @@ export class Config {
|
||||
get serverPath() {
|
||||
return this.get<null | string>("server.path") ?? this.get<null | string>("serverPath");
|
||||
}
|
||||
get serverExtraEnv() { return this.get<Env | null>("server.extraEnv") ?? {}; }
|
||||
get traceExtension() { return this.get<boolean>("trace.extension"); }
|
||||
get serverExtraEnv() {
|
||||
return this.get<Env | null>("server.extraEnv") ?? {};
|
||||
}
|
||||
get traceExtension() {
|
||||
return this.get<boolean>("trace.extension");
|
||||
}
|
||||
|
||||
get cargoRunner() {
|
||||
return this.get<string | undefined>("cargoRunner");
|
||||
@ -109,7 +119,8 @@ export class Config {
|
||||
let sourceFileMap = this.get<Record<string, string> | "auto">("debug.sourceFileMap");
|
||||
if (sourceFileMap !== "auto") {
|
||||
// "/rustc/<id>" used by suggestions only.
|
||||
const { ["/rustc/<id>"]: _, ...trimmed } = this.get<Record<string, string>>("debug.sourceFileMap");
|
||||
const { ["/rustc/<id>"]: _, ...trimmed } =
|
||||
this.get<Record<string, string>>("debug.sourceFileMap");
|
||||
sourceFileMap = trimmed;
|
||||
}
|
||||
|
||||
@ -117,7 +128,7 @@ export class Config {
|
||||
engine: this.get<string>("debug.engine"),
|
||||
engineSettings: this.get<object>("debug.engineSettings"),
|
||||
openDebugPane: this.get<boolean>("debug.openDebugPane"),
|
||||
sourceFileMap: sourceFileMap
|
||||
sourceFileMap: sourceFileMap,
|
||||
};
|
||||
}
|
||||
|
||||
@ -139,57 +150,69 @@ export class Config {
|
||||
|
||||
export async function updateConfig(config: vscode.WorkspaceConfiguration) {
|
||||
const renames = [
|
||||
["assist.allowMergingIntoGlobImports", "imports.merge.glob",],
|
||||
["assist.exprFillDefault", "assist.expressionFillDefault",],
|
||||
["assist.importEnforceGranularity", "imports.granularity.enforce",],
|
||||
["assist.importGranularity", "imports.granularity.group",],
|
||||
["assist.importMergeBehavior", "imports.granularity.group",],
|
||||
["assist.importMergeBehaviour", "imports.granularity.group",],
|
||||
["assist.importGroup", "imports.group.enable",],
|
||||
["assist.importPrefix", "imports.prefix",],
|
||||
["primeCaches.enable", "cachePriming.enable",],
|
||||
["cache.warmup", "cachePriming.enable",],
|
||||
["cargo.loadOutDirsFromCheck", "cargo.buildScripts.enable",],
|
||||
["cargo.runBuildScripts", "cargo.buildScripts.enable",],
|
||||
["cargo.runBuildScriptsCommand", "cargo.buildScripts.overrideCommand",],
|
||||
["cargo.useRustcWrapperForBuildScripts", "cargo.buildScripts.useRustcWrapper",],
|
||||
["completion.snippets", "completion.snippets.custom",],
|
||||
["diagnostics.enableExperimental", "diagnostics.experimental.enable",],
|
||||
["experimental.procAttrMacros", "procMacro.attributes.enable",],
|
||||
["highlighting.strings", "semanticHighlighting.strings.enable",],
|
||||
["highlightRelated.breakPoints", "highlightRelated.breakPoints.enable",],
|
||||
["highlightRelated.exitPoints", "highlightRelated.exitPoints.enable",],
|
||||
["highlightRelated.yieldPoints", "highlightRelated.yieldPoints.enable",],
|
||||
["highlightRelated.references", "highlightRelated.references.enable",],
|
||||
["hover.documentation", "hover.documentation.enable",],
|
||||
["hover.linksInHover", "hover.links.enable",],
|
||||
["hoverActions.linksInHover", "hover.links.enable",],
|
||||
["hoverActions.debug", "hover.actions.debug.enable",],
|
||||
["hoverActions.enable", "hover.actions.enable.enable",],
|
||||
["hoverActions.gotoTypeDef", "hover.actions.gotoTypeDef.enable",],
|
||||
["hoverActions.implementations", "hover.actions.implementations.enable",],
|
||||
["hoverActions.references", "hover.actions.references.enable",],
|
||||
["hoverActions.run", "hover.actions.run.enable",],
|
||||
["inlayHints.chainingHints", "inlayHints.chainingHints.enable",],
|
||||
["inlayHints.closureReturnTypeHints", "inlayHints.closureReturnTypeHints.enable",],
|
||||
["inlayHints.hideNamedConstructorHints", "inlayHints.typeHints.hideNamedConstructor",],
|
||||
["inlayHints.parameterHints", "inlayHints.parameterHints.enable",],
|
||||
["inlayHints.reborrowHints", "inlayHints.reborrowHints.enable",],
|
||||
["inlayHints.typeHints", "inlayHints.typeHints.enable",],
|
||||
["lruCapacity", "lru.capacity",],
|
||||
["runnables.cargoExtraArgs", "runnables.extraArgs",],
|
||||
["runnables.overrideCargo", "runnables.command",],
|
||||
["rustcSource", "rustc.source",],
|
||||
["rustfmt.enableRangeFormatting", "rustfmt.rangeFormatting.enable"]
|
||||
["assist.allowMergingIntoGlobImports", "imports.merge.glob"],
|
||||
["assist.exprFillDefault", "assist.expressionFillDefault"],
|
||||
["assist.importEnforceGranularity", "imports.granularity.enforce"],
|
||||
["assist.importGranularity", "imports.granularity.group"],
|
||||
["assist.importMergeBehavior", "imports.granularity.group"],
|
||||
["assist.importMergeBehaviour", "imports.granularity.group"],
|
||||
["assist.importGroup", "imports.group.enable"],
|
||||
["assist.importPrefix", "imports.prefix"],
|
||||
["primeCaches.enable", "cachePriming.enable"],
|
||||
["cache.warmup", "cachePriming.enable"],
|
||||
["cargo.loadOutDirsFromCheck", "cargo.buildScripts.enable"],
|
||||
["cargo.runBuildScripts", "cargo.buildScripts.enable"],
|
||||
["cargo.runBuildScriptsCommand", "cargo.buildScripts.overrideCommand"],
|
||||
["cargo.useRustcWrapperForBuildScripts", "cargo.buildScripts.useRustcWrapper"],
|
||||
["completion.snippets", "completion.snippets.custom"],
|
||||
["diagnostics.enableExperimental", "diagnostics.experimental.enable"],
|
||||
["experimental.procAttrMacros", "procMacro.attributes.enable"],
|
||||
["highlighting.strings", "semanticHighlighting.strings.enable"],
|
||||
["highlightRelated.breakPoints", "highlightRelated.breakPoints.enable"],
|
||||
["highlightRelated.exitPoints", "highlightRelated.exitPoints.enable"],
|
||||
["highlightRelated.yieldPoints", "highlightRelated.yieldPoints.enable"],
|
||||
["highlightRelated.references", "highlightRelated.references.enable"],
|
||||
["hover.documentation", "hover.documentation.enable"],
|
||||
["hover.linksInHover", "hover.links.enable"],
|
||||
["hoverActions.linksInHover", "hover.links.enable"],
|
||||
["hoverActions.debug", "hover.actions.debug.enable"],
|
||||
["hoverActions.enable", "hover.actions.enable.enable"],
|
||||
["hoverActions.gotoTypeDef", "hover.actions.gotoTypeDef.enable"],
|
||||
["hoverActions.implementations", "hover.actions.implementations.enable"],
|
||||
["hoverActions.references", "hover.actions.references.enable"],
|
||||
["hoverActions.run", "hover.actions.run.enable"],
|
||||
["inlayHints.chainingHints", "inlayHints.chainingHints.enable"],
|
||||
["inlayHints.closureReturnTypeHints", "inlayHints.closureReturnTypeHints.enable"],
|
||||
["inlayHints.hideNamedConstructorHints", "inlayHints.typeHints.hideNamedConstructor"],
|
||||
["inlayHints.parameterHints", "inlayHints.parameterHints.enable"],
|
||||
["inlayHints.reborrowHints", "inlayHints.reborrowHints.enable"],
|
||||
["inlayHints.typeHints", "inlayHints.typeHints.enable"],
|
||||
["lruCapacity", "lru.capacity"],
|
||||
["runnables.cargoExtraArgs", "runnables.extraArgs"],
|
||||
["runnables.overrideCargo", "runnables.command"],
|
||||
["rustcSource", "rustc.source"],
|
||||
["rustfmt.enableRangeFormatting", "rustfmt.rangeFormatting.enable"],
|
||||
];
|
||||
|
||||
for (const [oldKey, newKey] of renames) {
|
||||
const inspect = config.inspect(oldKey);
|
||||
if (inspect !== undefined) {
|
||||
const valMatrix = [
|
||||
{ val: inspect.globalValue, langVal: inspect.globalLanguageValue, target: vscode.ConfigurationTarget.Global },
|
||||
{ val: inspect.workspaceFolderValue, langVal: inspect.workspaceFolderLanguageValue, target: vscode.ConfigurationTarget.WorkspaceFolder },
|
||||
{ val: inspect.workspaceValue, langVal: inspect.workspaceLanguageValue, target: vscode.ConfigurationTarget.Workspace }
|
||||
{
|
||||
val: inspect.globalValue,
|
||||
langVal: inspect.globalLanguageValue,
|
||||
target: vscode.ConfigurationTarget.Global,
|
||||
},
|
||||
{
|
||||
val: inspect.workspaceFolderValue,
|
||||
langVal: inspect.workspaceFolderLanguageValue,
|
||||
target: vscode.ConfigurationTarget.WorkspaceFolder,
|
||||
},
|
||||
{
|
||||
val: inspect.workspaceValue,
|
||||
langVal: inspect.workspaceLanguageValue,
|
||||
target: vscode.ConfigurationTarget.Workspace,
|
||||
},
|
||||
];
|
||||
for (const { val, langVal, target } of valMatrix) {
|
||||
const pred = (val: unknown) => {
|
||||
@ -197,7 +220,14 @@ export async function updateConfig(config: vscode.WorkspaceConfiguration) {
|
||||
// that means on the next run we would find these again, but as objects with
|
||||
// these properties causing us to destroy the config
|
||||
// so filter those already updated ones out
|
||||
return val !== undefined && !(typeof val === "object" && val !== null && (val.hasOwnProperty("enable") || val.hasOwnProperty("custom")));
|
||||
return (
|
||||
val !== undefined &&
|
||||
!(
|
||||
typeof val === "object" &&
|
||||
val !== null &&
|
||||
(val.hasOwnProperty("enable") || val.hasOwnProperty("custom"))
|
||||
)
|
||||
);
|
||||
};
|
||||
if (pred(val)) {
|
||||
await config.update(newKey, val, target, false);
|
||||
@ -216,48 +246,50 @@ export function substituteVariablesInEnv(env: Env): Env {
|
||||
const missingDeps = new Set<string>();
|
||||
// vscode uses `env:ENV_NAME` for env vars resolution, and it's easier
|
||||
// to follow the same convention for our dependency tracking
|
||||
const definedEnvKeys = new Set(Object.keys(env).map(key => `env:${key}`));
|
||||
const envWithDeps = Object.fromEntries(Object.entries(env).map(([key, value]) => {
|
||||
const deps = new Set<string>();
|
||||
const depRe = new RegExp(/\${(?<depName>.+?)}/g);
|
||||
let match = undefined;
|
||||
while ((match = depRe.exec(value))) {
|
||||
const depName = match.groups!.depName;
|
||||
deps.add(depName);
|
||||
// `depName` at this point can have a form of `expression` or
|
||||
// `prefix:expression`
|
||||
if (!definedEnvKeys.has(depName)) {
|
||||
missingDeps.add(depName);
|
||||
const definedEnvKeys = new Set(Object.keys(env).map((key) => `env:${key}`));
|
||||
const envWithDeps = Object.fromEntries(
|
||||
Object.entries(env).map(([key, value]) => {
|
||||
const deps = new Set<string>();
|
||||
const depRe = new RegExp(/\${(?<depName>.+?)}/g);
|
||||
let match = undefined;
|
||||
while ((match = depRe.exec(value))) {
|
||||
const depName = match.groups!.depName;
|
||||
deps.add(depName);
|
||||
// `depName` at this point can have a form of `expression` or
|
||||
// `prefix:expression`
|
||||
if (!definedEnvKeys.has(depName)) {
|
||||
missingDeps.add(depName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [`env:${key}`, { deps: [...deps], value }];
|
||||
}));
|
||||
return [`env:${key}`, { deps: [...deps], value }];
|
||||
})
|
||||
);
|
||||
|
||||
const resolved = new Set<string>();
|
||||
for (const dep of missingDeps) {
|
||||
const match = /(?<prefix>.*?):(?<body>.+)/.exec(dep);
|
||||
if (match) {
|
||||
const { prefix, body } = match.groups!;
|
||||
if (prefix === 'env') {
|
||||
if (prefix === "env") {
|
||||
const envName = body;
|
||||
envWithDeps[dep] = {
|
||||
value: process.env[envName] ?? '',
|
||||
deps: []
|
||||
value: process.env[envName] ?? "",
|
||||
deps: [],
|
||||
};
|
||||
resolved.add(dep);
|
||||
} else {
|
||||
// we can't handle other prefixes at the moment
|
||||
// leave values as is, but still mark them as resolved
|
||||
envWithDeps[dep] = {
|
||||
value: '${' + dep + '}',
|
||||
deps: []
|
||||
value: "${" + dep + "}",
|
||||
deps: [],
|
||||
};
|
||||
resolved.add(dep);
|
||||
}
|
||||
} else {
|
||||
envWithDeps[dep] = {
|
||||
value: computeVscodeVar(dep),
|
||||
deps: []
|
||||
deps: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -267,11 +299,13 @@ export function substituteVariablesInEnv(env: Env): Env {
|
||||
do {
|
||||
leftToResolveSize = toResolve.size;
|
||||
for (const key of toResolve) {
|
||||
if (envWithDeps[key].deps.every(dep => resolved.has(dep))) {
|
||||
if (envWithDeps[key].deps.every((dep) => resolved.has(dep))) {
|
||||
envWithDeps[key].value = envWithDeps[key].value.replace(
|
||||
/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => {
|
||||
/\${(?<depName>.+?)}/g,
|
||||
(_wholeMatch, depName) => {
|
||||
return envWithDeps[depName].value;
|
||||
});
|
||||
}
|
||||
);
|
||||
resolved.add(key);
|
||||
toResolve.delete(key);
|
||||
}
|
||||
@ -302,16 +336,16 @@ function computeVscodeVar(varName: string): string {
|
||||
return folders[0].uri.fsPath;
|
||||
} else {
|
||||
// no workspace opened
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
workspaceFolderBasename: () => {
|
||||
const workspaceFolder = computeVscodeVar('workspaceFolder');
|
||||
const workspaceFolder = computeVscodeVar("workspaceFolder");
|
||||
if (workspaceFolder) {
|
||||
return path.basename(workspaceFolder);
|
||||
} else {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
@ -323,13 +357,13 @@ function computeVscodeVar(varName: string): string {
|
||||
// https://github.com/microsoft/vscode/blob/29eb316bb9f154b7870eb5204ec7f2e7cf649bec/src/vs/server/node/remoteTerminalChannel.ts#L56
|
||||
execPath: () => process.env.VSCODE_EXEC_PATH ?? process.execPath,
|
||||
|
||||
pathSeparator: () => path.sep
|
||||
pathSeparator: () => path.sep,
|
||||
};
|
||||
|
||||
if (varName in supportedVariables) {
|
||||
return supportedVariables[varName]();
|
||||
} else {
|
||||
// can't resolve, keep the expression as is
|
||||
return '${' + varName + '}';
|
||||
return "${" + varName + "}";
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as lc from 'vscode-languageclient/node';
|
||||
import * as ra from './lsp_ext';
|
||||
import * as vscode from "vscode";
|
||||
import * as lc from "vscode-languageclient/node";
|
||||
import * as ra from "./lsp_ext";
|
||||
|
||||
import { Config } from './config';
|
||||
import { createClient } from './client';
|
||||
import { isRustEditor, RustEditor } from './util';
|
||||
import { ServerStatusParams } from './lsp_ext';
|
||||
import { Config } from "./config";
|
||||
import { createClient } from "./client";
|
||||
import { isRustEditor, RustEditor } from "./util";
|
||||
import { ServerStatusParams } from "./lsp_ext";
|
||||
|
||||
export type Workspace =
|
||||
{
|
||||
kind: 'Workspace Folder';
|
||||
}
|
||||
| {
|
||||
kind: 'Detached Files';
|
||||
files: vscode.TextDocument[];
|
||||
};
|
||||
kind: "Workspace Folder";
|
||||
}
|
||||
| {
|
||||
kind: "Detached Files";
|
||||
files: vscode.TextDocument[];
|
||||
};
|
||||
|
||||
export class Ctx {
|
||||
private constructor(
|
||||
@ -22,16 +22,14 @@ export class Ctx {
|
||||
private readonly extCtx: vscode.ExtensionContext,
|
||||
readonly client: lc.LanguageClient,
|
||||
readonly serverPath: string,
|
||||
readonly statusBar: vscode.StatusBarItem,
|
||||
) {
|
||||
|
||||
}
|
||||
readonly statusBar: vscode.StatusBarItem
|
||||
) {}
|
||||
|
||||
static async create(
|
||||
config: Config,
|
||||
extCtx: vscode.ExtensionContext,
|
||||
serverPath: string,
|
||||
workspace: Workspace,
|
||||
workspace: Workspace
|
||||
): Promise<Ctx> {
|
||||
const client = await createClient(serverPath, workspace, config.serverExtraEnv);
|
||||
|
||||
@ -52,9 +50,7 @@ export class Ctx {
|
||||
|
||||
get activeRustEditor(): RustEditor | undefined {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
return editor && isRustEditor(editor)
|
||||
? editor
|
||||
: undefined;
|
||||
return editor && isRustEditor(editor) ? editor : undefined;
|
||||
}
|
||||
|
||||
get visibleRustEditors(): RustEditor[] {
|
||||
|
@ -1,14 +1,19 @@
|
||||
import * as os from "os";
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as ra from './lsp_ext';
|
||||
import * as vscode from "vscode";
|
||||
import * as path from "path";
|
||||
import * as ra from "./lsp_ext";
|
||||
|
||||
import { Cargo, getRustcId, getSysroot } from './toolchain';
|
||||
import { Cargo, getRustcId, getSysroot } from "./toolchain";
|
||||
import { Ctx } from "./ctx";
|
||||
import { prepareEnv } from "./run";
|
||||
|
||||
const debugOutput = vscode.window.createOutputChannel("Debug");
|
||||
type DebugConfigProvider = (config: ra.Runnable, executable: string, env: Record<string, string>, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration;
|
||||
type DebugConfigProvider = (
|
||||
config: ra.Runnable,
|
||||
executable: string,
|
||||
env: Record<string, string>,
|
||||
sourceFileMap?: Record<string, string>
|
||||
) => vscode.DebugConfiguration;
|
||||
|
||||
export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> {
|
||||
const scope = ctx.activeRustEditor?.document.uri;
|
||||
@ -20,9 +25,13 @@ export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<
|
||||
const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
|
||||
const configurations = wsLaunchSection.get<any[]>("configurations") || [];
|
||||
|
||||
const index = configurations.findIndex(c => c.name === debugConfig.name);
|
||||
const index = configurations.findIndex((c) => c.name === debugConfig.name);
|
||||
if (index !== -1) {
|
||||
const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update');
|
||||
const answer = await vscode.window.showErrorMessage(
|
||||
`Launch configuration '${debugConfig.name}' already exists!`,
|
||||
"Cancel",
|
||||
"Update"
|
||||
);
|
||||
if (answer === "Cancel") return;
|
||||
|
||||
configurations[index] = debugConfig;
|
||||
@ -40,7 +49,7 @@ export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promis
|
||||
const wsLaunchSection = vscode.workspace.getConfiguration("launch");
|
||||
const configurations = wsLaunchSection.get<any[]>("configurations") || [];
|
||||
|
||||
const index = configurations.findIndex(c => c.name === runnable.label);
|
||||
const index = configurations.findIndex((c) => c.name === runnable.label);
|
||||
if (-1 !== index) {
|
||||
debugConfig = configurations[index];
|
||||
message = " (from launch.json)";
|
||||
@ -56,13 +65,16 @@ export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promis
|
||||
return vscode.debug.startDebugging(undefined, debugConfig);
|
||||
}
|
||||
|
||||
async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<vscode.DebugConfiguration | undefined> {
|
||||
async function getDebugConfiguration(
|
||||
ctx: Ctx,
|
||||
runnable: ra.Runnable
|
||||
): Promise<vscode.DebugConfiguration | undefined> {
|
||||
const editor = ctx.activeRustEditor;
|
||||
if (!editor) return;
|
||||
|
||||
const knownEngines: Record<string, DebugConfigProvider> = {
|
||||
"vadimcn.vscode-lldb": getLldbDebugConfig,
|
||||
"ms-vscode.cpptools": getCppvsDebugConfig
|
||||
"ms-vscode.cpptools": getCppvsDebugConfig,
|
||||
};
|
||||
const debugOptions = ctx.config.debug;
|
||||
|
||||
@ -77,8 +89,10 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
|
||||
}
|
||||
|
||||
if (!debugEngine) {
|
||||
await vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)`
|
||||
+ ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`);
|
||||
await vscode.window.showErrorMessage(
|
||||
`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` +
|
||||
` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -91,15 +105,17 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders!;
|
||||
const isMultiFolderWorkspace = workspaceFolders.length > 1;
|
||||
const firstWorkspace = workspaceFolders[0];
|
||||
const workspace = !isMultiFolderWorkspace || !runnable.args.workspaceRoot ?
|
||||
firstWorkspace :
|
||||
workspaceFolders.find(w => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) || firstWorkspace;
|
||||
const workspace =
|
||||
!isMultiFolderWorkspace || !runnable.args.workspaceRoot
|
||||
? firstWorkspace
|
||||
: workspaceFolders.find((w) => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) ||
|
||||
firstWorkspace;
|
||||
|
||||
const wsFolder = path.normalize(workspace.uri.fsPath);
|
||||
const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : '';
|
||||
const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : "";
|
||||
function simplifyPath(p: string): string {
|
||||
// see https://github.com/rust-analyzer/rust-analyzer/pull/5513#issuecomment-663458818 for why this is needed
|
||||
return path.normalize(p).replace(wsFolder, '${workspaceFolder' + workspaceQualifier + '}');
|
||||
return path.normalize(p).replace(wsFolder, "${workspaceFolder" + workspaceQualifier + "}");
|
||||
}
|
||||
|
||||
const executable = await getDebugExecutable(runnable);
|
||||
@ -114,7 +130,12 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
|
||||
sourceFileMap[`/rustc/${commitHash}/`] = rustlib;
|
||||
}
|
||||
|
||||
const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), env, sourceFileMap);
|
||||
const debugConfig = knownEngines[debugEngine.id](
|
||||
runnable,
|
||||
simplifyPath(executable),
|
||||
env,
|
||||
sourceFileMap
|
||||
);
|
||||
if (debugConfig.type in debugOptions.engineSettings) {
|
||||
const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
|
||||
for (var key in settingsMap) {
|
||||
@ -136,14 +157,19 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
|
||||
}
|
||||
|
||||
async function getDebugExecutable(runnable: ra.Runnable): Promise<string> {
|
||||
const cargo = new Cargo(runnable.args.workspaceRoot || '.', debugOutput);
|
||||
const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput);
|
||||
const executable = await cargo.executableFromArgs(runnable.args.cargoArgs);
|
||||
|
||||
// if we are here, there were no compilation errors.
|
||||
return executable;
|
||||
}
|
||||
|
||||
function getLldbDebugConfig(runnable: ra.Runnable, executable: string, env: Record<string, string>, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
|
||||
function getLldbDebugConfig(
|
||||
runnable: ra.Runnable,
|
||||
executable: string,
|
||||
env: Record<string, string>,
|
||||
sourceFileMap?: Record<string, string>
|
||||
): vscode.DebugConfiguration {
|
||||
return {
|
||||
type: "lldb",
|
||||
request: "launch",
|
||||
@ -153,13 +179,18 @@ function getLldbDebugConfig(runnable: ra.Runnable, executable: string, env: Reco
|
||||
cwd: runnable.args.workspaceRoot,
|
||||
sourceMap: sourceFileMap,
|
||||
sourceLanguages: ["rust"],
|
||||
env
|
||||
env,
|
||||
};
|
||||
}
|
||||
|
||||
function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, env: Record<string, string>, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
|
||||
function getCppvsDebugConfig(
|
||||
runnable: ra.Runnable,
|
||||
executable: string,
|
||||
env: Record<string, string>,
|
||||
sourceFileMap?: Record<string, string>
|
||||
): vscode.DebugConfiguration {
|
||||
return {
|
||||
type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg",
|
||||
type: os.platform() === "win32" ? "cppvsdbg" : "cppdbg",
|
||||
request: "launch",
|
||||
name: runnable.label,
|
||||
program: executable,
|
||||
|
@ -7,7 +7,9 @@ import * as lc from "vscode-languageclient";
|
||||
export interface AnalyzerStatusParams {
|
||||
textDocument?: lc.TextDocumentIdentifier;
|
||||
}
|
||||
export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus");
|
||||
export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>(
|
||||
"rust-analyzer/analyzerStatus"
|
||||
);
|
||||
export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
|
||||
export const shuffleCrateGraph = new lc.RequestType0<null, void>("rust-analyzer/shuffleCrateGraph");
|
||||
|
||||
@ -16,7 +18,9 @@ export interface ServerStatusParams {
|
||||
quiescent: boolean;
|
||||
message?: string;
|
||||
}
|
||||
export const serverStatus = new lc.NotificationType<ServerStatusParams>("experimental/serverStatus");
|
||||
export const serverStatus = new lc.NotificationType<ServerStatusParams>(
|
||||
"experimental/serverStatus"
|
||||
);
|
||||
|
||||
export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
|
||||
|
||||
@ -31,23 +35,33 @@ export interface SyntaxTreeParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
range: lc.Range | null;
|
||||
}
|
||||
export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("rust-analyzer/syntaxTree");
|
||||
export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>(
|
||||
"rust-analyzer/syntaxTree"
|
||||
);
|
||||
|
||||
export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir");
|
||||
export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
|
||||
"rust-analyzer/viewHir"
|
||||
);
|
||||
|
||||
export const viewFileText = new lc.RequestType<lc.TextDocumentIdentifier, string, void>("rust-analyzer/viewFileText");
|
||||
export const viewFileText = new lc.RequestType<lc.TextDocumentIdentifier, string, void>(
|
||||
"rust-analyzer/viewFileText"
|
||||
);
|
||||
|
||||
export interface ViewItemTreeParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
}
|
||||
|
||||
export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>("rust-analyzer/viewItemTree");
|
||||
export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>(
|
||||
"rust-analyzer/viewItemTree"
|
||||
);
|
||||
|
||||
export interface ViewCrateGraphParams {
|
||||
full: boolean;
|
||||
}
|
||||
|
||||
export const viewCrateGraph = new lc.RequestType<ViewCrateGraphParams, string, void>("rust-analyzer/viewCrateGraph");
|
||||
export const viewCrateGraph = new lc.RequestType<ViewCrateGraphParams, string, void>(
|
||||
"rust-analyzer/viewCrateGraph"
|
||||
);
|
||||
|
||||
export interface ExpandMacroParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
@ -57,23 +71,35 @@ export interface ExpandedMacro {
|
||||
name: string;
|
||||
expansion: string;
|
||||
}
|
||||
export const expandMacro = new lc.RequestType<ExpandMacroParams, ExpandedMacro | null, void>("rust-analyzer/expandMacro");
|
||||
export const expandMacro = new lc.RequestType<ExpandMacroParams, ExpandedMacro | null, void>(
|
||||
"rust-analyzer/expandMacro"
|
||||
);
|
||||
|
||||
export interface MatchingBraceParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
positions: lc.Position[];
|
||||
}
|
||||
export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position[], void>("experimental/matchingBrace");
|
||||
export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position[], void>(
|
||||
"experimental/matchingBrace"
|
||||
);
|
||||
|
||||
export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[] | null, void>("experimental/parentModule");
|
||||
export const parentModule = new lc.RequestType<
|
||||
lc.TextDocumentPositionParams,
|
||||
lc.LocationLink[] | null,
|
||||
void
|
||||
>("experimental/parentModule");
|
||||
|
||||
export interface JoinLinesParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
ranges: lc.Range[];
|
||||
}
|
||||
export const joinLines = new lc.RequestType<JoinLinesParams, lc.TextEdit[], void>("experimental/joinLines");
|
||||
export const joinLines = new lc.RequestType<JoinLinesParams, lc.TextEdit[], void>(
|
||||
"experimental/joinLines"
|
||||
);
|
||||
|
||||
export const onEnter = new lc.RequestType<lc.TextDocumentPositionParams, lc.TextEdit[], void>("experimental/onEnter");
|
||||
export const onEnter = new lc.RequestType<lc.TextDocumentPositionParams, lc.TextEdit[], void>(
|
||||
"experimental/onEnter"
|
||||
);
|
||||
|
||||
export interface RunnablesParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
@ -93,13 +119,17 @@ export interface Runnable {
|
||||
overrideCargo?: string;
|
||||
};
|
||||
}
|
||||
export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables");
|
||||
export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>(
|
||||
"experimental/runnables"
|
||||
);
|
||||
|
||||
export interface TestInfo {
|
||||
runnable: Runnable;
|
||||
}
|
||||
|
||||
export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, TestInfo[], void>("rust-analyzer/relatedTests");
|
||||
export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, TestInfo[], void>(
|
||||
"rust-analyzer/relatedTests"
|
||||
);
|
||||
|
||||
export interface SsrParams {
|
||||
query: string;
|
||||
@ -108,7 +138,7 @@ export interface SsrParams {
|
||||
position: lc.Position;
|
||||
selections: readonly lc.Range[];
|
||||
}
|
||||
export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr');
|
||||
export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>("experimental/ssr");
|
||||
|
||||
export interface CommandLink extends lc.Command {
|
||||
/**
|
||||
@ -122,15 +152,21 @@ export interface CommandLinkGroup {
|
||||
commands: CommandLink[];
|
||||
}
|
||||
|
||||
export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>('experimental/externalDocs');
|
||||
export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>(
|
||||
"experimental/externalDocs"
|
||||
);
|
||||
|
||||
export const openCargoToml = new lc.RequestType<OpenCargoTomlParams, lc.Location, void>("experimental/openCargoToml");
|
||||
export const openCargoToml = new lc.RequestType<OpenCargoTomlParams, lc.Location, void>(
|
||||
"experimental/openCargoToml"
|
||||
);
|
||||
|
||||
export interface OpenCargoTomlParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
}
|
||||
|
||||
export const moveItem = new lc.RequestType<MoveItemParams, lc.TextEdit[], void>("experimental/moveItem");
|
||||
export const moveItem = new lc.RequestType<MoveItemParams, lc.TextEdit[], void>(
|
||||
"experimental/moveItem"
|
||||
);
|
||||
|
||||
export interface MoveItemParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
@ -140,5 +176,5 @@ export interface MoveItemParams {
|
||||
|
||||
export const enum Direction {
|
||||
Up = "Up",
|
||||
Down = "Down"
|
||||
Down = "Down",
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as lc from 'vscode-languageclient/node';
|
||||
import * as vscode from "vscode";
|
||||
import * as lc from "vscode-languageclient/node";
|
||||
import * as os from "os";
|
||||
|
||||
import * as commands from './commands';
|
||||
import { Ctx } from './ctx';
|
||||
import { Config } from './config';
|
||||
import { log, isValidExecutable, isRustDocument } from './util';
|
||||
import { PersistentState } from './persistent_state';
|
||||
import { activateTaskProvider } from './tasks';
|
||||
import { setContextValue } from './util';
|
||||
import { exec } from 'child_process';
|
||||
import * as commands from "./commands";
|
||||
import { Ctx } from "./ctx";
|
||||
import { Config } from "./config";
|
||||
import { log, isValidExecutable, isRustDocument } from "./util";
|
||||
import { PersistentState } from "./persistent_state";
|
||||
import { activateTaskProvider } from "./tasks";
|
||||
import { setContextValue } from "./util";
|
||||
import { exec } from "child_process";
|
||||
|
||||
let ctx: Ctx | undefined;
|
||||
|
||||
@ -19,10 +19,12 @@ export interface RustAnalyzerExtensionApi {
|
||||
client: lc.LanguageClient;
|
||||
}
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<RustAnalyzerExtensionApi> {
|
||||
export async function activate(
|
||||
context: vscode.ExtensionContext
|
||||
): Promise<RustAnalyzerExtensionApi> {
|
||||
// VS Code doesn't show a notification when an extension fails to activate
|
||||
// so we do it ourselves.
|
||||
return await tryActivate(context).catch(err => {
|
||||
return await tryActivate(context).catch((err) => {
|
||||
void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`);
|
||||
throw err;
|
||||
});
|
||||
@ -31,7 +33,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<RustAn
|
||||
async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyzerExtensionApi> {
|
||||
const config = new Config(context);
|
||||
const state = new PersistentState(context.globalState);
|
||||
const serverPath = await bootstrap(context, config, state).catch(err => {
|
||||
const serverPath = await bootstrap(context, config, state).catch((err) => {
|
||||
let message = "bootstrap error. ";
|
||||
|
||||
message += 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). ';
|
||||
@ -42,9 +44,14 @@ async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyz
|
||||
});
|
||||
|
||||
if ((vscode.workspace.workspaceFolders || []).length === 0) {
|
||||
const rustDocuments = vscode.workspace.textDocuments.filter(document => isRustDocument(document));
|
||||
const rustDocuments = vscode.workspace.textDocuments.filter((document) =>
|
||||
isRustDocument(document)
|
||||
);
|
||||
if (rustDocuments.length > 0) {
|
||||
ctx = await Ctx.create(config, context, serverPath, { kind: 'Detached Files', files: rustDocuments });
|
||||
ctx = await Ctx.create(config, context, serverPath, {
|
||||
kind: "Detached Files",
|
||||
files: rustDocuments,
|
||||
});
|
||||
} else {
|
||||
throw new Error("no rust files are opened");
|
||||
}
|
||||
@ -63,13 +70,16 @@ async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyz
|
||||
ctx.pushCleanup(configureLanguage());
|
||||
|
||||
vscode.workspace.onDidChangeConfiguration(
|
||||
_ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }).catch(log.error),
|
||||
(_) =>
|
||||
ctx?.client
|
||||
?.sendNotification("workspace/didChangeConfiguration", { settings: "" })
|
||||
.catch(log.error),
|
||||
null,
|
||||
ctx.subscriptions,
|
||||
ctx.subscriptions
|
||||
);
|
||||
|
||||
return {
|
||||
client: ctx.client
|
||||
client: ctx.client,
|
||||
};
|
||||
}
|
||||
|
||||
@ -88,9 +98,8 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
|
||||
// "rust-analyzer is not available"
|
||||
// ),
|
||||
// )
|
||||
const defaultOnEnter = vscode.commands.registerCommand(
|
||||
'rust-analyzer.onEnter',
|
||||
() => vscode.commands.executeCommand('default:type', { text: '\n' }),
|
||||
const defaultOnEnter = vscode.commands.registerCommand("rust-analyzer.onEnter", () =>
|
||||
vscode.commands.executeCommand("default:type", { text: "\n" })
|
||||
);
|
||||
context.subscriptions.push(defaultOnEnter);
|
||||
|
||||
@ -99,8 +108,8 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
|
||||
// Commands which invokes manually via command palette, shortcut, etc.
|
||||
|
||||
// Reloading is inspired by @DanTup maneuver: https://github.com/microsoft/vscode/issues/45774#issuecomment-373423895
|
||||
ctx.registerCommand('reload', _ => async () => {
|
||||
void vscode.window.showInformationMessage('Reloading rust-analyzer...');
|
||||
ctx.registerCommand("reload", (_) => async () => {
|
||||
void vscode.window.showInformationMessage("Reloading rust-analyzer...");
|
||||
await deactivate();
|
||||
while (context.subscriptions.length > 0) {
|
||||
try {
|
||||
@ -112,45 +121,45 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
|
||||
await activate(context).catch(log.error);
|
||||
});
|
||||
|
||||
ctx.registerCommand('analyzerStatus', commands.analyzerStatus);
|
||||
ctx.registerCommand('memoryUsage', commands.memoryUsage);
|
||||
ctx.registerCommand('shuffleCrateGraph', commands.shuffleCrateGraph);
|
||||
ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace);
|
||||
ctx.registerCommand('matchingBrace', commands.matchingBrace);
|
||||
ctx.registerCommand('joinLines', commands.joinLines);
|
||||
ctx.registerCommand('parentModule', commands.parentModule);
|
||||
ctx.registerCommand('syntaxTree', commands.syntaxTree);
|
||||
ctx.registerCommand('viewHir', commands.viewHir);
|
||||
ctx.registerCommand('viewFileText', commands.viewFileText);
|
||||
ctx.registerCommand('viewItemTree', commands.viewItemTree);
|
||||
ctx.registerCommand('viewCrateGraph', commands.viewCrateGraph);
|
||||
ctx.registerCommand('viewFullCrateGraph', commands.viewFullCrateGraph);
|
||||
ctx.registerCommand('expandMacro', commands.expandMacro);
|
||||
ctx.registerCommand('run', commands.run);
|
||||
ctx.registerCommand('copyRunCommandLine', commands.copyRunCommandLine);
|
||||
ctx.registerCommand('debug', commands.debug);
|
||||
ctx.registerCommand('newDebugConfig', commands.newDebugConfig);
|
||||
ctx.registerCommand('openDocs', commands.openDocs);
|
||||
ctx.registerCommand('openCargoToml', commands.openCargoToml);
|
||||
ctx.registerCommand('peekTests', commands.peekTests);
|
||||
ctx.registerCommand('moveItemUp', commands.moveItemUp);
|
||||
ctx.registerCommand('moveItemDown', commands.moveItemDown);
|
||||
ctx.registerCommand("analyzerStatus", commands.analyzerStatus);
|
||||
ctx.registerCommand("memoryUsage", commands.memoryUsage);
|
||||
ctx.registerCommand("shuffleCrateGraph", commands.shuffleCrateGraph);
|
||||
ctx.registerCommand("reloadWorkspace", commands.reloadWorkspace);
|
||||
ctx.registerCommand("matchingBrace", commands.matchingBrace);
|
||||
ctx.registerCommand("joinLines", commands.joinLines);
|
||||
ctx.registerCommand("parentModule", commands.parentModule);
|
||||
ctx.registerCommand("syntaxTree", commands.syntaxTree);
|
||||
ctx.registerCommand("viewHir", commands.viewHir);
|
||||
ctx.registerCommand("viewFileText", commands.viewFileText);
|
||||
ctx.registerCommand("viewItemTree", commands.viewItemTree);
|
||||
ctx.registerCommand("viewCrateGraph", commands.viewCrateGraph);
|
||||
ctx.registerCommand("viewFullCrateGraph", commands.viewFullCrateGraph);
|
||||
ctx.registerCommand("expandMacro", commands.expandMacro);
|
||||
ctx.registerCommand("run", commands.run);
|
||||
ctx.registerCommand("copyRunCommandLine", commands.copyRunCommandLine);
|
||||
ctx.registerCommand("debug", commands.debug);
|
||||
ctx.registerCommand("newDebugConfig", commands.newDebugConfig);
|
||||
ctx.registerCommand("openDocs", commands.openDocs);
|
||||
ctx.registerCommand("openCargoToml", commands.openCargoToml);
|
||||
ctx.registerCommand("peekTests", commands.peekTests);
|
||||
ctx.registerCommand("moveItemUp", commands.moveItemUp);
|
||||
ctx.registerCommand("moveItemDown", commands.moveItemDown);
|
||||
|
||||
defaultOnEnter.dispose();
|
||||
ctx.registerCommand('onEnter', commands.onEnter);
|
||||
ctx.registerCommand("onEnter", commands.onEnter);
|
||||
|
||||
ctx.registerCommand('ssr', commands.ssr);
|
||||
ctx.registerCommand('serverVersion', commands.serverVersion);
|
||||
ctx.registerCommand('toggleInlayHints', commands.toggleInlayHints);
|
||||
ctx.registerCommand("ssr", commands.ssr);
|
||||
ctx.registerCommand("serverVersion", commands.serverVersion);
|
||||
ctx.registerCommand("toggleInlayHints", commands.toggleInlayHints);
|
||||
|
||||
// Internal commands which are invoked by the server.
|
||||
ctx.registerCommand('runSingle', commands.runSingle);
|
||||
ctx.registerCommand('debugSingle', commands.debugSingle);
|
||||
ctx.registerCommand('showReferences', commands.showReferences);
|
||||
ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
|
||||
ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
|
||||
ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
|
||||
ctx.registerCommand('gotoLocation', commands.gotoLocation);
|
||||
ctx.registerCommand("runSingle", commands.runSingle);
|
||||
ctx.registerCommand("debugSingle", commands.debugSingle);
|
||||
ctx.registerCommand("showReferences", commands.showReferences);
|
||||
ctx.registerCommand("applySnippetWorkspaceEdit", commands.applySnippetWorkspaceEditCommand);
|
||||
ctx.registerCommand("resolveCodeAction", commands.resolveCodeAction);
|
||||
ctx.registerCommand("applyActionGroup", commands.applyActionGroup);
|
||||
ctx.registerCommand("gotoLocation", commands.gotoLocation);
|
||||
}
|
||||
|
||||
export async function deactivate() {
|
||||
@ -159,12 +168,16 @@ export async function deactivate() {
|
||||
ctx = undefined;
|
||||
}
|
||||
|
||||
async function bootstrap(context: vscode.ExtensionContext, config: Config, state: PersistentState): Promise<string> {
|
||||
async function bootstrap(
|
||||
context: vscode.ExtensionContext,
|
||||
config: Config,
|
||||
state: PersistentState
|
||||
): Promise<string> {
|
||||
const path = await getServer(context, config, state);
|
||||
if (!path) {
|
||||
throw new Error(
|
||||
"Rust Analyzer Language Server is not available. " +
|
||||
"Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation)."
|
||||
"Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation)."
|
||||
);
|
||||
}
|
||||
|
||||
@ -186,7 +199,7 @@ async function patchelf(dest: vscode.Uri): Promise<void> {
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Patching rust-analyzer for NixOS"
|
||||
title: "Patching rust-analyzer for NixOS",
|
||||
},
|
||||
async (progress, _) => {
|
||||
const expression = `
|
||||
@ -207,14 +220,16 @@ async function patchelf(dest: vscode.Uri): Promise<void> {
|
||||
try {
|
||||
progress.report({ message: "Patching executable", increment: 20 });
|
||||
await new Promise((resolve, reject) => {
|
||||
const handle = exec(`nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`,
|
||||
const handle = exec(
|
||||
`nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`,
|
||||
(err, stdout, stderr) => {
|
||||
if (err != null) {
|
||||
reject(Error(stderr));
|
||||
} else {
|
||||
resolve(stdout);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
handle.stdin?.write(expression);
|
||||
handle.stdin?.end();
|
||||
});
|
||||
@ -225,25 +240,35 @@ async function patchelf(dest: vscode.Uri): Promise<void> {
|
||||
);
|
||||
}
|
||||
|
||||
async function getServer(context: vscode.ExtensionContext, config: Config, state: PersistentState): Promise<string | undefined> {
|
||||
async function getServer(
|
||||
context: vscode.ExtensionContext,
|
||||
config: Config,
|
||||
state: PersistentState
|
||||
): Promise<string | undefined> {
|
||||
const explicitPath = serverPath(config);
|
||||
if (explicitPath) {
|
||||
if (explicitPath.startsWith("~/")) {
|
||||
return os.homedir() + explicitPath.slice("~".length);
|
||||
}
|
||||
return explicitPath;
|
||||
};
|
||||
}
|
||||
if (config.package.releaseTag === null) return "rust-analyzer";
|
||||
|
||||
const ext = process.platform === "win32" ? ".exe" : "";
|
||||
const bundled = vscode.Uri.joinPath(context.extensionUri, "server", `rust-analyzer${ext}`);
|
||||
const bundledExists = await vscode.workspace.fs.stat(bundled).then(() => true, () => false);
|
||||
const bundledExists = await vscode.workspace.fs.stat(bundled).then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
if (bundledExists) {
|
||||
let server = bundled;
|
||||
if (await isNixOs()) {
|
||||
await vscode.workspace.fs.createDirectory(config.globalStorageUri).then();
|
||||
const dest = vscode.Uri.joinPath(config.globalStorageUri, `rust-analyzer${ext}`);
|
||||
let exists = await vscode.workspace.fs.stat(dest).then(() => true, () => false);
|
||||
let exists = await vscode.workspace.fs.stat(dest).then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
if (exists && config.package.version !== state.serverVersion) {
|
||||
await vscode.workspace.fs.delete(dest);
|
||||
exists = false;
|
||||
@ -261,11 +286,11 @@ async function getServer(context: vscode.ExtensionContext, config: Config, state
|
||||
await state.updateServerVersion(undefined);
|
||||
await vscode.window.showErrorMessage(
|
||||
"Unfortunately we don't ship binaries for your platform yet. " +
|
||||
"You need to manually clone the rust-analyzer repository and " +
|
||||
"run `cargo xtask install --server` to build the language server from sources. " +
|
||||
"If you feel that your platform should be supported, please create an issue " +
|
||||
"about that [here](https://github.com/rust-analyzer/rust-analyzer/issues) and we " +
|
||||
"will consider it."
|
||||
"You need to manually clone the rust-analyzer repository and " +
|
||||
"run `cargo xtask install --server` to build the language server from sources. " +
|
||||
"If you feel that your platform should be supported, please create an issue " +
|
||||
"about that [here](https://github.com/rust-analyzer/rust-analyzer/issues) and we " +
|
||||
"will consider it."
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
@ -276,8 +301,10 @@ function serverPath(config: Config): string | null {
|
||||
|
||||
async function isNixOs(): Promise<boolean> {
|
||||
try {
|
||||
const contents = (await vscode.workspace.fs.readFile(vscode.Uri.file("/etc/os-release"))).toString();
|
||||
const idString = contents.split('\n').find((a) => a.startsWith("ID=")) || "ID=linux";
|
||||
const contents = (
|
||||
await vscode.workspace.fs.readFile(vscode.Uri.file("/etc/os-release"))
|
||||
).toString();
|
||||
const idString = contents.split("\n").find((a) => a.startsWith("ID=")) || "ID=linux";
|
||||
return idString.indexOf("nixos") !== -1;
|
||||
} catch {
|
||||
return false;
|
||||
@ -286,11 +313,14 @@ async function isNixOs(): Promise<boolean> {
|
||||
|
||||
function warnAboutExtensionConflicts() {
|
||||
if (vscode.extensions.getExtension("rust-lang.rust")) {
|
||||
vscode.window.showWarningMessage(
|
||||
`You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` +
|
||||
"plugins enabled. These are known to conflict and cause various functions of " +
|
||||
"both plugins to not work correctly. You should disable one of them.", "Got it")
|
||||
.then(() => { }, console.error);
|
||||
vscode.window
|
||||
.showWarningMessage(
|
||||
`You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` +
|
||||
"plugins enabled. These are known to conflict and cause various functions of " +
|
||||
"both plugins to not work correctly. You should disable one of them.",
|
||||
"Got it"
|
||||
)
|
||||
.then(() => {}, console.error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,38 +332,38 @@ function warnAboutExtensionConflicts() {
|
||||
*/
|
||||
function configureLanguage(): vscode.Disposable {
|
||||
const indentAction = vscode.IndentAction.None;
|
||||
return vscode.languages.setLanguageConfiguration('rust', {
|
||||
return vscode.languages.setLanguageConfiguration("rust", {
|
||||
onEnterRules: [
|
||||
{
|
||||
// Doc single-line comment
|
||||
// e.g. ///|
|
||||
beforeText: /^\s*\/{3}.*$/,
|
||||
action: { indentAction, appendText: '/// ' },
|
||||
action: { indentAction, appendText: "/// " },
|
||||
},
|
||||
{
|
||||
// Parent doc single-line comment
|
||||
// e.g. //!|
|
||||
beforeText: /^\s*\/{2}\!.*$/,
|
||||
action: { indentAction, appendText: '//! ' },
|
||||
action: { indentAction, appendText: "//! " },
|
||||
},
|
||||
{
|
||||
// Begins an auto-closed multi-line comment (standard or parent doc)
|
||||
// e.g. /** | */ or /*! | */
|
||||
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
|
||||
afterText: /^\s*\*\/$/,
|
||||
action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: ' * ' },
|
||||
action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: " * " },
|
||||
},
|
||||
{
|
||||
// Begins a multi-line comment (standard or parent doc)
|
||||
// e.g. /** ...| or /*! ...|
|
||||
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
|
||||
action: { indentAction, appendText: ' * ' },
|
||||
action: { indentAction, appendText: " * " },
|
||||
},
|
||||
{
|
||||
// Continues a multi-line comment
|
||||
// e.g. * ...|
|
||||
beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
|
||||
action: { indentAction, appendText: '* ' },
|
||||
action: { indentAction, appendText: "* " },
|
||||
},
|
||||
{
|
||||
// Dedents after closing a multi-line comment
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { log } from './util';
|
||||
import * as vscode from "vscode";
|
||||
import { log } from "./util";
|
||||
|
||||
export class PersistentState {
|
||||
constructor(private readonly globalState: vscode.Memento) {
|
||||
|
@ -1,15 +1,22 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as lc from 'vscode-languageclient';
|
||||
import * as ra from './lsp_ext';
|
||||
import * as tasks from './tasks';
|
||||
import * as vscode from "vscode";
|
||||
import * as lc from "vscode-languageclient";
|
||||
import * as ra from "./lsp_ext";
|
||||
import * as tasks from "./tasks";
|
||||
|
||||
import { Ctx } from './ctx';
|
||||
import { makeDebugConfig } from './debug';
|
||||
import { Config, RunnableEnvCfg } from './config';
|
||||
import { Ctx } from "./ctx";
|
||||
import { makeDebugConfig } from "./debug";
|
||||
import { Config, RunnableEnvCfg } from "./config";
|
||||
|
||||
const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }];
|
||||
const quickPickButtons = [
|
||||
{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." },
|
||||
];
|
||||
|
||||
export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> {
|
||||
export async function selectRunnable(
|
||||
ctx: Ctx,
|
||||
prevRunnable?: RunnableQuickPick,
|
||||
debuggeeOnly = false,
|
||||
showButtons: boolean = true
|
||||
): Promise<RunnableQuickPick | undefined> {
|
||||
const editor = ctx.activeRustEditor;
|
||||
const client = ctx.client;
|
||||
if (!editor || !client) return;
|
||||
@ -20,23 +27,18 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
|
||||
|
||||
const runnables = await client.sendRequest(ra.runnables, {
|
||||
textDocument,
|
||||
position: client.code2ProtocolConverter.asPosition(
|
||||
editor.selection.active,
|
||||
),
|
||||
position: client.code2ProtocolConverter.asPosition(editor.selection.active),
|
||||
});
|
||||
const items: RunnableQuickPick[] = [];
|
||||
if (prevRunnable) {
|
||||
items.push(prevRunnable);
|
||||
}
|
||||
for (const r of runnables) {
|
||||
if (
|
||||
prevRunnable &&
|
||||
JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
|
||||
) {
|
||||
if (prevRunnable && JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (debuggeeOnly && (r.label.startsWith('doctest') || r.label.startsWith('cargo'))) {
|
||||
if (debuggeeOnly && (r.label.startsWith("doctest") || r.label.startsWith("cargo"))) {
|
||||
continue;
|
||||
}
|
||||
items.push(new RunnableQuickPick(r));
|
||||
@ -53,7 +55,7 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
const close = (result?: RunnableQuickPick) => {
|
||||
resolve(result);
|
||||
disposables.forEach(d => d.dispose());
|
||||
disposables.forEach((d) => d.dispose());
|
||||
};
|
||||
|
||||
const quickPick = vscode.window.createQuickPick<RunnableQuickPick>();
|
||||
@ -71,7 +73,7 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
|
||||
}),
|
||||
quickPick.onDidChangeActive((active) => {
|
||||
if (showButtons && active.length > 0) {
|
||||
if (active[0].label.startsWith('cargo')) {
|
||||
if (active[0].label.startsWith("cargo")) {
|
||||
// save button makes no sense for `cargo test` or `cargo check`
|
||||
quickPick.buttons = [];
|
||||
} else if (quickPick.buttons.length === 0) {
|
||||
@ -96,8 +98,11 @@ export class RunnableQuickPick implements vscode.QuickPickItem {
|
||||
}
|
||||
}
|
||||
|
||||
export function prepareEnv(runnable: ra.Runnable, runnableEnvCfg: RunnableEnvCfg): Record<string, string> {
|
||||
const env: Record<string, string> = { "RUST_BACKTRACE": "short" };
|
||||
export function prepareEnv(
|
||||
runnable: ra.Runnable,
|
||||
runnableEnvCfg: RunnableEnvCfg
|
||||
): Record<string, string> {
|
||||
const env: Record<string, string> = { RUST_BACKTRACE: "short" };
|
||||
|
||||
if (runnable.args.expectTest) {
|
||||
env["UPDATE_EXPECT"] = "1";
|
||||
@ -141,7 +146,14 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate()
|
||||
const cargoTask = await tasks.buildCargoTask(target, definition, runnable.label, args, config.cargoRunner, true);
|
||||
const cargoTask = await tasks.buildCargoTask(
|
||||
target,
|
||||
definition,
|
||||
runnable.label,
|
||||
args,
|
||||
config.cargoRunner,
|
||||
true
|
||||
);
|
||||
|
||||
cargoTask.presentationOptions.clear = true;
|
||||
// Sadly, this doesn't prevent focus stealing if the terminal is currently
|
||||
@ -157,7 +169,7 @@ export function createArgs(runnable: ra.Runnable): string[] {
|
||||
args.push(...runnable.args.cargoExtraArgs); // Append user-specified cargo options.
|
||||
}
|
||||
if (runnable.args.executableArgs.length > 0) {
|
||||
args.push('--', ...runnable.args.executableArgs);
|
||||
args.push("--", ...runnable.args.executableArgs);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import { assert } from './util';
|
||||
import { assert } from "./util";
|
||||
|
||||
export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
|
||||
if (edit.entries().length === 1) {
|
||||
@ -11,12 +11,16 @@ export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
|
||||
}
|
||||
for (const [uri, edits] of edit.entries()) {
|
||||
const editor = await editorFromUri(uri);
|
||||
if (editor) await editor.edit((builder) => {
|
||||
for (const indel of edits) {
|
||||
assert(!parseSnippet(indel.newText), `bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`);
|
||||
builder.replace(indel.range, indel.newText);
|
||||
}
|
||||
});
|
||||
if (editor)
|
||||
await editor.edit((builder) => {
|
||||
for (const indel of edits) {
|
||||
assert(
|
||||
!parseSnippet(indel.newText),
|
||||
`bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`
|
||||
);
|
||||
builder.replace(indel.range, indel.newText);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +29,9 @@ async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undef
|
||||
// `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed
|
||||
await vscode.window.showTextDocument(uri, {});
|
||||
}
|
||||
return vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
|
||||
return vscode.window.visibleTextEditors.find(
|
||||
(it) => it.document.uri.toString() === uri.toString()
|
||||
);
|
||||
}
|
||||
|
||||
export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {
|
||||
@ -37,22 +43,26 @@ export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vs
|
||||
if (parsed) {
|
||||
const [newText, [placeholderStart, placeholderLength]] = parsed;
|
||||
const prefix = newText.substr(0, placeholderStart);
|
||||
const lastNewline = prefix.lastIndexOf('\n');
|
||||
const lastNewline = prefix.lastIndexOf("\n");
|
||||
|
||||
const startLine = indel.range.start.line + lineDelta + countLines(prefix);
|
||||
const startColumn = lastNewline === -1 ?
|
||||
indel.range.start.character + placeholderStart
|
||||
: prefix.length - lastNewline - 1;
|
||||
const startColumn =
|
||||
lastNewline === -1
|
||||
? indel.range.start.character + placeholderStart
|
||||
: prefix.length - lastNewline - 1;
|
||||
const endColumn = startColumn + placeholderLength;
|
||||
selections.push(new vscode.Selection(
|
||||
new vscode.Position(startLine, startColumn),
|
||||
new vscode.Position(startLine, endColumn),
|
||||
));
|
||||
selections.push(
|
||||
new vscode.Selection(
|
||||
new vscode.Position(startLine, startColumn),
|
||||
new vscode.Position(startLine, endColumn)
|
||||
)
|
||||
);
|
||||
builder.replace(indel.range, newText);
|
||||
} else {
|
||||
builder.replace(indel.range, indel.newText);
|
||||
}
|
||||
lineDelta += countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
|
||||
lineDelta +=
|
||||
countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
|
||||
}
|
||||
});
|
||||
if (selections.length > 0) editor.selections = selections;
|
||||
@ -65,8 +75,7 @@ function parseSnippet(snip: string): [string, [number, number]] | undefined {
|
||||
const m = snip.match(/\$(0|\{0:([^}]*)\})/);
|
||||
if (!m) return undefined;
|
||||
const placeholder = m[2] ?? "";
|
||||
if (m.index == null)
|
||||
return undefined;
|
||||
if (m.index == null) return undefined;
|
||||
const range: [number, number] = [m.index, placeholder.length];
|
||||
const insert = snip.replace(m[0], placeholder);
|
||||
return [insert, range];
|
||||
|
@ -1,12 +1,12 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as vscode from "vscode";
|
||||
import * as toolchain from "./toolchain";
|
||||
import { Config } from './config';
|
||||
import { log } from './util';
|
||||
import { Config } from "./config";
|
||||
import { log } from "./util";
|
||||
|
||||
// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
|
||||
// our configuration should be compatible with it so use the same key.
|
||||
export const TASK_TYPE = 'cargo';
|
||||
export const TASK_SOURCE = 'rust';
|
||||
export const TASK_TYPE = "cargo";
|
||||
export const TASK_SOURCE = "rust";
|
||||
|
||||
export interface CargoTaskDefinition extends vscode.TaskDefinition {
|
||||
command?: string;
|
||||
@ -30,17 +30,23 @@ class CargoTaskProvider implements vscode.TaskProvider {
|
||||
// tasks.json - only tweaked.
|
||||
|
||||
const defs = [
|
||||
{ command: 'build', group: vscode.TaskGroup.Build },
|
||||
{ command: 'check', group: vscode.TaskGroup.Build },
|
||||
{ command: 'test', group: vscode.TaskGroup.Test },
|
||||
{ command: 'clean', group: vscode.TaskGroup.Clean },
|
||||
{ command: 'run', group: undefined },
|
||||
{ command: "build", group: vscode.TaskGroup.Build },
|
||||
{ command: "check", group: vscode.TaskGroup.Build },
|
||||
{ command: "test", group: vscode.TaskGroup.Test },
|
||||
{ command: "clean", group: vscode.TaskGroup.Clean },
|
||||
{ command: "run", group: undefined },
|
||||
];
|
||||
|
||||
const tasks: vscode.Task[] = [];
|
||||
for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
|
||||
for (const def of defs) {
|
||||
const vscodeTask = await buildCargoTask(workspaceTarget, { type: TASK_TYPE, command: def.command }, `cargo ${def.command}`, [def.command], this.config.cargoRunner);
|
||||
const vscodeTask = await buildCargoTask(
|
||||
workspaceTarget,
|
||||
{ type: TASK_TYPE, command: def.command },
|
||||
`cargo ${def.command}`,
|
||||
[def.command],
|
||||
this.config.cargoRunner
|
||||
);
|
||||
vscodeTask.group = def.group;
|
||||
tasks.push(vscodeTask);
|
||||
}
|
||||
@ -58,7 +64,13 @@ class CargoTaskProvider implements vscode.TaskProvider {
|
||||
|
||||
if (definition.type === TASK_TYPE && definition.command) {
|
||||
const args = [definition.command].concat(definition.args ?? []);
|
||||
return await buildCargoTask(task.scope, definition, task.name, args, this.config.cargoRunner);
|
||||
return await buildCargoTask(
|
||||
task.scope,
|
||||
definition,
|
||||
task.name,
|
||||
args,
|
||||
this.config.cargoRunner
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@ -73,7 +85,6 @@ export async function buildCargoTask(
|
||||
customRunner?: string,
|
||||
throwOnError: boolean = false
|
||||
): Promise<vscode.Task> {
|
||||
|
||||
let exec: vscode.ProcessExecution | vscode.ShellExecution | undefined = undefined;
|
||||
|
||||
if (customRunner) {
|
||||
@ -90,7 +101,6 @@ export async function buildCargoTask(
|
||||
}
|
||||
}
|
||||
// fallback to default processing
|
||||
|
||||
} catch (e) {
|
||||
if (throwOnError) throw `Cargo runner '${customRunner}' failed! ${e}`;
|
||||
// fallback to default processing
|
||||
@ -117,7 +127,7 @@ export async function buildCargoTask(
|
||||
name,
|
||||
TASK_SOURCE,
|
||||
exec,
|
||||
['$rustc']
|
||||
["$rustc"]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as cp from 'child_process';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as readline from 'readline';
|
||||
import * as vscode from 'vscode';
|
||||
import { execute, log, memoizeAsync } from './util';
|
||||
import * as cp from "child_process";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import * as readline from "readline";
|
||||
import * as vscode from "vscode";
|
||||
import { execute, log, memoizeAsync } from "./util";
|
||||
|
||||
interface CompilationArtifact {
|
||||
fileName: string;
|
||||
@ -18,7 +18,7 @@ export interface ArtifactSpec {
|
||||
}
|
||||
|
||||
export class Cargo {
|
||||
constructor(readonly rootFolder: string, readonly output: vscode.OutputChannel) { }
|
||||
constructor(readonly rootFolder: string, readonly output: vscode.OutputChannel) {}
|
||||
|
||||
// Made public for testing purposes
|
||||
static artifactSpec(args: readonly string[]): ArtifactSpec {
|
||||
@ -27,7 +27,9 @@ export class Cargo {
|
||||
// arguments for a runnable from the quick pick should be updated.
|
||||
// see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
|
||||
switch (cargoArgs[0]) {
|
||||
case "run": cargoArgs[0] = "build"; break;
|
||||
case "run":
|
||||
cargoArgs[0] = "build";
|
||||
break;
|
||||
case "test": {
|
||||
if (!cargoArgs.includes("--no-run")) {
|
||||
cargoArgs.push("--no-run");
|
||||
@ -40,7 +42,7 @@ export class Cargo {
|
||||
if (cargoArgs[0] === "test") {
|
||||
// for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
|
||||
// produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
|
||||
result.filter = (artifacts) => artifacts.filter(it => it.isTest);
|
||||
result.filter = (artifacts) => artifacts.filter((it) => it.isTest);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -50,24 +52,25 @@ export class Cargo {
|
||||
const artifacts: CompilationArtifact[] = [];
|
||||
|
||||
try {
|
||||
await this.runCargo(spec.cargoArgs,
|
||||
message => {
|
||||
if (message.reason === 'compiler-artifact' && message.executable) {
|
||||
const isBinary = message.target.crate_types.includes('bin');
|
||||
const isBuildScript = message.target.kind.includes('custom-build');
|
||||
await this.runCargo(
|
||||
spec.cargoArgs,
|
||||
(message) => {
|
||||
if (message.reason === "compiler-artifact" && message.executable) {
|
||||
const isBinary = message.target.crate_types.includes("bin");
|
||||
const isBuildScript = message.target.kind.includes("custom-build");
|
||||
if ((isBinary && !isBuildScript) || message.profile.test) {
|
||||
artifacts.push({
|
||||
fileName: message.executable,
|
||||
name: message.target.name,
|
||||
kind: message.target.kind[0],
|
||||
isTest: message.profile.test
|
||||
isTest: message.profile.test,
|
||||
});
|
||||
}
|
||||
} else if (message.reason === 'compiler-message') {
|
||||
} else if (message.reason === "compiler-message") {
|
||||
this.output.append(message.message.rendered);
|
||||
}
|
||||
},
|
||||
stderr => this.output.append(stderr),
|
||||
(stderr) => this.output.append(stderr)
|
||||
);
|
||||
} catch (err) {
|
||||
this.output.show(true);
|
||||
@ -81,9 +84,9 @@ export class Cargo {
|
||||
const artifacts = await this.getArtifacts(Cargo.artifactSpec(args));
|
||||
|
||||
if (artifacts.length === 0) {
|
||||
throw new Error('No compilation artifacts');
|
||||
throw new Error("No compilation artifacts");
|
||||
} else if (artifacts.length > 1) {
|
||||
throw new Error('Multiple compilation artifacts are not supported.');
|
||||
throw new Error("Multiple compilation artifacts are not supported.");
|
||||
}
|
||||
|
||||
return artifacts[0].fileName;
|
||||
@ -97,25 +100,23 @@ export class Cargo {
|
||||
const path = await cargoPath();
|
||||
return await new Promise((resolve, reject) => {
|
||||
const cargo = cp.spawn(path, cargoArgs, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
cwd: this.rootFolder
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
cwd: this.rootFolder,
|
||||
});
|
||||
|
||||
cargo.on('error', err => reject(new Error(`could not launch cargo: ${err}`)));
|
||||
cargo.on("error", (err) => reject(new Error(`could not launch cargo: ${err}`)));
|
||||
|
||||
cargo.stderr.on('data', chunk => onStderrString(chunk.toString()));
|
||||
cargo.stderr.on("data", (chunk) => onStderrString(chunk.toString()));
|
||||
|
||||
const rl = readline.createInterface({ input: cargo.stdout });
|
||||
rl.on('line', line => {
|
||||
rl.on("line", (line) => {
|
||||
const message = JSON.parse(line);
|
||||
onStdoutJson(message);
|
||||
});
|
||||
|
||||
cargo.on('exit', (exitCode, _) => {
|
||||
if (exitCode === 0)
|
||||
resolve(exitCode);
|
||||
else
|
||||
reject(new Error(`exit code: ${exitCode}.`));
|
||||
cargo.on("exit", (exitCode, _) => {
|
||||
if (exitCode === 0) resolve(exitCode);
|
||||
else reject(new Error(`exit code: ${exitCode}.`));
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -158,7 +159,12 @@ export const getPathForExecutable = memoizeAsync(
|
||||
try {
|
||||
// hmm, `os.homedir()` seems to be infallible
|
||||
// it is not mentioned in docs and cannot be infered by the type signature...
|
||||
const standardPath = vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".cargo", "bin", executableName);
|
||||
const standardPath = vscode.Uri.joinPath(
|
||||
vscode.Uri.file(os.homedir()),
|
||||
".cargo",
|
||||
"bin",
|
||||
executableName
|
||||
);
|
||||
|
||||
if (await isFileAtUri(standardPath)) return standardPath.fsPath;
|
||||
} catch (err) {
|
||||
@ -169,13 +175,11 @@ export const getPathForExecutable = memoizeAsync(
|
||||
);
|
||||
|
||||
async function lookupInPath(exec: string): Promise<boolean> {
|
||||
const paths = process.env.PATH ?? "";;
|
||||
const paths = process.env.PATH ?? "";
|
||||
|
||||
const candidates = paths.split(path.delimiter).flatMap(dirInPath => {
|
||||
const candidates = paths.split(path.delimiter).flatMap((dirInPath) => {
|
||||
const candidate = path.join(dirInPath, exec);
|
||||
return os.type() === "Windows_NT"
|
||||
? [candidate, `${candidate}.exe`]
|
||||
: [candidate];
|
||||
return os.type() === "Windows_NT" ? [candidate, `${candidate}.exe`] : [candidate];
|
||||
});
|
||||
|
||||
for await (const isFile of candidates.map(isFileAtPath)) {
|
||||
|
@ -13,7 +13,7 @@ export function assert(condition: boolean, explanation: string): asserts conditi
|
||||
}
|
||||
}
|
||||
|
||||
export const log = new class {
|
||||
export const log = new (class {
|
||||
private enabled = true;
|
||||
private readonly output = vscode.window.createOutputChannel("Rust Analyzer Client");
|
||||
|
||||
@ -55,21 +55,20 @@ export const log = new class {
|
||||
depth: 6, // heuristic
|
||||
});
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
export async function sendRequestWithRetry<TParam, TRet>(
|
||||
client: lc.LanguageClient,
|
||||
reqType: lc.RequestType<TParam, TRet, unknown>,
|
||||
param: TParam,
|
||||
token?: vscode.CancellationToken,
|
||||
token?: vscode.CancellationToken
|
||||
): Promise<TRet> {
|
||||
// The sequence is `10 * (2 ** (2 * n))` where n is 1, 2, 3...
|
||||
for (const delay of [40, 160, 640, 2560, 10240, null]) {
|
||||
try {
|
||||
return await (token
|
||||
? client.sendRequest(reqType, param, token)
|
||||
: client.sendRequest(reqType, param)
|
||||
);
|
||||
: client.sendRequest(reqType, param));
|
||||
} catch (error) {
|
||||
if (delay === null) {
|
||||
log.warn("LSP request timed out", { method: reqType.method, param, error });
|
||||
@ -86,11 +85,11 @@ export async function sendRequestWithRetry<TParam, TRet>(
|
||||
await sleep(delay);
|
||||
}
|
||||
}
|
||||
throw 'unreachable';
|
||||
throw "unreachable";
|
||||
}
|
||||
|
||||
export function sleep(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export type RustDocument = vscode.TextDocument & { languageId: "rust" };
|
||||
@ -101,12 +100,12 @@ export function isRustDocument(document: vscode.TextDocument): document is RustD
|
||||
// by allowing only `file` schemes
|
||||
// unfortunately extensions that use diff views not always set this
|
||||
// to something different than 'file' (see ongoing bug: #4608)
|
||||
return document.languageId === 'rust' && document.uri.scheme === 'file';
|
||||
return document.languageId === "rust" && document.uri.scheme === "file";
|
||||
}
|
||||
|
||||
export function isCargoTomlDocument(document: vscode.TextDocument): document is RustDocument {
|
||||
// ideally `document.languageId` should be 'toml' but user maybe not have toml extension installed
|
||||
return document.uri.scheme === 'file' && document.fileName.endsWith('Cargo.toml');
|
||||
return document.uri.scheme === "file" && document.fileName.endsWith("Cargo.toml");
|
||||
}
|
||||
|
||||
export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
|
||||
@ -116,9 +115,9 @@ export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
|
||||
export function isValidExecutable(path: string): boolean {
|
||||
log.debug("Checking availability of a binary at", path);
|
||||
|
||||
const res = spawnSync(path, ["--version"], { encoding: 'utf8' });
|
||||
const res = spawnSync(path, ["--version"], { encoding: "utf8" });
|
||||
|
||||
const printOutput = res.error && (res.error as any).code !== 'ENOENT' ? log.warn : log.debug;
|
||||
const printOutput = res.error && (res.error as any).code !== "ENOENT" ? log.warn : log.debug;
|
||||
printOutput(path, "--version:", res);
|
||||
|
||||
return res.status === 0;
|
||||
@ -126,17 +125,19 @@ export function isValidExecutable(path: string): boolean {
|
||||
|
||||
/** Sets ['when'](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts) clause contexts */
|
||||
export function setContextValue(key: string, value: any): Thenable<void> {
|
||||
return vscode.commands.executeCommand('setContext', key, value);
|
||||
return vscode.commands.executeCommand("setContext", key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a higher-order function that caches the results of invoking the
|
||||
* underlying async function.
|
||||
*/
|
||||
export function memoizeAsync<Ret, TThis, Param extends string>(func: (this: TThis, arg: Param) => Promise<Ret>) {
|
||||
export function memoizeAsync<Ret, TThis, Param extends string>(
|
||||
func: (this: TThis, arg: Param) => Promise<Ret>
|
||||
) {
|
||||
const cache = new Map<string, Ret>();
|
||||
|
||||
return async function(this: TThis, arg: Param) {
|
||||
return async function (this: TThis, arg: Param) {
|
||||
const cached = cache.get(arg);
|
||||
if (cached) return cached;
|
||||
|
||||
|
@ -1,43 +1,43 @@
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
|
||||
import { runTests } from '@vscode/test-electron';
|
||||
import { runTests } from "@vscode/test-electron";
|
||||
|
||||
async function main() {
|
||||
// The folder containing the Extension Manifest package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, "../../");
|
||||
|
||||
// Minimum supported version.
|
||||
const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, 'package.json'));
|
||||
const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, "package.json"));
|
||||
const json = JSON.parse(jsonData.toString());
|
||||
let minimalVersion: string = json.engines.vscode;
|
||||
if (minimalVersion.startsWith('^')) minimalVersion = minimalVersion.slice(1);
|
||||
if (minimalVersion.startsWith("^")) minimalVersion = minimalVersion.slice(1);
|
||||
|
||||
const launchArgs = ["--disable-extensions", extensionDevelopmentPath];
|
||||
|
||||
// All test suites (either unit tests or integration tests) should be in subfolders.
|
||||
const extensionTestsPath = path.resolve(__dirname, './unit/index');
|
||||
const extensionTestsPath = path.resolve(__dirname, "./unit/index");
|
||||
|
||||
// Run tests using the minimal supported version.
|
||||
await runTests({
|
||||
version: minimalVersion,
|
||||
launchArgs,
|
||||
extensionDevelopmentPath,
|
||||
extensionTestsPath
|
||||
extensionTestsPath,
|
||||
});
|
||||
|
||||
// and the latest one
|
||||
await runTests({
|
||||
version: 'stable',
|
||||
version: "stable",
|
||||
launchArgs,
|
||||
extensionDevelopmentPath,
|
||||
extensionTestsPath
|
||||
extensionTestsPath,
|
||||
});
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
main().catch((err) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to run tests', err);
|
||||
console.error("Failed to run tests", err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { readdir } from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import { readdir } from "fs/promises";
|
||||
import * as path from "path";
|
||||
|
||||
class Test {
|
||||
readonly name: string;
|
||||
@ -59,7 +59,9 @@ export class Context {
|
||||
export async function run(): Promise<void> {
|
||||
const context = new Context();
|
||||
|
||||
const testFiles = (await readdir(path.resolve(__dirname))).filter(name => name.endsWith('.test.js'));
|
||||
const testFiles = (await readdir(path.resolve(__dirname))).filter((name) =>
|
||||
name.endsWith(".test.js")
|
||||
);
|
||||
for (const testFile of testFiles) {
|
||||
try {
|
||||
const testModule = require(path.resolve(__dirname, testFile));
|
||||
|
@ -1,51 +1,98 @@
|
||||
import * as assert from 'assert';
|
||||
import { Cargo } from '../../src/toolchain';
|
||||
import { Context } from '.';
|
||||
import * as assert from "assert";
|
||||
import { Cargo } from "../../src/toolchain";
|
||||
import { Context } from ".";
|
||||
|
||||
export async function getTests(ctx: Context) {
|
||||
await ctx.suite('Launch configuration/Lens', suite => {
|
||||
suite.addTest('A binary', async () => {
|
||||
const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]);
|
||||
await ctx.suite("Launch configuration/Lens", (suite) => {
|
||||
suite.addTest("A binary", async () => {
|
||||
const args = Cargo.artifactSpec([
|
||||
"build",
|
||||
"--package",
|
||||
"pkg_name",
|
||||
"--bin",
|
||||
"pkg_name",
|
||||
]);
|
||||
|
||||
assert.deepStrictEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
|
||||
assert.deepStrictEqual(args.cargoArgs, [
|
||||
"build",
|
||||
"--package",
|
||||
"pkg_name",
|
||||
"--bin",
|
||||
"pkg_name",
|
||||
"--message-format=json",
|
||||
]);
|
||||
assert.deepStrictEqual(args.filter, undefined);
|
||||
});
|
||||
|
||||
suite.addTest('One of Multiple Binaries', async () => {
|
||||
suite.addTest("One of Multiple Binaries", async () => {
|
||||
const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
|
||||
|
||||
assert.deepStrictEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]);
|
||||
assert.deepStrictEqual(args.cargoArgs, [
|
||||
"build",
|
||||
"--package",
|
||||
"pkg_name",
|
||||
"--bin",
|
||||
"bin1",
|
||||
"--message-format=json",
|
||||
]);
|
||||
assert.deepStrictEqual(args.filter, undefined);
|
||||
});
|
||||
|
||||
suite.addTest('A test', async () => {
|
||||
suite.addTest("A test", async () => {
|
||||
const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
|
||||
|
||||
assert.deepStrictEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]);
|
||||
assert.deepStrictEqual(args.cargoArgs, [
|
||||
"test",
|
||||
"--package",
|
||||
"pkg_name",
|
||||
"--lib",
|
||||
"--no-run",
|
||||
"--message-format=json",
|
||||
]);
|
||||
assert.notDeepStrictEqual(args.filter, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
await ctx.suite('Launch configuration/QuickPick', suite => {
|
||||
suite.addTest('A binary', async () => {
|
||||
await ctx.suite("Launch configuration/QuickPick", (suite) => {
|
||||
suite.addTest("A binary", async () => {
|
||||
const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
|
||||
|
||||
assert.deepStrictEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
|
||||
assert.deepStrictEqual(args.cargoArgs, [
|
||||
"build",
|
||||
"--package",
|
||||
"pkg_name",
|
||||
"--bin",
|
||||
"pkg_name",
|
||||
"--message-format=json",
|
||||
]);
|
||||
assert.deepStrictEqual(args.filter, undefined);
|
||||
});
|
||||
|
||||
|
||||
suite.addTest('One of Multiple Binaries', async () => {
|
||||
suite.addTest("One of Multiple Binaries", async () => {
|
||||
const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
|
||||
|
||||
assert.deepStrictEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]);
|
||||
assert.deepStrictEqual(args.cargoArgs, [
|
||||
"build",
|
||||
"--package",
|
||||
"pkg_name",
|
||||
"--bin",
|
||||
"bin2",
|
||||
"--message-format=json",
|
||||
]);
|
||||
assert.deepStrictEqual(args.filter, undefined);
|
||||
});
|
||||
|
||||
suite.addTest('A test', async () => {
|
||||
suite.addTest("A test", async () => {
|
||||
const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
|
||||
|
||||
assert.deepStrictEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]);
|
||||
assert.deepStrictEqual(args.cargoArgs, [
|
||||
"test",
|
||||
"--package",
|
||||
"pkg_name",
|
||||
"--lib",
|
||||
"--message-format=json",
|
||||
"--no-run",
|
||||
]);
|
||||
assert.notDeepStrictEqual(args.filter, undefined);
|
||||
});
|
||||
});
|
||||
|
@ -1,8 +1,8 @@
|
||||
import * as assert from 'assert';
|
||||
import { prepareEnv } from '../../src/run';
|
||||
import { RunnableEnvCfg } from '../../src/config';
|
||||
import { Context } from '.';
|
||||
import * as ra from '../../src/lsp_ext';
|
||||
import * as assert from "assert";
|
||||
import { prepareEnv } from "../../src/run";
|
||||
import { RunnableEnvCfg } from "../../src/config";
|
||||
import { Context } from ".";
|
||||
import * as ra from "../../src/lsp_ext";
|
||||
|
||||
function makeRunnable(label: string): ra.Runnable {
|
||||
return {
|
||||
@ -11,8 +11,8 @@ function makeRunnable(label: string): ra.Runnable {
|
||||
args: {
|
||||
cargoArgs: [],
|
||||
executableArgs: [],
|
||||
cargoExtraArgs: []
|
||||
}
|
||||
cargoExtraArgs: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -22,20 +22,20 @@ function fakePrepareEnv(runnableName: string, config: RunnableEnvCfg): Record<st
|
||||
}
|
||||
|
||||
export async function getTests(ctx: Context) {
|
||||
await ctx.suite('Runnable env', suite => {
|
||||
suite.addTest('Global config works', async () => {
|
||||
const binEnv = fakePrepareEnv("run project_name", { "GLOBAL": "g" });
|
||||
await ctx.suite("Runnable env", (suite) => {
|
||||
suite.addTest("Global config works", async () => {
|
||||
const binEnv = fakePrepareEnv("run project_name", { GLOBAL: "g" });
|
||||
assert.strictEqual(binEnv["GLOBAL"], "g");
|
||||
|
||||
const testEnv = fakePrepareEnv("test some::mod::test_name", { "GLOBAL": "g" });
|
||||
const testEnv = fakePrepareEnv("test some::mod::test_name", { GLOBAL: "g" });
|
||||
assert.strictEqual(testEnv["GLOBAL"], "g");
|
||||
});
|
||||
|
||||
suite.addTest('null mask works', async () => {
|
||||
suite.addTest("null mask works", async () => {
|
||||
const config = [
|
||||
{
|
||||
env: { DATA: "data" }
|
||||
}
|
||||
env: { DATA: "data" },
|
||||
},
|
||||
];
|
||||
const binEnv = fakePrepareEnv("run project_name", config);
|
||||
assert.strictEqual(binEnv["DATA"], "data");
|
||||
@ -44,14 +44,14 @@ export async function getTests(ctx: Context) {
|
||||
assert.strictEqual(testEnv["DATA"], "data");
|
||||
});
|
||||
|
||||
suite.addTest('order works', async () => {
|
||||
suite.addTest("order works", async () => {
|
||||
const config = [
|
||||
{
|
||||
env: { DATA: "data" }
|
||||
env: { DATA: "data" },
|
||||
},
|
||||
{
|
||||
env: { DATA: "newdata" }
|
||||
}
|
||||
env: { DATA: "newdata" },
|
||||
},
|
||||
];
|
||||
const binEnv = fakePrepareEnv("run project_name", config);
|
||||
assert.strictEqual(binEnv["DATA"], "newdata");
|
||||
@ -60,19 +60,19 @@ export async function getTests(ctx: Context) {
|
||||
assert.strictEqual(testEnv["DATA"], "newdata");
|
||||
});
|
||||
|
||||
suite.addTest('mask works', async () => {
|
||||
suite.addTest("mask works", async () => {
|
||||
const config = [
|
||||
{
|
||||
env: { DATA: "data" }
|
||||
env: { DATA: "data" },
|
||||
},
|
||||
{
|
||||
mask: "^run",
|
||||
env: { DATA: "rundata" }
|
||||
env: { DATA: "rundata" },
|
||||
},
|
||||
{
|
||||
mask: "special_test$",
|
||||
env: { DATA: "special_test" }
|
||||
}
|
||||
env: { DATA: "special_test" },
|
||||
},
|
||||
];
|
||||
const binEnv = fakePrepareEnv("run project_name", config);
|
||||
assert.strictEqual(binEnv["DATA"], "rundata");
|
||||
@ -84,15 +84,15 @@ export async function getTests(ctx: Context) {
|
||||
assert.strictEqual(specialTestEnv["DATA"], "special_test");
|
||||
});
|
||||
|
||||
suite.addTest('exact test name works', async () => {
|
||||
suite.addTest("exact test name works", async () => {
|
||||
const config = [
|
||||
{
|
||||
env: { DATA: "data" }
|
||||
env: { DATA: "data" },
|
||||
},
|
||||
{
|
||||
mask: "some::mod::test_name",
|
||||
env: { DATA: "test special" }
|
||||
}
|
||||
env: { DATA: "test special" },
|
||||
},
|
||||
];
|
||||
const testEnv = fakePrepareEnv("test some::mod::test_name", config);
|
||||
assert.strictEqual(testEnv["DATA"], "test special");
|
||||
@ -101,15 +101,15 @@ export async function getTests(ctx: Context) {
|
||||
assert.strictEqual(specialTestEnv["DATA"], "data");
|
||||
});
|
||||
|
||||
suite.addTest('test mod name works', async () => {
|
||||
suite.addTest("test mod name works", async () => {
|
||||
const config = [
|
||||
{
|
||||
env: { DATA: "data" }
|
||||
env: { DATA: "data" },
|
||||
},
|
||||
{
|
||||
mask: "some::mod",
|
||||
env: { DATA: "mod special" }
|
||||
}
|
||||
env: { DATA: "mod special" },
|
||||
},
|
||||
];
|
||||
const testEnv = fakePrepareEnv("test some::mod::test_name", config);
|
||||
assert.strictEqual(testEnv["DATA"], "mod special");
|
||||
|
@ -1,30 +1,30 @@
|
||||
import * as assert from 'assert';
|
||||
import { Context } from '.';
|
||||
import { substituteVariablesInEnv } from '../../src/config';
|
||||
import * as assert from "assert";
|
||||
import { Context } from ".";
|
||||
import { substituteVariablesInEnv } from "../../src/config";
|
||||
|
||||
export async function getTests(ctx: Context) {
|
||||
await ctx.suite('Server Env Settings', suite => {
|
||||
suite.addTest('Replacing Env Variables', async () => {
|
||||
await ctx.suite("Server Env Settings", (suite) => {
|
||||
suite.addTest("Replacing Env Variables", async () => {
|
||||
const envJson = {
|
||||
USING_MY_VAR: "${env:MY_VAR} test ${env:MY_VAR}",
|
||||
MY_VAR: "test"
|
||||
MY_VAR: "test",
|
||||
};
|
||||
const expectedEnv = {
|
||||
USING_MY_VAR: "test test test",
|
||||
MY_VAR: "test"
|
||||
MY_VAR: "test",
|
||||
};
|
||||
const actualEnv = await substituteVariablesInEnv(envJson);
|
||||
assert.deepStrictEqual(actualEnv, expectedEnv);
|
||||
});
|
||||
|
||||
suite.addTest('Circular dependencies remain as is', async () => {
|
||||
suite.addTest("Circular dependencies remain as is", async () => {
|
||||
const envJson = {
|
||||
A_USES_B: "${env:B_USES_A}",
|
||||
B_USES_A: "${env:A_USES_B}",
|
||||
C_USES_ITSELF: "${env:C_USES_ITSELF}",
|
||||
D_USES_C: "${env:C_USES_ITSELF}",
|
||||
E_IS_ISOLATED: "test",
|
||||
F_USES_E: "${env:E_IS_ISOLATED}"
|
||||
F_USES_E: "${env:E_IS_ISOLATED}",
|
||||
};
|
||||
const expectedEnv = {
|
||||
A_USES_B: "${env:B_USES_A}",
|
||||
@ -32,30 +32,30 @@ export async function getTests(ctx: Context) {
|
||||
C_USES_ITSELF: "${env:C_USES_ITSELF}",
|
||||
D_USES_C: "${env:C_USES_ITSELF}",
|
||||
E_IS_ISOLATED: "test",
|
||||
F_USES_E: "test"
|
||||
F_USES_E: "test",
|
||||
};
|
||||
const actualEnv = await substituteVariablesInEnv(envJson);
|
||||
assert.deepStrictEqual(actualEnv, expectedEnv);
|
||||
});
|
||||
|
||||
suite.addTest('Should support external variables', async () => {
|
||||
suite.addTest("Should support external variables", async () => {
|
||||
const envJson = {
|
||||
USING_EXTERNAL_VAR: "${env:TEST_VARIABLE} test ${env:TEST_VARIABLE}"
|
||||
USING_EXTERNAL_VAR: "${env:TEST_VARIABLE} test ${env:TEST_VARIABLE}",
|
||||
};
|
||||
const expectedEnv = {
|
||||
USING_EXTERNAL_VAR: "test test test"
|
||||
USING_EXTERNAL_VAR: "test test test",
|
||||
};
|
||||
|
||||
const actualEnv = await substituteVariablesInEnv(envJson);
|
||||
assert.deepStrictEqual(actualEnv, expectedEnv);
|
||||
});
|
||||
|
||||
suite.addTest('should support VSCode variables', async () => {
|
||||
suite.addTest("should support VSCode variables", async () => {
|
||||
const envJson = {
|
||||
USING_VSCODE_VAR: "${workspaceFolderBasename}"
|
||||
USING_VSCODE_VAR: "${workspaceFolderBasename}",
|
||||
};
|
||||
const actualEnv = await substituteVariablesInEnv(envJson);
|
||||
assert.deepStrictEqual(actualEnv.USING_VSCODE_VAR, 'code');
|
||||
assert.deepStrictEqual(actualEnv.USING_VSCODE_VAR, "code");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
// Special typescript project file, used by eslint only.
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
// repeated from base config's "include" setting
|
||||
"src",
|
||||
"tests",
|
||||
// these are the eslint-only inclusions
|
||||
".eslintrc.js",
|
||||
]
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
// repeated from base config's "include" setting
|
||||
"src",
|
||||
"tests",
|
||||
// these are the eslint-only inclusions
|
||||
".eslintrc.js"
|
||||
]
|
||||
}
|
||||
|
@ -3,9 +3,7 @@
|
||||
"module": "commonjs",
|
||||
"target": "es2021",
|
||||
"outDir": "out",
|
||||
"lib": [
|
||||
"es2021"
|
||||
],
|
||||
"lib": ["es2021"],
|
||||
"sourceMap": true,
|
||||
"rootDir": ".",
|
||||
"strict": true,
|
||||
@ -16,12 +14,6 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"newLine": "LF"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test"
|
||||
],
|
||||
"include": [
|
||||
"src",
|
||||
"tests"
|
||||
]
|
||||
"exclude": ["node_modules", ".vscode-test"],
|
||||
"include": ["src", "tests"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user