diff --git a/editors/code/.eslintrc.js b/editors/code/.eslintrc.js index 518c5d0755..684b5c6bd9 100644 --- a/editors/code/.eslintrc.js +++ b/editors/code/.eslintrc.js @@ -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", + }, }; diff --git a/editors/code/language-configuration.json b/editors/code/language-configuration.json index cf7b20eb79..b1ee0843e3 100644 --- a/editors/code/language-configuration.json +++ b/editors/code/language-configuration.json @@ -1,7 +1,7 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": ["/*", "*/"] }, "brackets": [ ["{", "}"], @@ -9,10 +9,10 @@ ["(", ")"] ], "colorizedBracketPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] - ], + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], "autoClosingPairs": [ { "open": "{", "close": "}" }, { "open": "[", "close": "]" }, diff --git a/editors/code/ra_syntax_tree.tmGrammar.json b/editors/code/ra_syntax_tree.tmGrammar.json index 431d414f64..279e7bafa0 100644 --- a/editors/code/ra_syntax_tree.tmGrammar.json +++ b/editors/code/ra_syntax_tree.tmGrammar.json @@ -25,7 +25,5 @@ "name": "string" } }, - "fileTypes": [ - "rast" - ] + "fileTypes": ["rast"] } diff --git a/editors/code/src/ast_inspector.ts b/editors/code/src/ast_inspector.ts index 28d8b2a5fd..fca992299e 100644 --- a/editors/code/src/ast_inspector.ts +++ b/editors/code/src/ast_inspector.ts @@ -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 { - if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) return; + provideDefinition( + doc: vscode.TextDocument, + pos: vscode.Position + ): vscode.ProviderResult { + 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 { + provideHover( + doc: vscode.TextDocument, + hoverPosition: vscode.Position + ): vscode.ProviderResult { 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 { val: undefined | T; - constructor(private readonly compute: () => undefined | T) { } + constructor(private readonly compute: () => undefined | T) {} get() { return this.val ?? (this.val = this.compute()); diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 7519cd3de3..c960e283d3 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -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 { +export async function createClient( + serverPath: string, + workspace: Workspace, + extraEnv: Env +): Promise { // '.' 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 = (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 = (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(); - 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, _documentSelector: lc.DocumentSelector | undefined): void { - } - dispose(): void { - } + initialize( + _capabilities: lc.ServerCapabilities, + _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 + ); } diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 1e89938c05..ae94ecac6d 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -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(); provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult { - 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 { 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(); provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult { - 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 { 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(); 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 { + provideTextDocumentContent( + uri: vscode.Uri, + ct: vscode.CancellationToken + ): vscode.ProviderResult { 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 { 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(); 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 { + provideTextDocumentContent( + _uri: vscode.Uri, + ct: vscode.CancellationToken + ): vscode.ProviderResult { 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 { 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(); 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 { + provideTextDocumentContent( + _uri: vscode.Uri, + ct: vscode.CancellationToken + ): vscode.ProviderResult { 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 { 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(); 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 { + provideTextDocumentContent( + _uri: vscode.Uri, + ct: vscode.CancellationToken + ): vscode.ProviderResult { 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 { 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(); async provideTextDocumentContent(_uri: vscode.Uri): Promise { 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 { 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); }; diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 9837fd16f5..592ebe0ce3 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -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 | { mask?: string; env: Record }[]; +export type RunnableEnvCfg = + | undefined + | Record + | { mask?: string; env: Record }[]; 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("server.path") ?? this.get("serverPath"); } - get serverExtraEnv() { return this.get("server.extraEnv") ?? {}; } - get traceExtension() { return this.get("trace.extension"); } + get serverExtraEnv() { + return this.get("server.extraEnv") ?? {}; + } + get traceExtension() { + return this.get("trace.extension"); + } get cargoRunner() { return this.get("cargoRunner"); @@ -109,7 +119,8 @@ export class Config { let sourceFileMap = this.get | "auto">("debug.sourceFileMap"); if (sourceFileMap !== "auto") { // "/rustc/" used by suggestions only. - const { ["/rustc/"]: _, ...trimmed } = this.get>("debug.sourceFileMap"); + const { ["/rustc/"]: _, ...trimmed } = + this.get>("debug.sourceFileMap"); sourceFileMap = trimmed; } @@ -117,7 +128,7 @@ export class Config { engine: this.get("debug.engine"), engineSettings: this.get("debug.engineSettings"), openDebugPane: this.get("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(); // 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(); - const depRe = new RegExp(/\${(?.+?)}/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(); + const depRe = new RegExp(/\${(?.+?)}/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(); for (const dep of missingDeps) { const match = /(?.*?):(?.+)/.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( - /\${(?.+?)}/g, (_wholeMatch, 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 + "}"; } } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 0c3e6810e9..fb2268f89f 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -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 { 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[] { diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 830980f968..1f06c99576 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -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, sourceFileMap?: Record) => vscode.DebugConfiguration; +type DebugConfigProvider = ( + config: ra.Runnable, + executable: string, + env: Record, + sourceFileMap?: Record +) => vscode.DebugConfiguration; export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise { 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("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("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 { +async function getDebugConfiguration( + ctx: Ctx, + runnable: ra.Runnable +): Promise { const editor = ctx.activeRustEditor; if (!editor) return; const knownEngines: Record = { "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 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 { - 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, sourceFileMap?: Record): vscode.DebugConfiguration { +function getLldbDebugConfig( + runnable: ra.Runnable, + executable: string, + env: Record, + sourceFileMap?: Record +): 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, sourceFileMap?: Record): vscode.DebugConfiguration { +function getCppvsDebugConfig( + runnable: ra.Runnable, + executable: string, + env: Record, + sourceFileMap?: Record +): vscode.DebugConfiguration { return { - type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", + type: os.platform() === "win32" ? "cppvsdbg" : "cppdbg", request: "launch", name: runnable.label, program: executable, diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 61078b58c7..f80af78a74 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -7,7 +7,9 @@ import * as lc from "vscode-languageclient"; export interface AnalyzerStatusParams { textDocument?: lc.TextDocumentIdentifier; } -export const analyzerStatus = new lc.RequestType("rust-analyzer/analyzerStatus"); +export const analyzerStatus = new lc.RequestType( + "rust-analyzer/analyzerStatus" +); export const memoryUsage = new lc.RequestType0("rust-analyzer/memoryUsage"); export const shuffleCrateGraph = new lc.RequestType0("rust-analyzer/shuffleCrateGraph"); @@ -16,7 +18,9 @@ export interface ServerStatusParams { quiescent: boolean; message?: string; } -export const serverStatus = new lc.NotificationType("experimental/serverStatus"); +export const serverStatus = new lc.NotificationType( + "experimental/serverStatus" +); export const reloadWorkspace = new lc.RequestType0("rust-analyzer/reloadWorkspace"); @@ -31,23 +35,33 @@ export interface SyntaxTreeParams { textDocument: lc.TextDocumentIdentifier; range: lc.Range | null; } -export const syntaxTree = new lc.RequestType("rust-analyzer/syntaxTree"); +export const syntaxTree = new lc.RequestType( + "rust-analyzer/syntaxTree" +); -export const viewHir = new lc.RequestType("rust-analyzer/viewHir"); +export const viewHir = new lc.RequestType( + "rust-analyzer/viewHir" +); -export const viewFileText = new lc.RequestType("rust-analyzer/viewFileText"); +export const viewFileText = new lc.RequestType( + "rust-analyzer/viewFileText" +); export interface ViewItemTreeParams { textDocument: lc.TextDocumentIdentifier; } -export const viewItemTree = new lc.RequestType("rust-analyzer/viewItemTree"); +export const viewItemTree = new lc.RequestType( + "rust-analyzer/viewItemTree" +); export interface ViewCrateGraphParams { full: boolean; } -export const viewCrateGraph = new lc.RequestType("rust-analyzer/viewCrateGraph"); +export const viewCrateGraph = new lc.RequestType( + "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("rust-analyzer/expandMacro"); +export const expandMacro = new lc.RequestType( + "rust-analyzer/expandMacro" +); export interface MatchingBraceParams { textDocument: lc.TextDocumentIdentifier; positions: lc.Position[]; } -export const matchingBrace = new lc.RequestType("experimental/matchingBrace"); +export const matchingBrace = new lc.RequestType( + "experimental/matchingBrace" +); -export const parentModule = new lc.RequestType("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("experimental/joinLines"); +export const joinLines = new lc.RequestType( + "experimental/joinLines" +); -export const onEnter = new lc.RequestType("experimental/onEnter"); +export const onEnter = new lc.RequestType( + "experimental/onEnter" +); export interface RunnablesParams { textDocument: lc.TextDocumentIdentifier; @@ -93,13 +119,17 @@ export interface Runnable { overrideCargo?: string; }; } -export const runnables = new lc.RequestType("experimental/runnables"); +export const runnables = new lc.RequestType( + "experimental/runnables" +); export interface TestInfo { runnable: Runnable; } -export const relatedTests = new lc.RequestType("rust-analyzer/relatedTests"); +export const relatedTests = new lc.RequestType( + "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('experimental/ssr'); +export const ssr = new lc.RequestType("experimental/ssr"); export interface CommandLink extends lc.Command { /** @@ -122,15 +152,21 @@ export interface CommandLinkGroup { commands: CommandLink[]; } -export const openDocs = new lc.RequestType('experimental/externalDocs'); +export const openDocs = new lc.RequestType( + "experimental/externalDocs" +); -export const openCargoToml = new lc.RequestType("experimental/openCargoToml"); +export const openCargoToml = new lc.RequestType( + "experimental/openCargoToml" +); export interface OpenCargoTomlParams { textDocument: lc.TextDocumentIdentifier; } -export const moveItem = new lc.RequestType("experimental/moveItem"); +export const moveItem = new lc.RequestType( + "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", } diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 39bd73c663..61824fae21 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -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 { +export async function activate( + context: vscode.ExtensionContext +): Promise { // 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 { 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 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 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 { +async function bootstrap( + context: vscode.ExtensionContext, + config: Config, + state: PersistentState +): Promise { 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 { 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 { 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 { ); } -async function getServer(context: vscode.ExtensionContext, config: Config, state: PersistentState): Promise { +async function getServer( + context: vscode.ExtensionContext, + config: Config, + state: PersistentState +): Promise { 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 { 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 { 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 diff --git a/editors/code/src/persistent_state.ts b/editors/code/src/persistent_state.ts index 3e86ed1e32..8964a78dc3 100644 --- a/editors/code/src/persistent_state.ts +++ b/editors/code/src/persistent_state.ts @@ -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) { diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index d0be840686..b3dff3db5d 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -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 { +export async function selectRunnable( + ctx: Ctx, + prevRunnable?: RunnableQuickPick, + debuggeeOnly = false, + showButtons: boolean = true +): Promise { 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(); @@ -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 { - const env: Record = { "RUST_BACKTRACE": "short" }; +export function prepareEnv( + runnable: ra.Runnable, + runnableEnvCfg: RunnableEnvCfg +): Record { + const env: Record = { 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; } diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts index a409e52963..14c2121d08 100644 --- a/editors/code/src/snippets.ts +++ b/editors/code/src/snippets.ts @@ -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 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]; diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 99edd9ae9d..8aa6bcee19 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -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 { - 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"] ); } diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 681932c93b..c23a9a8d9e 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -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 { - 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)) { diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 057a3d2e19..cd91932bb6 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -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( client: lc.LanguageClient, reqType: lc.RequestType, param: TParam, - token?: vscode.CancellationToken, + token?: vscode.CancellationToken ): Promise { // 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( 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 { - 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(func: (this: TThis, arg: Param) => Promise) { +export function memoizeAsync( + func: (this: TThis, arg: Param) => Promise +) { const cache = new Map(); - return async function(this: TThis, arg: Param) { + return async function (this: TThis, arg: Param) { const cached = cache.get(arg); if (cached) return cached; diff --git a/editors/code/tests/runTests.ts b/editors/code/tests/runTests.ts index 6172cc7d5f..08632ec3b4 100644 --- a/editors/code/tests/runTests.ts +++ b/editors/code/tests/runTests.ts @@ -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); }); diff --git a/editors/code/tests/unit/index.ts b/editors/code/tests/unit/index.ts index 288bd60326..2fa223bed4 100644 --- a/editors/code/tests/unit/index.ts +++ b/editors/code/tests/unit/index.ts @@ -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 { 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)); diff --git a/editors/code/tests/unit/launch_config.test.ts b/editors/code/tests/unit/launch_config.test.ts index aa7a6be269..0531e064d2 100644 --- a/editors/code/tests/unit/launch_config.test.ts +++ b/editors/code/tests/unit/launch_config.test.ts @@ -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); }); }); diff --git a/editors/code/tests/unit/runnable_env.test.ts b/editors/code/tests/unit/runnable_env.test.ts index 085c96da92..b7d59e399d 100644 --- a/editors/code/tests/unit/runnable_env.test.ts +++ b/editors/code/tests/unit/runnable_env.test.ts @@ -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 { - 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"); diff --git a/editors/code/tests/unit/settings.test.ts b/editors/code/tests/unit/settings.test.ts index dca4e38d13..224cea5a23 100644 --- a/editors/code/tests/unit/settings.test.ts +++ b/editors/code/tests/unit/settings.test.ts @@ -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"); }); }); } diff --git a/editors/code/tsconfig.eslint.json b/editors/code/tsconfig.eslint.json index 9eddf27986..5e2b33ca39 100644 --- a/editors/code/tsconfig.eslint.json +++ b/editors/code/tsconfig.eslint.json @@ -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" + ] } diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json index e2ba2f231a..42e2846858 100644 --- a/editors/code/tsconfig.json +++ b/editors/code/tsconfig.json @@ -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"] }