aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
Diffstat (limited to 'editors')
-rw-r--r--editors/code/package.json17
-rw-r--r--editors/code/src/client.ts7
-rw-r--r--editors/code/src/config.ts125
-rw-r--r--editors/code/src/inlay_hints.ts32
-rw-r--r--editors/code/src/main.ts16
-rw-r--r--editors/code/src/rust-analyzer-api.ts4
6 files changed, 112 insertions, 89 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index eb5748515..37e083220 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -73,10 +73,18 @@
73 "type": "string" 73 "type": "string"
74 }, 74 },
75 "args": { 75 "args": {
76 "type": "array" 76 "type": "array",
77 "items": {
78 "type": "string"
79 }
77 }, 80 },
78 "env": { 81 "env": {
79 "type": "object" 82 "type": "object",
83 "patternProperties": {
84 ".+": {
85 "type": "string"
86 }
87 }
80 } 88 }
81 } 89 }
82 } 90 }
@@ -325,6 +333,11 @@
325 "default": true, 333 "default": true,
326 "description": "Whether to show inlay type hints" 334 "description": "Whether to show inlay type hints"
327 }, 335 },
336 "rust-analyzer.inlayHints.chainingHints": {
337 "type": "boolean",
338 "default": true,
339 "description": "Whether to show inlay type hints for method chains"
340 },
328 "rust-analyzer.inlayHints.parameterHints": { 341 "rust-analyzer.inlayHints.parameterHints": {
329 "type": "boolean", 342 "type": "boolean",
330 "default": true, 343 "default": true,
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 08d821dd0..98f2f232f 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -32,6 +32,7 @@ export async function createClient(config: Config, serverPath: string): Promise<
32 32
33 inlayHintsType: config.inlayHints.typeHints, 33 inlayHintsType: config.inlayHints.typeHints,
34 inlayHintsParameter: config.inlayHints.parameterHints, 34 inlayHintsParameter: config.inlayHints.parameterHints,
35 inlayHintsChaining: config.inlayHints.chainingHints,
35 inlayHintsMaxLength: config.inlayHints.maxLength, 36 inlayHintsMaxLength: config.inlayHints.maxLength,
36 37
37 cargoWatchEnable: cargoWatchOpts.enable, 38 cargoWatchEnable: cargoWatchOpts.enable,
@@ -99,8 +100,10 @@ export async function createClient(config: Config, serverPath: string): Promise<
99 // Note that while the CallHierarchyFeature is stable the LSP protocol is not. 100 // Note that while the CallHierarchyFeature is stable the LSP protocol is not.
100 res.registerFeature(new CallHierarchyFeature(res)); 101 res.registerFeature(new CallHierarchyFeature(res));
101 102
102 if (config.highlightingSemanticTokens) { 103 if (config.package.enableProposedApi) {
103 res.registerFeature(new SemanticTokensFeature(res)); 104 if (config.highlightingSemanticTokens) {
105 res.registerFeature(new SemanticTokensFeature(res));
106 }
104 } 107 }
105 108
106 return res; 109 return res;
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index be5296fcf..637aea27d 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -1,29 +1,10 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import { log } from "./util"; 2import { log } from "./util";
3 3
4export interface InlayHintOptions {
5 typeHints: boolean;
6 parameterHints: boolean;
7 maxLength: number | null;
8}
9
10export interface CargoWatchOptions {
11 enable: boolean;
12 arguments: string[];
13 command: string;
14 allTargets: boolean;
15}
16
17export interface CargoFeatures {
18 noDefaultFeatures: boolean;
19 allFeatures: boolean;
20 features: string[];
21 loadOutDirsFromCheck: boolean;
22}
23
24export type UpdatesChannel = "stable" | "nightly"; 4export type UpdatesChannel = "stable" | "nightly";
25 5
26export const NIGHTLY_TAG = "nightly"; 6export const NIGHTLY_TAG = "nightly";
7
27export class Config { 8export class Config {
28 readonly extensionId = "matklad.rust-analyzer"; 9 readonly extensionId = "matklad.rust-analyzer";
29 10
@@ -38,37 +19,30 @@ export class Config {
38 ] 19 ]
39 .map(opt => `${this.rootSection}.${opt}`); 20 .map(opt => `${this.rootSection}.${opt}`);
40 21
41 readonly packageJsonVersion: string = vscode 22 readonly package: {
42 .extensions 23 version: string;
43 .getExtension(this.extensionId)! 24 releaseTag: string | undefined;
44 .packageJSON 25 enableProposedApi: boolean | undefined;
45 .version; 26 } = vscode.extensions.getExtension(this.extensionId)!.packageJSON;
46
47 readonly releaseTag: string | undefined = vscode
48 .extensions
49 .getExtension(this.extensionId)!
50 .packageJSON
51 .releaseTag ?? undefined;
52 27
53 private cfg!: vscode.WorkspaceConfiguration; 28 readonly globalStoragePath: string;
54 29
55 constructor(private readonly ctx: vscode.ExtensionContext) { 30 constructor(ctx: vscode.ExtensionContext) {
56 vscode.workspace.onDidChangeConfiguration(this.onConfigChange, this, ctx.subscriptions); 31 this.globalStoragePath = ctx.globalStoragePath;
57 this.refreshConfig(); 32 vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions);
33 this.refreshLogging();
58 } 34 }
59 35
60 private refreshConfig() { 36 private refreshLogging() {
61 this.cfg = vscode.workspace.getConfiguration(this.rootSection); 37 log.setEnabled(this.traceExtension);
62 const enableLogging = this.cfg.get("trace.extension") as boolean;
63 log.setEnabled(enableLogging);
64 log.debug( 38 log.debug(
65 "Extension version:", this.packageJsonVersion, 39 "Extension version:", this.package.version,
66 "using configuration:", this.cfg 40 "using configuration:", this.cfg
67 ); 41 );
68 } 42 }
69 43
70 private async onConfigChange(event: vscode.ConfigurationChangeEvent) { 44 private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) {
71 this.refreshConfig(); 45 this.refreshLogging();
72 46
73 const requiresReloadOpt = this.requiresReloadOpts.find( 47 const requiresReloadOpt = this.requiresReloadOpts.find(
74 opt => event.affectsConfiguration(opt) 48 opt => event.affectsConfiguration(opt)
@@ -86,49 +60,54 @@ export class Config {
86 } 60 }
87 } 61 }
88 62
89 get globalStoragePath(): string { return this.ctx.globalStoragePath; }
90
91 // We don't do runtime config validation here for simplicity. More on stackoverflow: 63 // We don't do runtime config validation here for simplicity. More on stackoverflow:
92 // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension 64 // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension
93 65
94 get serverPath() { return this.cfg.get("serverPath") as null | string; } 66 private get cfg(): vscode.WorkspaceConfiguration {
95 get channel() { return this.cfg.get<"stable" | "nightly">("updates.channel")!; } 67 return vscode.workspace.getConfiguration(this.rootSection);
96 get askBeforeDownload() { return this.cfg.get("updates.askBeforeDownload") as boolean; } 68 }
97 get highlightingSemanticTokens() { return this.cfg.get("highlighting.semanticTokens") as boolean; } 69
98 get highlightingOn() { return this.cfg.get("highlightingOn") as boolean; } 70 get serverPath() { return this.cfg.get<null | string>("serverPath")!; }
99 get rainbowHighlightingOn() { return this.cfg.get("rainbowHighlightingOn") as boolean; } 71 get channel() { return this.cfg.get<UpdatesChannel>("updates.channel")!; }
100 get lruCapacity() { return this.cfg.get("lruCapacity") as null | number; } 72 get askBeforeDownload() { return this.cfg.get<boolean>("updates.askBeforeDownload")!; }
101 get inlayHints(): InlayHintOptions { 73 get highlightingSemanticTokens() { return this.cfg.get<boolean>("highlighting.semanticTokens")!; }
74 get highlightingOn() { return this.cfg.get<boolean>("highlightingOn")!; }
75 get rainbowHighlightingOn() { return this.cfg.get<boolean>("rainbowHighlightingOn")!; }
76 get lruCapacity() { return this.cfg.get<null | number>("lruCapacity")!; }
77 get excludeGlobs() { return this.cfg.get<string[]>("excludeGlobs")!; }
78 get useClientWatching() { return this.cfg.get<boolean>("useClientWatching")!; }
79 get featureFlags() { return this.cfg.get<Record<string, boolean>>("featureFlags")!; }
80 get rustfmtArgs() { return this.cfg.get<string[]>("rustfmtArgs")!; }
81 get loadOutDirsFromCheck() { return this.cfg.get<boolean>("loadOutDirsFromCheck")!; }
82 get traceExtension() { return this.cfg.get<boolean>("trace.extension")!; }
83
84 // for internal use
85 get withSysroot() { return this.cfg.get<boolean>("withSysroot", true)!; }
86
87 get inlayHints() {
102 return { 88 return {
103 typeHints: this.cfg.get("inlayHints.typeHints") as boolean, 89 typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!,
104 parameterHints: this.cfg.get("inlayHints.parameterHints") as boolean, 90 parameterHints: this.cfg.get<boolean>("inlayHints.parameterHints")!,
105 maxLength: this.cfg.get("inlayHints.maxLength") as null | number, 91 chainingHints: this.cfg.get<boolean>("inlayHints.chainingHints")!,
92 maxLength: this.cfg.get<null | number>("inlayHints.maxLength")!,
106 }; 93 };
107 } 94 }
108 get excludeGlobs() { return this.cfg.get("excludeGlobs") as string[]; }
109 get useClientWatching() { return this.cfg.get("useClientWatching") as boolean; }
110 get featureFlags() { return this.cfg.get("featureFlags") as Record<string, boolean>; }
111 get rustfmtArgs() { return this.cfg.get("rustfmtArgs") as string[]; }
112 get loadOutDirsFromCheck() { return this.cfg.get("loadOutDirsFromCheck") as boolean; }
113 95
114 get cargoWatchOptions(): CargoWatchOptions { 96 get cargoWatchOptions() {
115 return { 97 return {
116 enable: this.cfg.get("cargo-watch.enable") as boolean, 98 enable: this.cfg.get<boolean>("cargo-watch.enable")!,
117 arguments: this.cfg.get("cargo-watch.arguments") as string[], 99 arguments: this.cfg.get<string[]>("cargo-watch.arguments")!,
118 allTargets: this.cfg.get("cargo-watch.allTargets") as boolean, 100 allTargets: this.cfg.get<boolean>("cargo-watch.allTargets")!,
119 command: this.cfg.get("cargo-watch.command") as string, 101 command: this.cfg.get<string>("cargo-watch.command")!,
120 }; 102 };
121 } 103 }
122 104
123 get cargoFeatures(): CargoFeatures { 105 get cargoFeatures() {
124 return { 106 return {
125 noDefaultFeatures: this.cfg.get("cargoFeatures.noDefaultFeatures") as boolean, 107 noDefaultFeatures: this.cfg.get<boolean>("cargoFeatures.noDefaultFeatures")!,
126 allFeatures: this.cfg.get("cargoFeatures.allFeatures") as boolean, 108 allFeatures: this.cfg.get<boolean>("cargoFeatures.allFeatures")!,
127 features: this.cfg.get("cargoFeatures.features") as string[], 109 features: this.cfg.get<string[]>("cargoFeatures.features")!,
128 loadOutDirsFromCheck: this.cfg.get("cargoFeatures.loadOutDirsFromCheck") as boolean, 110 loadOutDirsFromCheck: this.cfg.get<boolean>("cargoFeatures.loadOutDirsFromCheck")!,
129 }; 111 };
130 } 112 }
131
132 // for internal use
133 get withSysroot() { return this.cfg.get("withSysroot", true) as boolean; }
134} 113}
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 17d0dfa33..542d1f367 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -10,7 +10,11 @@ export function activateInlayHints(ctx: Ctx) {
10 const maybeUpdater = { 10 const maybeUpdater = {
11 updater: null as null | HintsUpdater, 11 updater: null as null | HintsUpdater,
12 onConfigChange() { 12 onConfigChange() {
13 if (!ctx.config.inlayHints.typeHints && !ctx.config.inlayHints.parameterHints) { 13 if (
14 !ctx.config.inlayHints.typeHints &&
15 !ctx.config.inlayHints.parameterHints &&
16 !ctx.config.inlayHints.chainingHints
17 ) {
14 return this.dispose(); 18 return this.dispose();
15 } 19 }
16 if (!this.updater) this.updater = new HintsUpdater(ctx); 20 if (!this.updater) this.updater = new HintsUpdater(ctx);
@@ -63,6 +67,22 @@ const paramHints = {
63 } 67 }
64}; 68};
65 69
70const chainingHints = {
71 decorationType: vscode.window.createTextEditorDecorationType({
72 after: {
73 color: new vscode.ThemeColor('rust_analyzer.inlayHint'),
74 fontStyle: "normal",
75 }
76 }),
77
78 toDecoration(hint: ra.InlayHint.ChainingHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions {
79 return {
80 range: conv.asRange(hint.range),
81 renderOptions: { after: { contentText: ` ${hint.label}` } }
82 };
83 }
84};
85
66class HintsUpdater implements Disposable { 86class HintsUpdater implements Disposable {
67 private sourceFiles = new Map<string, RustSourceFile>(); // map Uri -> RustSourceFile 87 private sourceFiles = new Map<string, RustSourceFile>(); // map Uri -> RustSourceFile
68 private readonly disposables: Disposable[] = []; 88 private readonly disposables: Disposable[] = [];
@@ -95,7 +115,7 @@ class HintsUpdater implements Disposable {
95 115
96 dispose() { 116 dispose() {
97 this.sourceFiles.forEach(file => file.inlaysRequest?.cancel()); 117 this.sourceFiles.forEach(file => file.inlaysRequest?.cancel());
98 this.ctx.visibleRustEditors.forEach(editor => this.renderDecorations(editor, { param: [], type: [] })); 118 this.ctx.visibleRustEditors.forEach(editor => this.renderDecorations(editor, { param: [], type: [], chaining: [] }));
99 this.disposables.forEach(d => d.dispose()); 119 this.disposables.forEach(d => d.dispose());
100 } 120 }
101 121
@@ -154,10 +174,11 @@ class HintsUpdater implements Disposable {
154 private renderDecorations(editor: RustEditor, decorations: InlaysDecorations) { 174 private renderDecorations(editor: RustEditor, decorations: InlaysDecorations) {
155 editor.setDecorations(typeHints.decorationType, decorations.type); 175 editor.setDecorations(typeHints.decorationType, decorations.type);
156 editor.setDecorations(paramHints.decorationType, decorations.param); 176 editor.setDecorations(paramHints.decorationType, decorations.param);
177 editor.setDecorations(chainingHints.decorationType, decorations.chaining);
157 } 178 }
158 179
159 private hintsToDecorations(hints: ra.InlayHint[]): InlaysDecorations { 180 private hintsToDecorations(hints: ra.InlayHint[]): InlaysDecorations {
160 const decorations: InlaysDecorations = { type: [], param: [] }; 181 const decorations: InlaysDecorations = { type: [], param: [], chaining: [] };
161 const conv = this.ctx.client.protocol2CodeConverter; 182 const conv = this.ctx.client.protocol2CodeConverter;
162 183
163 for (const hint of hints) { 184 for (const hint of hints) {
@@ -170,6 +191,10 @@ class HintsUpdater implements Disposable {
170 decorations.param.push(paramHints.toDecoration(hint, conv)); 191 decorations.param.push(paramHints.toDecoration(hint, conv));
171 continue; 192 continue;
172 } 193 }
194 case ra.InlayHint.Kind.ChainingHint: {
195 decorations.chaining.push(chainingHints.toDecoration(hint, conv));
196 continue;
197 }
173 } 198 }
174 } 199 }
175 return decorations; 200 return decorations;
@@ -196,6 +221,7 @@ class HintsUpdater implements Disposable {
196interface InlaysDecorations { 221interface InlaysDecorations {
197 type: vscode.DecorationOptions[]; 222 type: vscode.DecorationOptions[];
198 param: vscode.DecorationOptions[]; 223 param: vscode.DecorationOptions[];
224 chaining: vscode.DecorationOptions[];
199} 225}
200 226
201interface RustSourceFile { 227interface RustSourceFile {
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 5d2da9a76..de27d9535 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -110,9 +110,9 @@ async function bootstrap(config: Config, state: PersistentState): Promise<string
110} 110}
111 111
112async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> { 112async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> {
113 if (config.releaseTag === undefined) return; 113 if (config.package.releaseTag === undefined) return;
114 if (config.channel === "stable") { 114 if (config.channel === "stable") {
115 if (config.releaseTag === NIGHTLY_TAG) { 115 if (config.package.releaseTag === NIGHTLY_TAG) {
116 vscode.window.showWarningMessage(`You are running a nightly version of rust-analyzer extension. 116 vscode.window.showWarningMessage(`You are running a nightly version of rust-analyzer extension.
117To switch to stable, uninstall the extension and re-install it from the marketplace`); 117To switch to stable, uninstall the extension and re-install it from the marketplace`);
118 } 118 }
@@ -185,10 +185,10 @@ async function getServer(config: Config, state: PersistentState): Promise<string
185 } 185 }
186 return explicitPath; 186 return explicitPath;
187 }; 187 };
188 if (config.releaseTag === undefined) return "rust-analyzer"; 188 if (config.package.releaseTag === undefined) return "rust-analyzer";
189 189
190 let binaryName: string | undefined = undefined; 190 let binaryName: string | undefined = undefined;
191 if (process.arch === "x64" || process.arch === "x32") { 191 if (process.arch === "x64" || process.arch === "ia32") {
192 if (process.platform === "linux") binaryName = "rust-analyzer-linux"; 192 if (process.platform === "linux") binaryName = "rust-analyzer-linux";
193 if (process.platform === "darwin") binaryName = "rust-analyzer-mac"; 193 if (process.platform === "darwin") binaryName = "rust-analyzer-mac";
194 if (process.platform === "win32") binaryName = "rust-analyzer-windows.exe"; 194 if (process.platform === "win32") binaryName = "rust-analyzer-windows.exe";
@@ -211,21 +211,21 @@ async function getServer(config: Config, state: PersistentState): Promise<string
211 await state.updateServerVersion(undefined); 211 await state.updateServerVersion(undefined);
212 } 212 }
213 213
214 if (state.serverVersion === config.packageJsonVersion) return dest; 214 if (state.serverVersion === config.package.version) return dest;
215 215
216 if (config.askBeforeDownload) { 216 if (config.askBeforeDownload) {
217 const userResponse = await vscode.window.showInformationMessage( 217 const userResponse = await vscode.window.showInformationMessage(
218 `Language server version ${config.packageJsonVersion} for rust-analyzer is not installed.`, 218 `Language server version ${config.package.version} for rust-analyzer is not installed.`,
219 "Download now" 219 "Download now"
220 ); 220 );
221 if (userResponse !== "Download now") return dest; 221 if (userResponse !== "Download now") return dest;
222 } 222 }
223 223
224 const release = await fetchRelease(config.releaseTag); 224 const release = await fetchRelease(config.package.releaseTag);
225 const artifact = release.assets.find(artifact => artifact.name === binaryName); 225 const artifact = release.assets.find(artifact => artifact.name === binaryName);
226 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 226 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
227 227
228 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 }); 228 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 });
229 await state.updateServerVersion(config.packageJsonVersion); 229 await state.updateServerVersion(config.package.version);
230 return dest; 230 return dest;
231} 231}
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
index 9846f7343..400ac3714 100644
--- a/editors/code/src/rust-analyzer-api.ts
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -86,12 +86,13 @@ export interface Runnable {
86} 86}
87export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables"); 87export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables");
88 88
89export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint; 89export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint;
90 90
91export namespace InlayHint { 91export namespace InlayHint {
92 export const enum Kind { 92 export const enum Kind {
93 TypeHint = "TypeHint", 93 TypeHint = "TypeHint",
94 ParamHint = "ParameterHint", 94 ParamHint = "ParameterHint",
95 ChainingHint = "ChainingHint",
95 } 96 }
96 interface Common { 97 interface Common {
97 range: lc.Range; 98 range: lc.Range;
@@ -99,6 +100,7 @@ export namespace InlayHint {
99 } 100 }
100 export type TypeHint = Common & { kind: Kind.TypeHint }; 101 export type TypeHint = Common & { kind: Kind.TypeHint };
101 export type ParamHint = Common & { kind: Kind.ParamHint }; 102 export type ParamHint = Common & { kind: Kind.ParamHint };
103 export type ChainingHint = Common & { kind: Kind.ChainingHint };
102} 104}
103export interface InlayHintsParams { 105export interface InlayHintsParams {
104 textDocument: lc.TextDocumentIdentifier; 106 textDocument: lc.TextDocumentIdentifier;