From 5eac2d4c55fec5f96d84d20cc033c2a8e938f53e Mon Sep 17 00:00:00 2001 From: veetaha Date: Wed, 6 May 2020 00:39:29 +0300 Subject: Drop dead code and a dependency! --- editors/code/src/color_theme.ts | 129 ---------------------------------------- 1 file changed, 129 deletions(-) delete mode 100644 editors/code/src/color_theme.ts (limited to 'editors/code/src') diff --git a/editors/code/src/color_theme.ts b/editors/code/src/color_theme.ts deleted file mode 100644 index 5b9327b28..000000000 --- a/editors/code/src/color_theme.ts +++ /dev/null @@ -1,129 +0,0 @@ -import * as fs from 'fs'; -import * as jsonc from 'jsonc-parser'; -import * as path from 'path'; -import * as vscode from 'vscode'; - -export interface TextMateRuleSettings { - foreground?: string; - background?: string; - fontStyle?: string; -} - -export class ColorTheme { - private rules: Map = new Map(); - - static load(): ColorTheme { - // Find out current color theme - const themeName = vscode.workspace - .getConfiguration('workbench') - .get('colorTheme'); - - if (typeof themeName !== 'string') { - // console.warn('workbench.colorTheme is', themeName) - return new ColorTheme(); - } - return loadThemeNamed(themeName); - } - - static fromRules(rules: TextMateRule[]): ColorTheme { - const res = new ColorTheme(); - for (const rule of rules) { - const scopes = typeof rule.scope === 'undefined' - ? [] - : typeof rule.scope === 'string' - ? [rule.scope] - : rule.scope; - - for (const scope of scopes) { - res.rules.set(scope, rule.settings); - } - } - return res; - } - - lookup(scopes: string[]): TextMateRuleSettings { - let res: TextMateRuleSettings = {}; - for (const scope of scopes) { - this.rules.forEach((value, key) => { - if (scope.startsWith(key)) { - res = mergeRuleSettings(res, value); - } - }); - } - return res; - } - - mergeFrom(other: ColorTheme) { - other.rules.forEach((value, key) => { - const merged = mergeRuleSettings(this.rules.get(key), value); - this.rules.set(key, merged); - }); - } -} - -function loadThemeNamed(themeName: string): ColorTheme { - function isTheme(extension: vscode.Extension): boolean { - return ( - extension.extensionKind === vscode.ExtensionKind.UI && - extension.packageJSON.contributes && - extension.packageJSON.contributes.themes - ); - } - - const themePaths: string[] = vscode.extensions.all - .filter(isTheme) - .flatMap( - ext => ext.packageJSON.contributes.themes - .filter((it: any) => (it.id || it.label) === themeName) - .map((it: any) => path.join(ext.extensionPath, it.path)) - ); - - const res = new ColorTheme(); - for (const themePath of themePaths) { - res.mergeFrom(loadThemeFile(themePath)); - } - - const globalCustomizations: any = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations'); - res.mergeFrom(ColorTheme.fromRules(globalCustomizations?.textMateRules ?? [])); - - const themeCustomizations: any = vscode.workspace.getConfiguration('editor.tokenColorCustomizations').get(`[${themeName}]`); - res.mergeFrom(ColorTheme.fromRules(themeCustomizations?.textMateRules ?? [])); - - - return res; -} - -function loadThemeFile(themePath: string): ColorTheme { - let text; - try { - text = fs.readFileSync(themePath, 'utf8'); - } catch { - return new ColorTheme(); - } - const obj = jsonc.parse(text); - const tokenColors: TextMateRule[] = obj?.tokenColors ?? []; - const res = ColorTheme.fromRules(tokenColors); - - for (const include of obj?.include ?? []) { - const includePath = path.join(path.dirname(themePath), include); - res.mergeFrom(loadThemeFile(includePath)); - } - - return res; -} - -interface TextMateRule { - scope: string | string[]; - settings: TextMateRuleSettings; -} - -function mergeRuleSettings( - defaultSetting: TextMateRuleSettings | undefined, - override: TextMateRuleSettings, -): TextMateRuleSettings { - return { - foreground: override.foreground ?? defaultSetting?.foreground, - background: override.background ?? defaultSetting?.background, - fontStyle: override.fontStyle ?? defaultSetting?.fontStyle, - }; -} -- cgit v1.2.3 From a78dd06951dffcc6ff69aec21a2d8224c12f5026 Mon Sep 17 00:00:00 2001 From: veetaha Date: Wed, 6 May 2020 01:22:02 +0300 Subject: Preliminary refactoring of cargo.ts --- editors/code/src/cargo.ts | 45 +++++++++++----------------------- editors/code/src/commands/runnables.ts | 7 ++++-- 2 files changed, 19 insertions(+), 33 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index a328ba9bd..613aa82da 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -10,17 +10,9 @@ interface CompilationArtifact { } export class Cargo { - rootFolder: string; - env?: Record; - output: OutputChannel; + constructor(readonly rootFolder: string, readonly output: OutputChannel) { } - public constructor(cargoTomlFolder: string, output: OutputChannel, env: Record | undefined = undefined) { - this.rootFolder = cargoTomlFolder; - this.output = output; - this.env = env; - } - - public async artifactsFromArgs(cargoArgs: string[]): Promise { + private async artifactsFromArgs(cargoArgs: string[]): Promise { const artifacts: CompilationArtifact[] = []; try { @@ -37,17 +29,13 @@ export class Cargo { 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) { + } catch (err) { this.output.show(true); throw new Error(`Cargo invocation has failed: ${err}`); } @@ -55,9 +43,8 @@ export class Cargo { return artifacts; } - public async executableFromArgs(args: string[]): Promise { - const cargoArgs = [...args]; // to remain args unchanged - cargoArgs.push("--message-format=json"); + async executableFromArgs(args: readonly string[]): Promise { + const cargoArgs = [...args, "--message-format=json"]; const artifacts = await this.artifactsFromArgs(cargoArgs); @@ -70,24 +57,20 @@ export class Cargo { return artifacts[0].fileName; } - runCargo( + private runCargo( cargoArgs: string[], onStdoutJson: (obj: any) => void, onStderrString: (data: string) => void ): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const cargo = cp.spawn('cargo', cargoArgs, { stdio: ['ignore', 'pipe', 'pipe'], - cwd: this.rootFolder, - env: this.env, + cwd: this.rootFolder }); - cargo.on('error', err => { - reject(new Error(`could not launch cargo: ${err}`)); - }); - cargo.stderr.on('data', chunk => { - onStderrString(chunk.toString()); - }); + cargo.on('error', err => reject(new Error(`could not launch cargo: ${err}`))); + + cargo.stderr.on('data', chunk => onStderrString(chunk.toString())); const rl = readline.createInterface({ input: cargo.stdout }); rl.on('line', line => { @@ -103,4 +86,4 @@ export class Cargo { }); }); } -} \ No newline at end of file +} diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index d77e8188c..2ed150e25 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -119,8 +119,11 @@ export function debugSingle(ctx: Ctx): Cmd { } if (!debugEngine) { - vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=${lldbId})` - + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=${cpptoolsId}) extension for debugging.`); + vscode.window.showErrorMessage( + `Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=${lldbId}) ` + + `or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=${cpptoolsId}) ` + + `extension for debugging.` + ); return; } -- cgit v1.2.3 From c9b395be2bfcd67e045c1031143b7e8c27a6d3fb Mon Sep 17 00:00:00 2001 From: veetaha Date: Wed, 6 May 2020 01:42:04 +0300 Subject: Fix cargo not found on macos bug at vscode extension side --- editors/code/src/cargo.ts | 36 +++++++++++++++++++++++++++++++++++- editors/code/src/main.ts | 8 ++------ editors/code/src/util.ts | 11 +++++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index 613aa82da..2a2c2e0e1 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -1,6 +1,9 @@ import * as cp from 'child_process'; +import * as os from 'os'; +import * as path from 'path'; import * as readline from 'readline'; import { OutputChannel } from 'vscode'; +import { isValidExecutable } from './util'; interface CompilationArtifact { fileName: string; @@ -63,7 +66,14 @@ export class Cargo { onStderrString: (data: string) => void ): Promise { return new Promise((resolve, reject) => { - const cargo = cp.spawn('cargo', cargoArgs, { + let cargoPath; + try { + cargoPath = getCargoPathOrFail(); + } catch (err) { + return reject(err); + } + + const cargo = cp.spawn(cargoPath, cargoArgs, { stdio: ['ignore', 'pipe', 'pipe'], cwd: this.rootFolder }); @@ -87,3 +97,27 @@ export class Cargo { }); } } + +// Mirrors `ra_env::get_path_for_executable` implementation +function getCargoPathOrFail(): string { + const envVar = process.env.CARGO; + const executableName = "cargo"; + + if (envVar) { + if (isValidExecutable(envVar)) return envVar; + + throw new Error(`\`${envVar}\` environment variable points to something that's not a valid executable`); + } + + if (isValidExecutable(executableName)) return executableName; + + const standardLocation = path.join(os.homedir(), '.cargo', 'bin', executableName); + + if (isValidExecutable(standardLocation)) return standardLocation; + + throw new Error( + `Failed to find \`${executableName}\` executable. ` + + `Make sure \`${executableName}\` is in \`$PATH\`, ` + + `or set \`${envVar}\` to point to a valid executable.` + ); +} diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index efd56a84b..9b020d001 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -8,10 +8,9 @@ import { activateInlayHints } from './inlay_hints'; import { activateStatusDisplay } from './status_display'; import { Ctx } from './ctx'; import { Config, NIGHTLY_TAG } from './config'; -import { log, assert } from './util'; +import { log, assert, isValidExecutable } from './util'; import { PersistentState } from './persistent_state'; import { fetchRelease, download } from './net'; -import { spawnSync } from 'child_process'; import { activateTaskProvider } from './tasks'; let ctx: Ctx | undefined; @@ -179,10 +178,7 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise< log.debug("Using server binary at", path); - const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); - log.debug("Checked binary availability via --version", res); - log.debug(res, "--version output:", res.output); - if (res.status !== 0) { + if (!isValidExecutable(path)) { throw new Error(`Failed to execute ${path} --version`); } diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 6f91f81d6..127a9e911 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -1,6 +1,7 @@ import * as lc from "vscode-languageclient"; import * as vscode from "vscode"; import { strict as nativeAssert } from "assert"; +import { spawnSync } from "child_process"; export function assert(condition: boolean, explanation: string): asserts condition { try { @@ -82,3 +83,13 @@ export function isRustDocument(document: vscode.TextDocument): document is RustD export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor { return isRustDocument(editor.document); } + +export function isValidExecutable(path: string): boolean { + log.debug("Checking availability of a binary at", path); + + const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); + + log.debug(res, "--version output:", res.output); + + return res.status === 0; +} -- cgit v1.2.3 From c4ca6e29c25df209c2733ef24b8b4eca70ee93a4 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 6 May 2020 16:01:35 +0300 Subject: Uniformed way to get Debug Lens target executable. --- editors/code/src/commands/runnables.ts | 59 +++++++++++++++++++--------------- editors/code/src/config.ts | 6 ++-- 2 files changed, 37 insertions(+), 28 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index d77e8188c..7bb8727e7 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -64,29 +64,19 @@ export function runSingle(ctx: Ctx): Cmd { }; } -function getLldbDebugConfig(config: ra.Runnable, sourceFileMap: Record): vscode.DebugConfiguration { +function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { return { type: "lldb", request: "launch", name: config.label, - cargo: { - args: config.args, - }, + program: executable, args: config.extraArgs, cwd: config.cwd, sourceMap: sourceFileMap }; } -const debugOutput = vscode.window.createOutputChannel("Debug"); - -async function getCppvsDebugConfig(config: ra.Runnable, sourceFileMap: Record): Promise { - debugOutput.clear(); - - const cargo = new Cargo(config.cwd || '.', debugOutput); - const executable = await cargo.executableFromArgs(config.args); - - // if we are here, there were no compilation errors. +function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { return { type: (os.platform() === "win32") ? "cppvsdbg" : 'cppdbg', request: "launch", @@ -98,36 +88,53 @@ async function getCppvsDebugConfig(config: ra.Runnable, sourceFileMap: Record { + debugOutput.clear(); + + const cargo = new Cargo(config.cwd || '.', debugOutput); + const executable = await cargo.executableFromArgs(config.args); + + // if we are here, there were no compilation errors. + return executable; +} + +type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record) => vscode.DebugConfiguration; + export function debugSingle(ctx: Ctx): Cmd { return async (config: ra.Runnable) => { const editor = ctx.activeRustEditor; if (!editor) return; - const lldbId = "vadimcn.vscode-lldb"; - const cpptoolsId = "ms-vscode.cpptools"; + const knownEngines: Record = { + "vadimcn.vscode-lldb": getLldbDebugConfig, + "ms-vscode.cpptools": getCppvsDebugConfig + }; + const debugOptions = ctx.config.debug; - const debugEngineId = ctx.config.debug.engine; let debugEngine = null; - if (debugEngineId === "auto") { - debugEngine = vscode.extensions.getExtension(lldbId); - if (!debugEngine) { - debugEngine = vscode.extensions.getExtension(cpptoolsId); + if (debugOptions.engine === "auto") { + for (var engineId in knownEngines) { + debugEngine = vscode.extensions.getExtension(engineId); + if (debugEngine) break; } } else { - debugEngine = vscode.extensions.getExtension(debugEngineId); + debugEngine = vscode.extensions.getExtension(debugOptions.engine); } if (!debugEngine) { - vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=${lldbId})` - + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=${cpptoolsId}) extension for debugging.`); + vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` + + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`); return; } - const debugConfig = lldbId === debugEngine.id - ? getLldbDebugConfig(config, ctx.config.debug.sourceFileMap) - : await getCppvsDebugConfig(config, ctx.config.debug.sourceFileMap); + const executable = await getDebugExecutable(config); + const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + debugOutput.appendLine("Launching debug configuration:"); + debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); return vscode.debug.startDebugging(undefined, debugConfig); }; } diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 110e54180..8bceaaf72 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -108,10 +108,12 @@ export class Config { } get debug() { + // "/rustc/" used by suggestions only. + const { ["/rustc/"]: _, ...sourceFileMap } = this.get>("debug.sourceFileMap"); + return { engine: this.get("debug.engine"), - sourceFileMap: this.get>("debug.sourceFileMap"), + sourceFileMap: sourceFileMap, }; } - } -- cgit v1.2.3 From 5426e2927e317a5e78179a5bd74b9414c0651b86 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 7 May 2020 17:07:58 +0300 Subject: Add additional debug options --- editors/code/src/commands/runnables.ts | 12 +++++++++--- editors/code/src/config.ts | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 7bb8727e7..782a7ba89 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -91,8 +91,6 @@ function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFile const debugOutput = vscode.window.createOutputChannel("Debug"); async function getDebugExecutable(config: ra.Runnable): Promise { - debugOutput.clear(); - const cargo = new Cargo(config.cwd || '.', debugOutput); const executable = await cargo.executableFromArgs(config.args); @@ -130,8 +128,16 @@ export function debugSingle(ctx: Ctx): Cmd { return; } + debugOutput.clear(); + if (ctx.config.debug.openUpDebugPane) { + debugOutput.show(true); + } + const executable = await getDebugExecutable(config); - const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + let debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + for (var key in debugOptions.engineSettings) { + debugConfig[key] = (debugOptions.engineSettings as any)[key]; + } debugOutput.appendLine("Launching debug configuration:"); debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 8bceaaf72..533be1913 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -113,6 +113,8 @@ export class Config { return { engine: this.get("debug.engine"), + engineSettings: this.get("debug.engineSettings"), + openUpDebugPane: this.get("debug.openUpDebugPane"), sourceFileMap: sourceFileMap, }; } -- cgit v1.2.3 From 435a17ecd8806f3ae81edf6277c17363b01f4334 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 7 May 2020 18:35:48 +0300 Subject: Add separate settings for each debug engine. --- editors/code/src/commands/runnables.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 782a7ba89..e62de7d6e 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -134,9 +134,12 @@ export function debugSingle(ctx: Ctx): Cmd { } const executable = await getDebugExecutable(config); - let debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); - for (var key in debugOptions.engineSettings) { - debugConfig[key] = (debugOptions.engineSettings as any)[key]; + const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + if (debugConfig.type in debugOptions.engineSettings) { + const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; + for (var key in settingsMap) { + debugConfig[key] = settingsMap[key]; + } } debugOutput.appendLine("Launching debug configuration:"); -- cgit v1.2.3 From 23f4859166ba16f02927b476aad2ae91e618b1ef Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 7 May 2020 18:53:14 +0300 Subject: Add CodeLLDB Rust visualization --- editors/code/src/commands/runnables.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index e62de7d6e..ae328d2a4 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -72,7 +72,8 @@ function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileM program: executable, args: config.extraArgs, cwd: config.cwd, - sourceMap: sourceFileMap + sourceMap: sourceFileMap, + sourceLanguages: ["rust"] }; } -- cgit v1.2.3 From 3bf5ef02c0dc3087fb4cdd0a794892edde359a0d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 8 May 2020 09:28:15 +0200 Subject: Add master config for inlayHints to make disabling easy --- editors/code/src/config.ts | 1 + editors/code/src/inlay_hints.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 110e54180..46de922f3 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -94,6 +94,7 @@ export class Config { get inlayHints() { return { + enable: this.get("inlayHints.enable"), typeHints: this.get("inlayHints.typeHints"), parameterHints: this.get("inlayHints.parameterHints"), chainingHints: this.get("inlayHints.chainingHints"), diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index a09531797..a2b07d003 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts @@ -10,13 +10,13 @@ export function activateInlayHints(ctx: Ctx) { const maybeUpdater = { updater: null as null | HintsUpdater, async onConfigChange() { - if ( - !ctx.config.inlayHints.typeHints && - !ctx.config.inlayHints.parameterHints && - !ctx.config.inlayHints.chainingHints - ) { - return this.dispose(); - } + const anyEnabled = ctx.config.inlayHints.typeHints + || ctx.config.inlayHints.parameterHints + || ctx.config.inlayHints.chainingHints; + const enabled = ctx.config.inlayHints.enable && anyEnabled; + + if (!enabled) return this.dispose(); + await sleep(100); if (this.updater) { this.updater.syncCacheAndRenderHints(); -- cgit v1.2.3 From 31d5c8d4878911b21280b144b1aac19545509973 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 10 May 2020 21:05:09 +0800 Subject: Word fix --- editors/code/src/commands/syntax_tree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts index cfcf47b2f..b80a18a47 100644 --- a/editors/code/src/commands/syntax_tree.ts +++ b/editors/code/src/commands/syntax_tree.ts @@ -225,7 +225,7 @@ class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, D return doc.positionAt(targetOffset); } - // Shitty workaround for crlf line endings + // Dirty workaround for crlf line endings // We are still in this prehistoric era of carriage returns here... let line = 0; -- cgit v1.2.3 From 9a82ee0de25901447fc49a9337d7290c0e6f6532 Mon Sep 17 00:00:00 2001 From: veetaha Date: Sun, 10 May 2020 20:43:48 +0300 Subject: Fix "show syntax tree" command @matlkad please don't forget to keep it up-to-date! --- editors/code/src/commands/syntax_tree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts index cfcf47b2f..b7616c21f 100644 --- a/editors/code/src/commands/syntax_tree.ts +++ b/editors/code/src/commands/syntax_tree.ts @@ -206,7 +206,7 @@ class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, D } private parseRustTextRange(doc: vscode.TextDocument, astLine: string): undefined | vscode.Range { - const parsedRange = /\[(\d+); (\d+)\)/.exec(astLine); + const parsedRange = /(\d+)\.\.(\d+)/.exec(astLine); if (!parsedRange) return; const [begin, end] = parsedRange -- cgit v1.2.3 From 155f0601421620086a256c9e313568d5bd7391e0 Mon Sep 17 00:00:00 2001 From: vsrs Date: Mon, 11 May 2020 16:06:57 +0300 Subject: "rust-analyzer.debug" command --- editors/code/src/commands/runnables.ts | 149 ++++++++++----------------------- editors/code/src/debug.ts | 95 +++++++++++++++++++++ editors/code/src/main.ts | 1 + 3 files changed, 139 insertions(+), 106 deletions(-) create mode 100644 editors/code/src/debug.ts (limited to 'editors/code/src') diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index ae328d2a4..c1b872bce 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -1,43 +1,46 @@ import * as vscode from 'vscode'; import * as lc from 'vscode-languageclient'; import * as ra from '../rust-analyzer-api'; -import * as os from "os"; import { Ctx, Cmd } from '../ctx'; -import { Cargo } from '../cargo'; +import { startDebugSession } from '../debug'; + +async function selectRunnable(ctx: Ctx, prevRunnable: RunnableQuickPick | undefined): Promise { + const editor = ctx.activeRustEditor; + const client = ctx.client; + if (!editor || !client) return; + + const textDocument: lc.TextDocumentIdentifier = { + uri: editor.document.uri.toString(), + }; + + const runnables = await client.sendRequest(ra.runnables, { + textDocument, + 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) + ) { + continue; + } + items.push(new RunnableQuickPick(r)); + } + return await vscode.window.showQuickPick(items); +} export function run(ctx: Ctx): Cmd { let prevRunnable: RunnableQuickPick | undefined; return async () => { - const editor = ctx.activeRustEditor; - const client = ctx.client; - if (!editor || !client) return; - - const textDocument: lc.TextDocumentIdentifier = { - uri: editor.document.uri.toString(), - }; - - const runnables = await client.sendRequest(ra.runnables, { - textDocument, - 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) - ) { - continue; - } - items.push(new RunnableQuickPick(r)); - } - const item = await vscode.window.showQuickPick(items); + const item = await selectRunnable(ctx, prevRunnable); if (!item) return; item.detail = 'rerun'; @@ -64,88 +67,22 @@ export function runSingle(ctx: Ctx): Cmd { }; } -function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { - return { - type: "lldb", - request: "launch", - name: config.label, - program: executable, - args: config.extraArgs, - cwd: config.cwd, - sourceMap: sourceFileMap, - sourceLanguages: ["rust"] - }; -} - -function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { - return { - type: (os.platform() === "win32") ? "cppvsdbg" : 'cppdbg', - request: "launch", - name: config.label, - program: executable, - args: config.extraArgs, - cwd: config.cwd, - sourceFileMap: sourceFileMap, - }; -} +export function debug(ctx: Ctx): Cmd { + let prevDebuggee: RunnableQuickPick | undefined; -const debugOutput = vscode.window.createOutputChannel("Debug"); - -async function getDebugExecutable(config: ra.Runnable): Promise { - const cargo = new Cargo(config.cwd || '.', debugOutput); - const executable = await cargo.executableFromArgs(config.args); + return async () => { + const item = await selectRunnable(ctx, prevDebuggee); + if (!item) return; - // if we are here, there were no compilation errors. - return executable; + item.detail = 'restart'; + prevDebuggee = item; + return await startDebugSession(ctx, item.runnable); + }; } -type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record) => vscode.DebugConfiguration; - export function debugSingle(ctx: Ctx): Cmd { return async (config: ra.Runnable) => { - const editor = ctx.activeRustEditor; - if (!editor) return; - - const knownEngines: Record = { - "vadimcn.vscode-lldb": getLldbDebugConfig, - "ms-vscode.cpptools": getCppvsDebugConfig - }; - const debugOptions = ctx.config.debug; - - let debugEngine = null; - if (debugOptions.engine === "auto") { - for (var engineId in knownEngines) { - debugEngine = vscode.extensions.getExtension(engineId); - if (debugEngine) break; - } - } - else { - debugEngine = vscode.extensions.getExtension(debugOptions.engine); - } - - if (!debugEngine) { - vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` - + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`); - return; - } - - debugOutput.clear(); - if (ctx.config.debug.openUpDebugPane) { - debugOutput.show(true); - } - - const executable = await getDebugExecutable(config); - const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); - if (debugConfig.type in debugOptions.engineSettings) { - const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; - for (var key in settingsMap) { - debugConfig[key] = settingsMap[key]; - } - } - - debugOutput.appendLine("Launching debug configuration:"); - debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); - return vscode.debug.startDebugging(undefined, debugConfig); + await startDebugSession(ctx, config); }; } diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts new file mode 100644 index 000000000..4f4b88adf --- /dev/null +++ b/editors/code/src/debug.ts @@ -0,0 +1,95 @@ +import * as os from "os"; +import * as vscode from 'vscode'; +import * as ra from './rust-analyzer-api'; + +import { Cargo } from './cargo'; +import { Ctx } from "./ctx"; + +const debugOutput = vscode.window.createOutputChannel("Debug"); +type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record) => vscode.DebugConfiguration; + +function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { + return { + type: "lldb", + request: "launch", + name: config.label, + program: executable, + args: config.extraArgs, + cwd: config.cwd, + sourceMap: sourceFileMap, + sourceLanguages: ["rust"] + }; +} + +function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { + return { + type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", + request: "launch", + name: config.label, + program: executable, + args: config.extraArgs, + cwd: config.cwd, + sourceFileMap: sourceFileMap, + }; +} + +async function getDebugExecutable(config: ra.Runnable): Promise { + const cargo = new Cargo(config.cwd || '.', debugOutput); + const executable = await cargo.executableFromArgs(config.args); + + // if we are here, there were no compilation errors. + return executable; +} + +export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Promise { + const editor = ctx.activeRustEditor; + if (!editor) return; + + const knownEngines: Record = { + "vadimcn.vscode-lldb": getLldbDebugConfig, + "ms-vscode.cpptools": getCppvsDebugConfig + }; + const debugOptions = ctx.config.debug; + + let debugEngine = null; + if (debugOptions.engine === "auto") { + for (var engineId in knownEngines) { + debugEngine = vscode.extensions.getExtension(engineId); + if (debugEngine) break; + } + } + else { + debugEngine = vscode.extensions.getExtension(debugOptions.engine); + } + + if (!debugEngine) { + vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` + + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`); + return; + } + + debugOutput.clear(); + if (ctx.config.debug.openUpDebugPane) { + debugOutput.show(true); + } + + const executable = await getDebugExecutable(config); + const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + if (debugConfig.type in debugOptions.engineSettings) { + const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; + for (var key in settingsMap) { + debugConfig[key] = settingsMap[key]; + } + } + + return debugConfig; +} + +export async function startDebugSession(ctx: Ctx, config: ra.Runnable): Promise { + const debugConfig = await getDebugConfiguration(ctx, config); + if (!debugConfig) return false; + + debugOutput.appendLine("Launching debug configuration:"); + debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); + return vscode.debug.startDebugging(undefined, debugConfig); +} diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 9b020d001..5fdeebd68 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -77,6 +77,7 @@ export async function activate(context: vscode.ExtensionContext) { ctx.registerCommand('syntaxTree', commands.syntaxTree); ctx.registerCommand('expandMacro', commands.expandMacro); ctx.registerCommand('run', commands.run); + ctx.registerCommand('debug', commands.debug); defaultOnEnter.dispose(); ctx.registerCommand('onEnter', commands.onEnter); -- cgit v1.2.3 From fee0a9fa5a3dd84400108b33a1e8225dc364a9fa Mon Sep 17 00:00:00 2001 From: vsrs Date: Mon, 11 May 2020 18:00:15 +0300 Subject: "rust-analyzer.newDebugConfig" command --- editors/code/src/commands/runnables.ts | 30 +++++++++++++++++++++++++++++- editors/code/src/debug.ts | 3 +-- editors/code/src/main.ts | 1 + 3 files changed, 31 insertions(+), 3 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index c1b872bce..5e88eeae0 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -3,7 +3,7 @@ import * as lc from 'vscode-languageclient'; import * as ra from '../rust-analyzer-api'; import { Ctx, Cmd } from '../ctx'; -import { startDebugSession } from '../debug'; +import { startDebugSession, getDebugConfiguration } from '../debug'; async function selectRunnable(ctx: Ctx, prevRunnable: RunnableQuickPick | undefined): Promise { const editor = ctx.activeRustEditor; @@ -86,6 +86,34 @@ export function debugSingle(ctx: Ctx): Cmd { }; } +export function newDebugConfig(ctx: Ctx): Cmd { + return async () => { + const scope = ctx.activeRustEditor?.document.uri; + if (!scope) return; + + const item = await selectRunnable(ctx, undefined); + if (!item) return; + + const debugConfig = await getDebugConfiguration(ctx, item.runnable); + if (!debugConfig) return; + + const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); + const configurations = wsLaunchSection.get("configurations") || []; + + 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'); + if (answer === "Cancel") return; + + configurations[index] = debugConfig; + } else { + configurations.push(debugConfig); + } + + await wsLaunchSection.update("configurations", configurations); + }; +} + class RunnableQuickPick implements vscode.QuickPickItem { public label: string; public description?: string | undefined; diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 4f4b88adf..228a7ab75 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -57,8 +57,7 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom debugEngine = vscode.extensions.getExtension(engineId); if (debugEngine) break; } - } - else { + } else { debugEngine = vscode.extensions.getExtension(debugOptions.engine); } diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 5fdeebd68..c015460b8 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -78,6 +78,7 @@ export async function activate(context: vscode.ExtensionContext) { ctx.registerCommand('expandMacro', commands.expandMacro); ctx.registerCommand('run', commands.run); ctx.registerCommand('debug', commands.debug); + ctx.registerCommand('newDebugConfig', commands.newDebugConfig); defaultOnEnter.dispose(); ctx.registerCommand('onEnter', commands.onEnter); -- cgit v1.2.3 From e914d622ec378fd9efb9b20801c925ade806ef60 Mon Sep 17 00:00:00 2001 From: vsrs Date: Mon, 11 May 2020 18:49:45 +0300 Subject: DebugConfiguration simplification. ${workspaceRoot} substitution in generated DebugConfiguration. --- editors/code/src/debug.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 228a7ab75..cc5755611 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -1,5 +1,6 @@ import * as os from "os"; import * as vscode from 'vscode'; +import * as path from 'path'; import * as ra from './rust-analyzer-api'; import { Cargo } from './cargo'; @@ -72,8 +73,13 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom debugOutput.show(true); } + const wsFolder = path.normalize(vscode.workspace.workspaceFolders![0].uri.fsPath); // folder exists or RA is not active. + function simplifyPath(p: string): string { + return path.normalize(p).replace(wsFolder, '${workspaceRoot}'); + } + const executable = await getDebugExecutable(config); - const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + const debugConfig = knownEngines[debugEngine.id](config, simplifyPath(executable), debugOptions.sourceFileMap); if (debugConfig.type in debugOptions.engineSettings) { const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; for (var key in settingsMap) { @@ -81,6 +87,10 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom } } + if (debugConfig.cwd) { + debugConfig.cwd = simplifyPath(debugConfig.cwd); + } + return debugConfig; } -- cgit v1.2.3 From 9ebb2acdca6c711cff7bfc84a410794739092dbe Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 13 May 2020 15:51:15 +0300 Subject: Use launch.json in Debug Lens sessions. Add the possibility to use existing configurations via Debug Lens --- editors/code/src/config.ts | 1 + editors/code/src/debug.ts | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index be2e27aec..24002483d 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -117,6 +117,7 @@ export class Config { engineSettings: this.get("debug.engineSettings"), openUpDebugPane: this.get("debug.openUpDebugPane"), sourceFileMap: sourceFileMap, + useLaunchJson: this.get("debug.useLaunchJson"), }; } } diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index cc5755611..bbf3ff312 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -95,10 +95,27 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom } export async function startDebugSession(ctx: Ctx, config: ra.Runnable): Promise { - const debugConfig = await getDebugConfiguration(ctx, config); + let debugConfig: vscode.DebugConfiguration | undefined = undefined; + let message = ""; + + if (ctx.config.debug.useLaunchJson) { + const wsLaunchSection = vscode.workspace.getConfiguration("launch"); + const configurations = wsLaunchSection.get("configurations") || []; + + const index = configurations.findIndex(c => c.name === config.label); + if (-1 !== index) { + debugConfig = configurations[index]; + message = " (from launch.json)"; + debugOutput.clear(); + } + } + if (!debugConfig) { + debugConfig = await getDebugConfiguration(ctx, config); + } + if (!debugConfig) return false; - debugOutput.appendLine("Launching debug configuration:"); + debugOutput.appendLine(`Launching debug configuration${message}:`); debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); return vscode.debug.startDebugging(undefined, debugConfig); } -- cgit v1.2.3 From 3ffc26eaebb1f9491477e99d5187b048bd489cd6 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 14 May 2020 11:12:10 +0300 Subject: Remove "rust-analyzer.debug.useLaunchJson" option --- editors/code/src/config.ts | 3 +-- editors/code/src/debug.ts | 21 +++++++++------------ 2 files changed, 10 insertions(+), 14 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 24002483d..1652827c3 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -116,8 +116,7 @@ export class Config { engine: this.get("debug.engine"), engineSettings: this.get("debug.engineSettings"), openUpDebugPane: this.get("debug.openUpDebugPane"), - sourceFileMap: sourceFileMap, - useLaunchJson: this.get("debug.useLaunchJson"), + sourceFileMap: sourceFileMap }; } } diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index bbf3ff312..b500fe029 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -98,18 +98,15 @@ export async function startDebugSession(ctx: Ctx, config: ra.Runnable): Promise< let debugConfig: vscode.DebugConfiguration | undefined = undefined; let message = ""; - if (ctx.config.debug.useLaunchJson) { - const wsLaunchSection = vscode.workspace.getConfiguration("launch"); - const configurations = wsLaunchSection.get("configurations") || []; - - const index = configurations.findIndex(c => c.name === config.label); - if (-1 !== index) { - debugConfig = configurations[index]; - message = " (from launch.json)"; - debugOutput.clear(); - } - } - if (!debugConfig) { + const wsLaunchSection = vscode.workspace.getConfiguration("launch"); + const configurations = wsLaunchSection.get("configurations") || []; + + const index = configurations.findIndex(c => c.name === config.label); + if (-1 !== index) { + debugConfig = configurations[index]; + message = " (from launch.json)"; + debugOutput.clear(); + } else { debugConfig = await getDebugConfiguration(ctx, config); } -- cgit v1.2.3 From 9f0a7eb97b4e047cebbe51ffd6f9e2092dd63e00 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Fri, 24 Apr 2020 21:57:10 +0200 Subject: Make some stuff public so that they can be reused by other tools --- editors/code/src/commands/ssr.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts index 6fee051fd..4ef8cdf04 100644 --- a/editors/code/src/commands/ssr.ts +++ b/editors/code/src/commands/ssr.ts @@ -11,7 +11,7 @@ export function ssr(ctx: Ctx): Cmd { const options: vscode.InputBoxOptions = { value: "() ==>> ()", - prompt: "EnteR request, for example 'Foo($a:expr) ==> Foo::new($a)' ", + prompt: "Enter request, for example 'Foo($a:expr) ==> Foo::new($a)' ", validateInput: async (x: string) => { try { await client.sendRequest(ra.ssr, { query: x, parseOnly: true }); -- cgit v1.2.3 From be9b0609d55f9f49e4473b4ab2bc55583974fc2f Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 14 May 2020 13:22:52 +0300 Subject: Runnable quick pick with buttons --- editors/code/src/commands/runnables.ts | 82 +++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 21 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 5e88eeae0..b1d93fc34 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -5,7 +5,9 @@ import * as ra from '../rust-analyzer-api'; import { Ctx, Cmd } from '../ctx'; import { startDebugSession, getDebugConfiguration } from '../debug'; -async function selectRunnable(ctx: Ctx, prevRunnable: RunnableQuickPick | undefined): Promise { +const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; + +async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showButtons: boolean = true): Promise { const editor = ctx.activeRustEditor; const client = ctx.client; if (!editor || !client) return; @@ -33,7 +35,41 @@ async function selectRunnable(ctx: Ctx, prevRunnable: RunnableQuickPick | undefi } items.push(new RunnableQuickPick(r)); } - return await vscode.window.showQuickPick(items); + + return await new Promise((resolve) => { + const disposables: vscode.Disposable[] = []; + const close = (result?: RunnableQuickPick) => { + resolve(result); + disposables.forEach(d => d.dispose()); + }; + + const quickPick = vscode.window.createQuickPick(); + quickPick.items = items; + quickPick.title = "Select Runnable"; + if (showButtons) { + quickPick.buttons = quickPickButtons; + } + disposables.push( + quickPick.onDidHide(() => close()), + quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), + quickPick.onDidTriggerButton((_button) => { + (async () => await makeDebugConfig(ctx, quickPick.activeItems[0]))(); + close(); + }), + quickPick.onDidChangeActive((active) => { + if (showButtons && active.length > 0) { + 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) { + quickPick.buttons = quickPickButtons; + } + } + }), + quickPick + ); + quickPick.show(); + }); } export function run(ctx: Ctx): Cmd { @@ -86,31 +122,35 @@ export function debugSingle(ctx: Ctx): Cmd { }; } -export function newDebugConfig(ctx: Ctx): Cmd { - return async () => { - const scope = ctx.activeRustEditor?.document.uri; - if (!scope) return; +async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise { + const scope = ctx.activeRustEditor?.document.uri; + if (!scope) return; - const item = await selectRunnable(ctx, undefined); - if (!item) return; + const debugConfig = await getDebugConfiguration(ctx, item.runnable); + if (!debugConfig) return; - const debugConfig = await getDebugConfiguration(ctx, item.runnable); - if (!debugConfig) return; + const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); + const configurations = wsLaunchSection.get("configurations") || []; - const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); - const configurations = wsLaunchSection.get("configurations") || []; + 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'); + if (answer === "Cancel") return; - 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'); - if (answer === "Cancel") return; + configurations[index] = debugConfig; + } else { + configurations.push(debugConfig); + } - configurations[index] = debugConfig; - } else { - configurations.push(debugConfig); - } + await wsLaunchSection.update("configurations", configurations); +} + +export function newDebugConfig(ctx: Ctx): Cmd { + return async () => { + const item = await selectRunnable(ctx, undefined, false); + if (!item) return; - await wsLaunchSection.update("configurations", configurations); + await makeDebugConfig(ctx, item); }; } -- cgit v1.2.3 From a233346a2d08bde9869d86d14e67ca3290c10cb2 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 14 May 2020 13:30:05 +0300 Subject: Fix "rust-analyzer.debug" for QuickPick binaries. --- editors/code/src/cargo.ts | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'editors/code/src') diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index 2a2c2e0e1..ba286c0ab 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -48,6 +48,10 @@ export class Cargo { async executableFromArgs(args: readonly string[]): Promise { const cargoArgs = [...args, "--message-format=json"]; + if( cargoArgs[0] == "run" ) { + // a runnable from the quick pick. + cargoArgs[0] = "build"; + } const artifacts = await this.artifactsFromArgs(cargoArgs); -- cgit v1.2.3 From af7c50f8a2e6763d4d72d0fa0b33e62b12aaf1c7 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 14 May 2020 13:48:02 +0300 Subject: Multiple binaries support for launch.json. --- editors/code/src/debug.ts | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'editors/code/src') diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index b500fe029..966019883 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -87,6 +87,11 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom } } + if (debugConfig.name === "run binary") { + // A workaround for multiple binaries. It would be better to get proper names on the LSP side. + debugConfig.name = `run binary [${path.basename(executable)}]`; + } + if (debugConfig.cwd) { debugConfig.cwd = simplifyPath(debugConfig.cwd); } -- cgit v1.2.3 From 5f6cdae18f415b9af3b3d24234ab2943efb30993 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 14 May 2020 14:42:40 +0300 Subject: Mixed "bin" and "test" artifacts workaround. --- editors/code/src/cargo.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index ba286c0ab..4ff24ea7d 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -48,12 +48,17 @@ export class Cargo { async executableFromArgs(args: readonly string[]): Promise { const cargoArgs = [...args, "--message-format=json"]; - if( cargoArgs[0] == "run" ) { + if (cargoArgs[0] === "run") { // a runnable from the quick pick. cargoArgs[0] = "build"; } - const artifacts = await this.artifactsFromArgs(cargoArgs); + let artifacts = await this.artifactsFromArgs(cargoArgs); + if (cargoArgs[0] === "test") { + // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests + // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} + artifacts = artifacts.filter(a => a.isTest); + } if (artifacts.length === 0) { throw new Error('No compilation artifacts'); -- cgit v1.2.3 From 20fdd14c62aa9c5327f1e6afc04f01a5af6763fb Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 14 May 2020 16:02:01 +0300 Subject: Multiple binaries support for launch.json. Generate unique names on the LSP side. --- editors/code/src/debug.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 966019883..1f93a2b7e 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -88,8 +88,9 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom } if (debugConfig.name === "run binary") { - // A workaround for multiple binaries. It would be better to get proper names on the LSP side. - debugConfig.name = `run binary [${path.basename(executable)}]`; + // The LSP side: crates\rust-analyzer\src\main_loop\handlers.rs, + // fn to_lsp_runnable(...) with RunnableKind::Bin + debugConfig.name = `run binary '${path.basename(executable)}'`; } if (debugConfig.cwd) { -- cgit v1.2.3 From abef76bc87137203081faedd3c758796e8d6598d Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 14 May 2020 17:32:24 +0300 Subject: Fix runnable naming in the client side fallback. --- editors/code/src/debug.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 1f93a2b7e..d3fe588e8 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -90,7 +90,7 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom if (debugConfig.name === "run binary") { // The LSP side: crates\rust-analyzer\src\main_loop\handlers.rs, // fn to_lsp_runnable(...) with RunnableKind::Bin - debugConfig.name = `run binary '${path.basename(executable)}'`; + debugConfig.name = `run ${path.basename(executable)}`; } if (debugConfig.cwd) { -- cgit v1.2.3 From a4ecaa70969067c1149711dbf1f40a8a95cb5b72 Mon Sep 17 00:00:00 2001 From: vsrs Date: Fri, 15 May 2020 15:31:09 +0300 Subject: Fix occasional test run during debug configuration --- editors/code/src/cargo.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index 4ff24ea7d..28c7de992 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -48,9 +48,13 @@ export class Cargo { async executableFromArgs(args: readonly string[]): Promise { const cargoArgs = [...args, "--message-format=json"]; + + // arguments for a runnable from the quick pick should be updated. + // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens if (cargoArgs[0] === "run") { - // a runnable from the quick pick. cargoArgs[0] = "build"; + } else if (cargoArgs.indexOf("--no-run") === -1) { + cargoArgs.push("--no-run"); } let artifacts = await this.artifactsFromArgs(cargoArgs); -- cgit v1.2.3 From dc217bdf90d555eaa1780041fc3a14e64173994d Mon Sep 17 00:00:00 2001 From: vsrs Date: Sun, 17 May 2020 19:51:44 +0300 Subject: CodeLens configuration options. --- editors/code/src/config.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'editors/code/src') diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 1652827c3..93d9aa160 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -16,6 +16,9 @@ export class Config { "files", "highlighting", "updates.channel", + "lens.run", + "lens.debug", + "lens.implementations", ] .map(opt => `${this.rootSection}.${opt}`); @@ -119,4 +122,12 @@ export class Config { sourceFileMap: sourceFileMap }; } + + get lens() { + return { + run: this.get("lens.run"), + debug: this.get("lens.debug"), + implementations: this.get("lens.implementations"), + }; + } } -- cgit v1.2.3 From dec2f3fa657a2700f9db1962891e7be71c299543 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sun, 17 May 2020 20:29:59 +0300 Subject: Runnable QuickPick with debuggees only --- editors/code/src/commands/runnables.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index b1d93fc34..a408021e7 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -7,7 +7,7 @@ import { startDebugSession, getDebugConfiguration } from '../debug'; const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; -async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showButtons: boolean = true): Promise { +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; @@ -33,9 +33,20 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showBu ) { continue; } + + if (debuggeeOnly && (r.label.startsWith('doctest') || r.label.startsWith('cargo'))) { + continue; + } items.push(new RunnableQuickPick(r)); } + if( items.length === 0 ) { + // it is the debug case, run always has at least 'cargo check ...' + // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables + vscode.window.showErrorMessage("There's no debug target!"); + return; + } + return await new Promise((resolve) => { const disposables: vscode.Disposable[] = []; const close = (result?: RunnableQuickPick) => { @@ -107,7 +118,7 @@ export function debug(ctx: Ctx): Cmd { let prevDebuggee: RunnableQuickPick | undefined; return async () => { - const item = await selectRunnable(ctx, prevDebuggee); + const item = await selectRunnable(ctx, prevDebuggee, true); if (!item) return; item.detail = 'restart'; @@ -147,7 +158,7 @@ async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise export function newDebugConfig(ctx: Ctx): Cmd { return async () => { - const item = await selectRunnable(ctx, undefined, false); + const item = await selectRunnable(ctx, undefined, true, false); if (!item) return; await makeDebugConfig(ctx, item); -- cgit v1.2.3 From 3d445256fe56f4a7ead64514fb57b79079973d84 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sun, 17 May 2020 20:38:50 +0300 Subject: code formatting --- editors/code/src/commands/runnables.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index a408021e7..0bd30fb07 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -40,7 +40,7 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debugg items.push(new RunnableQuickPick(r)); } - if( items.length === 0 ) { + if (items.length === 0) { // it is the debug case, run always has at least 'cargo check ...' // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables vscode.window.showErrorMessage("There's no debug target!"); -- cgit v1.2.3 From 78817a319476d8af40c4f78e8c47dc958781f88f Mon Sep 17 00:00:00 2001 From: vsrs Date: Mon, 18 May 2020 10:27:00 +0300 Subject: Add "rust-analyzer.lens.enable" --- editors/code/src/config.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'editors/code/src') diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 93d9aa160..ee294fbe3 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -16,6 +16,7 @@ export class Config { "files", "highlighting", "updates.channel", + "lens.enable", "lens.run", "lens.debug", "lens.implementations", @@ -125,6 +126,7 @@ export class Config { get lens() { return { + enable: this.get("lens.enable"), run: this.get("lens.run"), debug: this.get("lens.debug"), implementations: this.get("lens.implementations"), -- cgit v1.2.3 From a752853350639a915178ea900a51f3c45443795e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 17 May 2020 21:24:33 +0200 Subject: Add snippetTextEdit protocol extension --- editors/code/src/client.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index cffdcf11a..2067738ea 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -35,7 +35,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient } as any }; - const res = new lc.LanguageClient( + const client = new lc.LanguageClient( 'rust-analyzer', 'Rust Analyzer Language Server', serverOptions, @@ -47,8 +47,19 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient // since they are available on stable. // Note that while these features are stable in vscode their LSP protocol // implementations are still in the "proposed" category for 3.16. - res.registerFeature(new CallHierarchyFeature(res)); - res.registerFeature(new SemanticTokensFeature(res)); + client.registerFeature(new CallHierarchyFeature(client)); + client.registerFeature(new SemanticTokensFeature(client)); + client.registerFeature(new SnippetTextEditFeature()); - return res; + return client; +} + +class SnippetTextEditFeature implements lc.StaticFeature { + fillClientCapabilities(capabilities: lc.ClientCapabilities): void { + const caps: any = capabilities.experimental ?? {}; + caps.snippetTextEdit = true; + capabilities.experimental = caps + } + initialize(_capabilities: lc.ServerCapabilities, _documentSelector: lc.DocumentSelector | undefined): void { + } } -- cgit v1.2.3 From 3dd68c1ba3e72a0959bcdaa46e730a7ae4d9ed4c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 18 May 2020 01:53:55 +0200 Subject: Implement client-side of SnippetTextEdit --- editors/code/src/client.ts | 48 ++++++++++++++++++++++++++++++++++++-- editors/code/src/commands/index.ts | 34 +++++++++++++++++++++++++++ editors/code/src/main.ts | 1 + 3 files changed, 81 insertions(+), 2 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 2067738ea..fac1a0be3 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -31,7 +31,39 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient const res = await next(document, token); if (res === undefined) throw new Error('busy'); return res; + }, + 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: client.code2ProtocolConverter.asCodeActionContext(context) + }; + return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => { + if (values === null) return undefined; + const result: (vscode.CodeAction | vscode.Command)[] = []; + for (const item of values) { + if (lc.CodeAction.is(item)) { + const action = client.protocol2CodeConverter.asCodeAction(item); + if (isSnippetEdit(item)) { + action.command = { + command: "rust-analyzer.applySnippetWorkspaceEdit", + title: "", + arguments: [action.edit], + }; + action.edit = undefined; + } + result.push(action); + } else { + const command = client.protocol2CodeConverter.asCommand(item); + result.push(command); + } + } + return result; + }, + (_error) => undefined + ); } + } as any }; @@ -42,7 +74,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient clientOptions, ); - // To turn on all proposed features use: res.registerProposedFeatures(); + // To turn on all proposed features use: client.registerProposedFeatures(); // Here we want to enable CallHierarchyFeature and SemanticTokensFeature // since they are available on stable. // Note that while these features are stable in vscode their LSP protocol @@ -58,8 +90,20 @@ class SnippetTextEditFeature implements lc.StaticFeature { fillClientCapabilities(capabilities: lc.ClientCapabilities): void { const caps: any = capabilities.experimental ?? {}; caps.snippetTextEdit = true; - capabilities.experimental = caps + capabilities.experimental = caps; } initialize(_capabilities: lc.ServerCapabilities, _documentSelector: lc.DocumentSelector | undefined): void { } } + +function isSnippetEdit(action: lc.CodeAction): boolean { + const documentChanges = action.edit?.documentChanges ?? []; + for (const edit of documentChanges) { + if (lc.TextDocumentEdit.is(edit)) { + if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) { + return true; + } + } + } + return false; +} diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index bdb7fc3b0..770d11bd3 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts @@ -4,6 +4,7 @@ import * as ra from '../rust-analyzer-api'; import { Ctx, Cmd } from '../ctx'; import * as sourceChange from '../source_change'; +import { assert } from '../util'; export * from './analyzer_status'; export * from './matching_brace'; @@ -51,3 +52,36 @@ export function selectAndApplySourceChange(ctx: Ctx): Cmd { } }; } + +export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd { + return async (edit: vscode.WorkspaceEdit) => { + assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); + const [uri, edits] = edit.entries()[0]; + + const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); + if (!editor) return; + + let editWithSnippet: vscode.TextEdit | undefined = undefined; + let lineDelta = 0; + await editor.edit((builder) => { + for (const indel of edits) { + if (indel.newText.indexOf('$0') !== -1) { + editWithSnippet = indel; + } else { + if (!editWithSnippet) { + lineDelta = (indel.newText.match(/\n/g) || []).length - (indel.range.end.line - indel.range.start.line); + } + builder.replace(indel.range, indel.newText); + } + } + }); + if (editWithSnippet) { + const snip = editWithSnippet as vscode.TextEdit; + const range = snip.range.with( + snip.range.start.with(snip.range.start.line + lineDelta), + snip.range.end.with(snip.range.end.line + lineDelta), + ); + await editor.insertSnippet(new vscode.SnippetString(snip.newText), range); + } + }; +} diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index c015460b8..ac3bb365e 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -91,6 +91,7 @@ export async function activate(context: vscode.ExtensionContext) { ctx.registerCommand('debugSingle', commands.debugSingle); ctx.registerCommand('showReferences', commands.showReferences); ctx.registerCommand('applySourceChange', commands.applySourceChange); + ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEdit); ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); ctx.pushCleanup(activateTaskProvider(workspaceFolder)); -- cgit v1.2.3 From 39ec581bf6e01cf2d7f33aacbe8879abf7ea3199 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 00:49:08 +0200 Subject: Fix client-side snippets --- editors/code/src/commands/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index 770d11bd3..0937b495c 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts @@ -65,7 +65,8 @@ export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd { let lineDelta = 0; await editor.edit((builder) => { for (const indel of edits) { - if (indel.newText.indexOf('$0') !== -1) { + const isSnippet = indel.newText.indexOf('$0') !== -1 || indel.newText.indexOf('${') !== -1; + if (isSnippet) { editWithSnippet = indel; } else { if (!editWithSnippet) { -- cgit v1.2.3 From d264d7b9f264c31fe2b1ea7e623611a816979789 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 20 May 2020 09:42:00 +0300 Subject: Debug lens fix for a binary. --- editors/code/src/cargo.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index 28c7de992..6a41873d0 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -51,10 +51,14 @@ 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 - if (cargoArgs[0] === "run") { - cargoArgs[0] = "build"; - } else if (cargoArgs.indexOf("--no-run") === -1) { - cargoArgs.push("--no-run"); + switch (cargoArgs[0]) { + case "run": cargoArgs[0] = "build"; break; + case "test": { + if (cargoArgs.indexOf("--no-run") === -1) { + cargoArgs.push("--no-run"); + } + break; + } } let artifacts = await this.artifactsFromArgs(cargoArgs); -- cgit v1.2.3 From 4b495da368162a5b373d078be4ff51e55bffdf69 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 May 2020 14:26:44 +0200 Subject: Transition OnEnter to WorkspaceSnippetEdit This also changes our handiling of snippet edits on the client side. `editor.insertSnippet` unfortunately forces indentation, which we really don't want to have to deal with. So, let's just implement our manual hacky way of dealing with a simple subset of snippets we actually use in rust-analyzer --- editors/code/src/commands/index.ts | 75 ++++++++++++++++++++++------------- editors/code/src/commands/on_enter.ts | 5 ++- editors/code/src/main.ts | 2 +- editors/code/src/rust-analyzer-api.ts | 2 +- 4 files changed, 53 insertions(+), 31 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index 0937b495c..e5ed77e32 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts @@ -53,36 +53,57 @@ export function selectAndApplySourceChange(ctx: Ctx): Cmd { }; } -export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd { +export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd { return async (edit: vscode.WorkspaceEdit) => { - assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); - const [uri, edits] = edit.entries()[0]; + await applySnippetWorkspaceEdit(edit); + }; +} + +export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) { + assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); + const [uri, edits] = edit.entries()[0]; - const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); - if (!editor) return; + const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); + if (!editor) return; - let editWithSnippet: vscode.TextEdit | undefined = undefined; - let lineDelta = 0; - await editor.edit((builder) => { - for (const indel of edits) { - const isSnippet = indel.newText.indexOf('$0') !== -1 || indel.newText.indexOf('${') !== -1; - if (isSnippet) { - editWithSnippet = indel; - } else { - if (!editWithSnippet) { - lineDelta = (indel.newText.match(/\n/g) || []).length - (indel.range.end.line - indel.range.start.line); - } - builder.replace(indel.range, indel.newText); - } + let selection: vscode.Selection | undefined = undefined; + let lineDelta = 0; + await editor.edit((builder) => { + for (const indel of edits) { + const parsed = parseSnippet(indel.newText); + if (parsed) { + const [newText, [placeholderStart, placeholderLength]] = parsed; + const prefix = newText.substr(0, placeholderStart); + 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 endColumn = startColumn + placeholderLength; + selection = new vscode.Selection( + new vscode.Position(startLine, startColumn), + new vscode.Position(startLine, endColumn), + ); + builder.replace(indel.range, newText); + } else { + lineDelta = countLines(indel.newText) - (indel.range.end.line - indel.range.start.line); + builder.replace(indel.range, indel.newText); } - }); - if (editWithSnippet) { - const snip = editWithSnippet as vscode.TextEdit; - const range = snip.range.with( - snip.range.start.with(snip.range.start.line + lineDelta), - snip.range.end.with(snip.range.end.line + lineDelta), - ); - await editor.insertSnippet(new vscode.SnippetString(snip.newText), range); } - }; + }); + if (selection) editor.selection = selection; +} + +function parseSnippet(snip: string): [string, [number, number]] | undefined { + const m = snip.match(/\$(0|\{0:([^}]*)\})/); + if (!m) return undefined; + const placeholder = m[2] ?? ""; + const range: [number, number] = [m.index!!, placeholder.length]; + const insert = snip.replace(m[0], placeholder); + return [insert, range]; +} + +function countLines(text: string): number { + return (text.match(/\n/g) || []).length; } diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts index 285849db7..a7871c31e 100644 --- a/editors/code/src/commands/on_enter.ts +++ b/editors/code/src/commands/on_enter.ts @@ -1,8 +1,8 @@ import * as vscode from 'vscode'; import * as ra from '../rust-analyzer-api'; -import { applySourceChange } from '../source_change'; import { Cmd, Ctx } from '../ctx'; +import { applySnippetWorkspaceEdit } from '.'; async function handleKeypress(ctx: Ctx) { const editor = ctx.activeRustEditor; @@ -21,7 +21,8 @@ async function handleKeypress(ctx: Ctx) { }); if (!change) return false; - await applySourceChange(ctx, change); + const workspaceEdit = client.protocol2CodeConverter.asWorkspaceEdit(change); + await applySnippetWorkspaceEdit(workspaceEdit); return true; } diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index ac3bb365e..8b0a9d870 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -91,7 +91,7 @@ export async function activate(context: vscode.ExtensionContext) { ctx.registerCommand('debugSingle', commands.debugSingle); ctx.registerCommand('showReferences', commands.showReferences); ctx.registerCommand('applySourceChange', commands.applySourceChange); - ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEdit); + ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); ctx.pushCleanup(activateTaskProvider(workspaceFolder)); diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts index 400ac3714..3b83b10e3 100644 --- a/editors/code/src/rust-analyzer-api.ts +++ b/editors/code/src/rust-analyzer-api.ts @@ -69,7 +69,7 @@ export interface JoinLinesParams { export const joinLines = request("joinLines"); -export const onEnter = request>("onEnter"); +export const onEnter = request>("onEnter"); export interface RunnablesParams { textDocument: lc.TextDocumentIdentifier; -- cgit v1.2.3 From 5b5ebec440841ee98a0aa70b71a135d94f5ca077 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 May 2020 19:50:23 +0200 Subject: Formalize JoinLines protocol extension --- editors/code/src/commands/join_lines.ts | 12 ++++++++---- editors/code/src/rust-analyzer-api.ts | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts index de0614653..0bf1ee6e6 100644 --- a/editors/code/src/commands/join_lines.ts +++ b/editors/code/src/commands/join_lines.ts @@ -1,7 +1,7 @@ import * as ra from '../rust-analyzer-api'; +import * as lc from 'vscode-languageclient'; import { Ctx, Cmd } from '../ctx'; -import { applySourceChange } from '../source_change'; export function joinLines(ctx: Ctx): Cmd { return async () => { @@ -9,10 +9,14 @@ export function joinLines(ctx: Ctx): Cmd { const client = ctx.client; if (!editor || !client) return; - const change = await client.sendRequest(ra.joinLines, { - range: client.code2ProtocolConverter.asRange(editor.selection), + const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, { + ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)), textDocument: { uri: editor.document.uri.toString() }, }); - await applySourceChange(ctx, change); + editor.edit((builder) => { + client.protocol2CodeConverter.asTextEdits(items).forEach((edit) => { + builder.replace(edit.range, edit.newText); + }); + }); }; } diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts index 3b83b10e3..8ed56c173 100644 --- a/editors/code/src/rust-analyzer-api.ts +++ b/editors/code/src/rust-analyzer-api.ts @@ -64,9 +64,9 @@ export const parentModule = request("joinLines"); +export const joinLines = new lc.RequestType('experimental/joinLines'); export const onEnter = request>("onEnter"); -- cgit v1.2.3