diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-24 23:28:57 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-24 23:28:57 +0000 |
commit | 6ad1a0711631d8017791a6dfe85bbe205d6c7414 (patch) | |
tree | ce2d6448ff8770c8fe73086eefb85dab38d298d8 /editors/code/src | |
parent | fae627174aecae0b4f4d2c087a856eda1a97a1ac (diff) | |
parent | 7b35da04bf56a5461321a6dca515dcd29f44b57f (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/src')
-rw-r--r-- | editors/code/src/client.ts | 1 | ||||
-rw-r--r-- | editors/code/src/config.ts | 1 | ||||
-rw-r--r-- | editors/code/src/inlay_hints.ts | 32 | ||||
-rw-r--r-- | editors/code/src/rust-analyzer-api.ts | 4 |
4 files changed, 34 insertions, 4 deletions
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 | ||
70 | const 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 | |||
66 | class HintsUpdater implements Disposable { | 86 | class 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 { | |||
196 | interface InlaysDecorations { | 221 | interface 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 | ||
201 | interface RustSourceFile { | 227 | interface 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 | } |
87 | export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables"); | 87 | export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables"); |
88 | 88 | ||
89 | export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint; | 89 | export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint; |
90 | 90 | ||
91 | export namespace InlayHint { | 91 | export 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 | } |
103 | export interface InlayHintsParams { | 105 | export interface InlayHintsParams { |
104 | textDocument: lc.TextDocumentIdentifier; | 106 | textDocument: lc.TextDocumentIdentifier; |