aboutsummaryrefslogtreecommitdiff
path: root/editors/code
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-03-24 23:28:57 +0000
committerGitHub <[email protected]>2020-03-24 23:28:57 +0000
commit6ad1a0711631d8017791a6dfe85bbe205d6c7414 (patch)
treece2d6448ff8770c8fe73086eefb85dab38d298d8 /editors/code
parentfae627174aecae0b4f4d2c087a856eda1a97a1ac (diff)
parent7b35da04bf56a5461321a6dca515dcd29f44b57f (diff)
Merge #3710
3710: Inlay hints for method chaining pattern r=matklad a=M-J-Hooper This PR adds inlay hints on method call chains: ![image](https://user-images.githubusercontent.com/13765376/77472008-8dc2a880-6e13-11ea-9c18-2c2e2b809799.png) It is not only explicit `MethodCall`s where this can be helpful. The heuristic used here is that whenever any expression is followed by a new line and then a dot, it resembles a call chain and type information can be #useful. Changes: - A new `InlayKind` for chaining. - New option for disabling this type of hints. - Tree traversal rules for identifying the chaining hints. - VSCode decorators in the extension layer (and associated types). Notes: - IntelliJ has additional rules and configuration on this topic. Eg. minimum length of chain to start displaying hints and only displaying distinct types in the chain. - I am checking for chaining on every `ast::Expr` in the tree; Are there performance concerns there? This is my first contribution (to RA and to Rust in general) so would appreciate any feedback. The only issue I can find the references this feature is #2741. Co-authored-by: Matt Hooper <[email protected]>
Diffstat (limited to 'editors/code')
-rw-r--r--editors/code/package.json5
-rw-r--r--editors/code/src/client.ts1
-rw-r--r--editors/code/src/config.ts1
-rw-r--r--editors/code/src/inlay_hints.ts32
-rw-r--r--editors/code/src/rust-analyzer-api.ts4
5 files changed, 39 insertions, 4 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index 1d113ebb6..37e083220 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -333,6 +333,11 @@
333 "default": true, 333 "default": true,
334 "description": "Whether to show inlay type hints" 334 "description": "Whether to show inlay type hints"
335 }, 335 },
336 "rust-analyzer.inlayHints.chainingHints": {
337 "type": "boolean",
338 "default": true,
339 "description": "Whether to show inlay type hints for method chains"
340 },
336 "rust-analyzer.inlayHints.parameterHints": { 341 "rust-analyzer.inlayHints.parameterHints": {
337 "type": "boolean", 342 "type": "boolean",
338 "default": true, 343 "default": true,
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 82ca749f3..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,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 7668c20b7..637aea27d 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -88,6 +88,7 @@ export class Config {
88 return { 88 return {
89 typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!, 89 typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!,
90 parameterHints: this.cfg.get<boolean>("inlayHints.parameterHints")!, 90 parameterHints: this.cfg.get<boolean>("inlayHints.parameterHints")!,
91 chainingHints: this.cfg.get<boolean>("inlayHints.chainingHints")!,
91 maxLength: this.cfg.get<null | number>("inlayHints.maxLength")!, 92 maxLength: this.cfg.get<null | number>("inlayHints.maxLength")!,
92 }; 93 };
93 } 94 }
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/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;