diff options
Diffstat (limited to 'editors/code/src')
-rw-r--r-- | editors/code/src/commands.ts | 26 | ||||
-rw-r--r-- | editors/code/src/config.ts | 9 | ||||
-rw-r--r-- | editors/code/src/debug.ts | 14 | ||||
-rw-r--r-- | editors/code/src/inlay_hints.ts | 52 | ||||
-rw-r--r-- | editors/code/src/lsp_ext.ts | 2 | ||||
-rw-r--r-- | editors/code/src/toolchain.ts | 20 | ||||
-rw-r--r-- | editors/code/src/util.ts | 21 |
7 files changed, 109 insertions, 35 deletions
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 1a0805bd3..4092435db 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -148,34 +148,16 @@ export function moveItem(ctx: Ctx, direction: ra.Direction): Cmd { | |||
148 | const client = ctx.client; | 148 | const client = ctx.client; |
149 | if (!editor || !client) return; | 149 | if (!editor || !client) return; |
150 | 150 | ||
151 | const edit = await client.sendRequest(ra.moveItem, { | 151 | const lcEdits = await client.sendRequest(ra.moveItem, { |
152 | range: client.code2ProtocolConverter.asRange(editor.selection), | 152 | range: client.code2ProtocolConverter.asRange(editor.selection), |
153 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), | 153 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), |
154 | direction | 154 | direction |
155 | }); | 155 | }); |
156 | 156 | ||
157 | if (!edit) return; | 157 | if (!lcEdits) return; |
158 | 158 | ||
159 | let cursor: vscode.Position | null = null; | 159 | const edits = client.protocol2CodeConverter.asTextEdits(lcEdits); |
160 | 160 | await applySnippetTextEdits(editor, edits); | |
161 | await editor.edit((builder) => { | ||
162 | client.protocol2CodeConverter.asTextEdits(edit.edits).forEach((edit: any) => { | ||
163 | builder.replace(edit.range, edit.newText); | ||
164 | |||
165 | if (direction === ra.Direction.Up) { | ||
166 | if (!cursor || edit.range.end.isBeforeOrEqual(cursor)) { | ||
167 | cursor = edit.range.end; | ||
168 | } | ||
169 | } else { | ||
170 | if (!cursor || edit.range.end.isAfterOrEqual(cursor)) { | ||
171 | cursor = edit.range.end; | ||
172 | } | ||
173 | } | ||
174 | }); | ||
175 | }).then(() => { | ||
176 | const newPosition = cursor ?? editor.selection.start; | ||
177 | editor.selection = new vscode.Selection(newPosition, newPosition); | ||
178 | }); | ||
179 | }; | 161 | }; |
180 | } | 162 | } |
181 | 163 | ||
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 82f0a0566..e858f80bc 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -115,6 +115,7 @@ export class Config { | |||
115 | typeHints: this.get<boolean>("inlayHints.typeHints"), | 115 | typeHints: this.get<boolean>("inlayHints.typeHints"), |
116 | parameterHints: this.get<boolean>("inlayHints.parameterHints"), | 116 | parameterHints: this.get<boolean>("inlayHints.parameterHints"), |
117 | chainingHints: this.get<boolean>("inlayHints.chainingHints"), | 117 | chainingHints: this.get<boolean>("inlayHints.chainingHints"), |
118 | smallerHints: this.get<boolean>("inlayHints.smallerHints"), | ||
118 | maxLength: this.get<null | number>("inlayHints.maxLength"), | 119 | maxLength: this.get<null | number>("inlayHints.maxLength"), |
119 | }; | 120 | }; |
120 | } | 121 | } |
@@ -134,8 +135,12 @@ export class Config { | |||
134 | } | 135 | } |
135 | 136 | ||
136 | get debug() { | 137 | get debug() { |
137 | // "/rustc/<id>" used by suggestions only. | 138 | let sourceFileMap = this.get<Record<string, string> | "auto">("debug.sourceFileMap"); |
138 | const { ["/rustc/<id>"]: _, ...sourceFileMap } = this.get<Record<string, string>>("debug.sourceFileMap"); | 139 | if (sourceFileMap !== "auto") { |
140 | // "/rustc/<id>" used by suggestions only. | ||
141 | const { ["/rustc/<id>"]: _, ...trimmed } = this.get<Record<string, string>>("debug.sourceFileMap"); | ||
142 | sourceFileMap = trimmed; | ||
143 | } | ||
139 | 144 | ||
140 | return { | 145 | return { |
141 | engine: this.get<string>("debug.engine"), | 146 | engine: this.get<string>("debug.engine"), |
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 3889a2773..830980f96 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts | |||
@@ -3,7 +3,7 @@ import * as vscode from 'vscode'; | |||
3 | import * as path from 'path'; | 3 | import * as path from 'path'; |
4 | import * as ra from './lsp_ext'; | 4 | import * as ra from './lsp_ext'; |
5 | 5 | ||
6 | import { Cargo } from './toolchain'; | 6 | import { Cargo, getRustcId, getSysroot } from './toolchain'; |
7 | import { Ctx } from "./ctx"; | 7 | import { Ctx } from "./ctx"; |
8 | import { prepareEnv } from "./run"; | 8 | import { prepareEnv } from "./run"; |
9 | 9 | ||
@@ -104,7 +104,17 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v | |||
104 | 104 | ||
105 | const executable = await getDebugExecutable(runnable); | 105 | const executable = await getDebugExecutable(runnable); |
106 | const env = prepareEnv(runnable, ctx.config.runnableEnv); | 106 | const env = prepareEnv(runnable, ctx.config.runnableEnv); |
107 | const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), env, debugOptions.sourceFileMap); | 107 | let sourceFileMap = debugOptions.sourceFileMap; |
108 | if (sourceFileMap === "auto") { | ||
109 | // let's try to use the default toolchain | ||
110 | const commitHash = await getRustcId(wsFolder); | ||
111 | const sysroot = await getSysroot(wsFolder); | ||
112 | const rustlib = path.normalize(sysroot + "/lib/rustlib/src/rust"); | ||
113 | sourceFileMap = {}; | ||
114 | sourceFileMap[`/rustc/${commitHash}/`] = rustlib; | ||
115 | } | ||
116 | |||
117 | const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), env, sourceFileMap); | ||
108 | if (debugConfig.type in debugOptions.engineSettings) { | 118 | if (debugConfig.type in debugOptions.engineSettings) { |
109 | const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; | 119 | const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; |
110 | for (var key in settingsMap) { | 120 | for (var key in settingsMap) { |
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index 61db6b8d0..c23d6f738 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts | |||
@@ -5,6 +5,17 @@ import * as ra from './lsp_ext'; | |||
5 | import { Ctx, Disposable } from './ctx'; | 5 | import { Ctx, Disposable } from './ctx'; |
6 | import { sendRequestWithRetry, isRustDocument, RustDocument, RustEditor, sleep } from './util'; | 6 | import { sendRequestWithRetry, isRustDocument, RustDocument, RustEditor, sleep } from './util'; |
7 | 7 | ||
8 | interface InlayHintStyle { | ||
9 | decorationType: vscode.TextEditorDecorationType; | ||
10 | toDecoration(hint: ra.InlayHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions; | ||
11 | }; | ||
12 | |||
13 | interface InlayHintsStyles { | ||
14 | typeHints: InlayHintStyle; | ||
15 | paramHints: InlayHintStyle; | ||
16 | chainingHints: InlayHintStyle; | ||
17 | } | ||
18 | |||
8 | 19 | ||
9 | export function activateInlayHints(ctx: Ctx) { | 20 | export function activateInlayHints(ctx: Ctx) { |
10 | const maybeUpdater = { | 21 | const maybeUpdater = { |
@@ -19,6 +30,7 @@ export function activateInlayHints(ctx: Ctx) { | |||
19 | 30 | ||
20 | await sleep(100); | 31 | await sleep(100); |
21 | if (this.updater) { | 32 | if (this.updater) { |
33 | this.updater.updateInlayHintsStyles(); | ||
22 | this.updater.syncCacheAndRenderHints(); | 34 | this.updater.syncCacheAndRenderHints(); |
23 | } else { | 35 | } else { |
24 | this.updater = new HintsUpdater(ctx); | 36 | this.updater = new HintsUpdater(ctx); |
@@ -39,11 +51,7 @@ export function activateInlayHints(ctx: Ctx) { | |||
39 | maybeUpdater.onConfigChange().catch(console.error); | 51 | maybeUpdater.onConfigChange().catch(console.error); |
40 | } | 52 | } |
41 | 53 | ||
42 | const typeHints = createHintStyle("type"); | 54 | function createHintStyle(hintKind: "type" | "parameter" | "chaining", smallerHints: boolean): InlayHintStyle { |
43 | const paramHints = createHintStyle("parameter"); | ||
44 | const chainingHints = createHintStyle("chaining"); | ||
45 | |||
46 | function createHintStyle(hintKind: "type" | "parameter" | "chaining") { | ||
47 | // U+200C is a zero-width non-joiner to prevent the editor from forming a ligature | 55 | // U+200C is a zero-width non-joiner to prevent the editor from forming a ligature |
48 | // between code and type hints | 56 | // between code and type hints |
49 | const [pos, render] = ({ | 57 | const [pos, render] = ({ |
@@ -61,7 +69,7 @@ function createHintStyle(hintKind: "type" | "parameter" | "chaining") { | |||
61 | backgroundColor: bg, | 69 | backgroundColor: bg, |
62 | fontStyle: "normal", | 70 | fontStyle: "normal", |
63 | fontWeight: "normal", | 71 | fontWeight: "normal", |
64 | textDecoration: ";font-size:smaller", | 72 | textDecoration: smallerHints ? ";font-size:smaller" : "none", |
65 | }, | 73 | }, |
66 | }), | 74 | }), |
67 | toDecoration(hint: ra.InlayHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions { | 75 | toDecoration(hint: ra.InlayHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions { |
@@ -73,9 +81,23 @@ function createHintStyle(hintKind: "type" | "parameter" | "chaining") { | |||
73 | }; | 81 | }; |
74 | } | 82 | } |
75 | 83 | ||
84 | const smallHintsStyles = { | ||
85 | typeHints: createHintStyle("type", true), | ||
86 | paramHints: createHintStyle("parameter", true), | ||
87 | chainingHints: createHintStyle("chaining", true), | ||
88 | }; | ||
89 | |||
90 | const biggerHintsStyles = { | ||
91 | typeHints: createHintStyle("type", false), | ||
92 | paramHints: createHintStyle("parameter", false), | ||
93 | chainingHints: createHintStyle("chaining", false), | ||
94 | }; | ||
95 | |||
76 | class HintsUpdater implements Disposable { | 96 | class HintsUpdater implements Disposable { |
77 | private sourceFiles = new Map<string, RustSourceFile>(); // map Uri -> RustSourceFile | 97 | private sourceFiles = new Map<string, RustSourceFile>(); // map Uri -> RustSourceFile |
78 | private readonly disposables: Disposable[] = []; | 98 | private readonly disposables: Disposable[] = []; |
99 | private pendingDisposeDecorations: undefined | InlayHintsStyles = undefined; | ||
100 | private inlayHintsStyles!: InlayHintsStyles; | ||
79 | 101 | ||
80 | constructor(private readonly ctx: Ctx) { | 102 | constructor(private readonly ctx: Ctx) { |
81 | vscode.window.onDidChangeVisibleTextEditors( | 103 | vscode.window.onDidChangeVisibleTextEditors( |
@@ -100,6 +122,7 @@ class HintsUpdater implements Disposable { | |||
100 | } | 122 | } |
101 | )); | 123 | )); |
102 | 124 | ||
125 | this.updateInlayHintsStyles(); | ||
103 | this.syncCacheAndRenderHints(); | 126 | this.syncCacheAndRenderHints(); |
104 | } | 127 | } |
105 | 128 | ||
@@ -114,6 +137,15 @@ class HintsUpdater implements Disposable { | |||
114 | this.syncCacheAndRenderHints(); | 137 | this.syncCacheAndRenderHints(); |
115 | } | 138 | } |
116 | 139 | ||
140 | updateInlayHintsStyles() { | ||
141 | const inlayHintsStyles = this.ctx.config.inlayHints.smallerHints ? smallHintsStyles : biggerHintsStyles; | ||
142 | |||
143 | if (inlayHintsStyles !== this.inlayHintsStyles) { | ||
144 | this.pendingDisposeDecorations = this.inlayHintsStyles; | ||
145 | this.inlayHintsStyles = inlayHintsStyles; | ||
146 | } | ||
147 | } | ||
148 | |||
117 | syncCacheAndRenderHints() { | 149 | syncCacheAndRenderHints() { |
118 | this.sourceFiles.forEach((file, uri) => this.fetchHints(file).then(hints => { | 150 | this.sourceFiles.forEach((file, uri) => this.fetchHints(file).then(hints => { |
119 | if (!hints) return; | 151 | if (!hints) return; |
@@ -161,12 +193,20 @@ class HintsUpdater implements Disposable { | |||
161 | } | 193 | } |
162 | 194 | ||
163 | private renderDecorations(editor: RustEditor, decorations: InlaysDecorations) { | 195 | private renderDecorations(editor: RustEditor, decorations: InlaysDecorations) { |
196 | const { typeHints, paramHints, chainingHints } = this.inlayHintsStyles; | ||
197 | if (this.pendingDisposeDecorations !== undefined) { | ||
198 | const { typeHints, paramHints, chainingHints } = this.pendingDisposeDecorations; | ||
199 | editor.setDecorations(typeHints.decorationType, []); | ||
200 | editor.setDecorations(paramHints.decorationType, []); | ||
201 | editor.setDecorations(chainingHints.decorationType, []); | ||
202 | } | ||
164 | editor.setDecorations(typeHints.decorationType, decorations.type); | 203 | editor.setDecorations(typeHints.decorationType, decorations.type); |
165 | editor.setDecorations(paramHints.decorationType, decorations.param); | 204 | editor.setDecorations(paramHints.decorationType, decorations.param); |
166 | editor.setDecorations(chainingHints.decorationType, decorations.chaining); | 205 | editor.setDecorations(chainingHints.decorationType, decorations.chaining); |
167 | } | 206 | } |
168 | 207 | ||
169 | private hintsToDecorations(hints: ra.InlayHint[]): InlaysDecorations { | 208 | private hintsToDecorations(hints: ra.InlayHint[]): InlaysDecorations { |
209 | const { typeHints, paramHints, chainingHints } = this.inlayHintsStyles; | ||
170 | const decorations: InlaysDecorations = { type: [], param: [], chaining: [] }; | 210 | const decorations: InlaysDecorations = { type: [], param: [], chaining: [] }; |
171 | const conv = this.ctx.client.protocol2CodeConverter; | 211 | const conv = this.ctx.client.protocol2CodeConverter; |
172 | 212 | ||
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index e453bb9e0..f78de894b 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -129,7 +129,7 @@ export interface OpenCargoTomlParams { | |||
129 | textDocument: lc.TextDocumentIdentifier; | 129 | textDocument: lc.TextDocumentIdentifier; |
130 | } | 130 | } |
131 | 131 | ||
132 | export const moveItem = new lc.RequestType<MoveItemParams, lc.TextDocumentEdit | void, void>("experimental/moveItem"); | 132 | export const moveItem = new lc.RequestType<MoveItemParams, lc.TextEdit[], void>("experimental/moveItem"); |
133 | 133 | ||
134 | export interface MoveItemParams { | 134 | export interface MoveItemParams { |
135 | textDocument: lc.TextDocumentIdentifier; | 135 | textDocument: lc.TextDocumentIdentifier; |
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index a5dc3cf0c..68826c478 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts | |||
@@ -4,7 +4,7 @@ import * as path from 'path'; | |||
4 | import * as fs from 'fs'; | 4 | import * as fs from 'fs'; |
5 | import * as readline from 'readline'; | 5 | import * as readline from 'readline'; |
6 | import { OutputChannel } from 'vscode'; | 6 | import { OutputChannel } from 'vscode'; |
7 | import { log, memoize } from './util'; | 7 | import { execute, log, memoize } from './util'; |
8 | 8 | ||
9 | interface CompilationArtifact { | 9 | interface CompilationArtifact { |
10 | fileName: string; | 10 | fileName: string; |
@@ -121,6 +121,24 @@ export class Cargo { | |||
121 | } | 121 | } |
122 | } | 122 | } |
123 | 123 | ||
124 | /** Mirrors `project_model::sysroot::discover_sysroot_dir()` implementation*/ | ||
125 | export function getSysroot(dir: string): Promise<string> { | ||
126 | const rustcPath = getPathForExecutable("rustc"); | ||
127 | |||
128 | // do not memoize the result because the toolchain may change between runs | ||
129 | return execute(`${rustcPath} --print sysroot`, { cwd: dir }); | ||
130 | } | ||
131 | |||
132 | export async function getRustcId(dir: string): Promise<string> { | ||
133 | const rustcPath = getPathForExecutable("rustc"); | ||
134 | |||
135 | // do not memoize the result because the toolchain may change between runs | ||
136 | const data = await execute(`${rustcPath} -V -v`, { cwd: dir }); | ||
137 | const rx = /commit-hash:\s(.*)$/m.compile(); | ||
138 | |||
139 | return rx.exec(data)![1]; | ||
140 | } | ||
141 | |||
124 | /** Mirrors `toolchain::cargo()` implementation */ | 142 | /** Mirrors `toolchain::cargo()` implementation */ |
125 | export function cargoPath(): string { | 143 | export function cargoPath(): string { |
126 | return getPathForExecutable("cargo"); | 144 | return getPathForExecutable("cargo"); |
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 53492a445..56e0e439e 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as lc from "vscode-languageclient/node"; | 1 | import * as lc from "vscode-languageclient/node"; |
2 | import * as vscode from "vscode"; | 2 | import * as vscode from "vscode"; |
3 | import { strict as nativeAssert } from "assert"; | 3 | import { strict as nativeAssert } from "assert"; |
4 | import { spawnSync } from "child_process"; | 4 | import { exec, ExecOptions, spawnSync } from "child_process"; |
5 | import { inspect } from "util"; | 5 | import { inspect } from "util"; |
6 | 6 | ||
7 | export function assert(condition: boolean, explanation: string): asserts condition { | 7 | export function assert(condition: boolean, explanation: string): asserts condition { |
@@ -141,3 +141,22 @@ export function memoize<Ret, TThis, Param extends string>(func: (this: TThis, ar | |||
141 | return result; | 141 | return result; |
142 | }; | 142 | }; |
143 | } | 143 | } |
144 | |||
145 | /** Awaitable wrapper around `child_process.exec` */ | ||
146 | export function execute(command: string, options: ExecOptions): Promise<string> { | ||
147 | return new Promise((resolve, reject) => { | ||
148 | exec(command, options, (err, stdout, stderr) => { | ||
149 | if (err) { | ||
150 | reject(err); | ||
151 | return; | ||
152 | } | ||
153 | |||
154 | if (stderr) { | ||
155 | reject(new Error(stderr)); | ||
156 | return; | ||
157 | } | ||
158 | |||
159 | resolve(stdout.trimEnd()); | ||
160 | }); | ||
161 | }); | ||
162 | } | ||