aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src')
-rw-r--r--editors/code/src/commands.ts26
-rw-r--r--editors/code/src/config.ts9
-rw-r--r--editors/code/src/debug.ts14
-rw-r--r--editors/code/src/inlay_hints.ts52
-rw-r--r--editors/code/src/lsp_ext.ts2
-rw-r--r--editors/code/src/toolchain.ts20
-rw-r--r--editors/code/src/util.ts21
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';
3import * as path from 'path'; 3import * as path from 'path';
4import * as ra from './lsp_ext'; 4import * as ra from './lsp_ext';
5 5
6import { Cargo } from './toolchain'; 6import { Cargo, getRustcId, getSysroot } from './toolchain';
7import { Ctx } from "./ctx"; 7import { Ctx } from "./ctx";
8import { prepareEnv } from "./run"; 8import { 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';
5import { Ctx, Disposable } from './ctx'; 5import { Ctx, Disposable } from './ctx';
6import { sendRequestWithRetry, isRustDocument, RustDocument, RustEditor, sleep } from './util'; 6import { sendRequestWithRetry, isRustDocument, RustDocument, RustEditor, sleep } from './util';
7 7
8interface InlayHintStyle {
9 decorationType: vscode.TextEditorDecorationType;
10 toDecoration(hint: ra.InlayHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions;
11};
12
13interface InlayHintsStyles {
14 typeHints: InlayHintStyle;
15 paramHints: InlayHintStyle;
16 chainingHints: InlayHintStyle;
17}
18
8 19
9export function activateInlayHints(ctx: Ctx) { 20export 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
42const typeHints = createHintStyle("type"); 54function createHintStyle(hintKind: "type" | "parameter" | "chaining", smallerHints: boolean): InlayHintStyle {
43const paramHints = createHintStyle("parameter");
44const chainingHints = createHintStyle("chaining");
45
46function 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
84const smallHintsStyles = {
85 typeHints: createHintStyle("type", true),
86 paramHints: createHintStyle("parameter", true),
87 chainingHints: createHintStyle("chaining", true),
88};
89
90const biggerHintsStyles = {
91 typeHints: createHintStyle("type", false),
92 paramHints: createHintStyle("parameter", false),
93 chainingHints: createHintStyle("chaining", false),
94};
95
76class HintsUpdater implements Disposable { 96class 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
132export const moveItem = new lc.RequestType<MoveItemParams, lc.TextDocumentEdit | void, void>("experimental/moveItem"); 132export const moveItem = new lc.RequestType<MoveItemParams, lc.TextEdit[], void>("experimental/moveItem");
133 133
134export interface MoveItemParams { 134export 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';
4import * as fs from 'fs'; 4import * as fs from 'fs';
5import * as readline from 'readline'; 5import * as readline from 'readline';
6import { OutputChannel } from 'vscode'; 6import { OutputChannel } from 'vscode';
7import { log, memoize } from './util'; 7import { execute, log, memoize } from './util';
8 8
9interface CompilationArtifact { 9interface 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*/
125export 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
132export 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 */
125export function cargoPath(): string { 143export 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 @@
1import * as lc from "vscode-languageclient/node"; 1import * as lc from "vscode-languageclient/node";
2import * as vscode from "vscode"; 2import * as vscode from "vscode";
3import { strict as nativeAssert } from "assert"; 3import { strict as nativeAssert } from "assert";
4import { spawnSync } from "child_process"; 4import { exec, ExecOptions, spawnSync } from "child_process";
5import { inspect } from "util"; 5import { inspect } from "util";
6 6
7export function assert(condition: boolean, explanation: string): asserts condition { 7export 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` */
146export 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}