diff options
-rw-r--r-- | crates/ra_ide/src/completion/complete_postfix.rs | 140 | ||||
-rw-r--r-- | editors/code/package.json | 12 | ||||
-rw-r--r-- | editors/code/src/client.ts | 6 | ||||
-rw-r--r-- | editors/code/src/config.ts | 18 | ||||
-rw-r--r-- | editors/code/src/main.ts | 14 |
5 files changed, 150 insertions, 40 deletions
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 0ba382165..0a00054b2 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_syntax::{ast::AstNode, TextRange, TextUnit}; | 3 | use ra_syntax::{ |
4 | ast::{self, AstNode}, | ||
5 | TextRange, TextUnit, | ||
6 | }; | ||
4 | use ra_text_edit::TextEdit; | 7 | use ra_text_edit::TextEdit; |
5 | 8 | ||
6 | use crate::{ | 9 | use crate::{ |
@@ -21,13 +24,8 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
21 | None => return, | 24 | None => return, |
22 | }; | 25 | }; |
23 | 26 | ||
24 | let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal { | 27 | let receiver_text = |
25 | let text = dot_receiver.syntax().text(); | 28 | get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal); |
26 | let without_dot = ..text.len() - TextUnit::of_char('.'); | ||
27 | text.slice(without_dot).to_string() | ||
28 | } else { | ||
29 | dot_receiver.syntax().text().to_string() | ||
30 | }; | ||
31 | 29 | ||
32 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { | 30 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { |
33 | Some(it) => it, | 31 | Some(it) => it, |
@@ -35,10 +33,17 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
35 | }; | 33 | }; |
36 | 34 | ||
37 | if receiver_ty.is_bool() || receiver_ty.is_unknown() { | 35 | if receiver_ty.is_bool() || receiver_ty.is_unknown() { |
38 | postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text)) | ||
39 | .add_to(acc); | ||
40 | postfix_snippet( | 36 | postfix_snippet( |
41 | ctx, | 37 | ctx, |
38 | &dot_receiver, | ||
39 | "if", | ||
40 | "if expr {}", | ||
41 | &format!("if {} {{$0}}", receiver_text), | ||
42 | ) | ||
43 | .add_to(acc); | ||
44 | postfix_snippet( | ||
45 | ctx, | ||
46 | &dot_receiver, | ||
42 | "while", | 47 | "while", |
43 | "while expr {}", | 48 | "while expr {}", |
44 | &format!("while {} {{\n$0\n}}", receiver_text), | 49 | &format!("while {} {{\n$0\n}}", receiver_text), |
@@ -46,28 +51,70 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
46 | .add_to(acc); | 51 | .add_to(acc); |
47 | } | 52 | } |
48 | 53 | ||
49 | postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); | 54 | // !&&&42 is a compiler error, ergo process it before considering the references |
55 | postfix_snippet(ctx, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); | ||
50 | 56 | ||
51 | postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); | 57 | postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); |
52 | postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc); | 58 | postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text)) |
59 | .add_to(acc); | ||
60 | |||
61 | // The rest of the postfix completions create an expression that moves an argument, | ||
62 | // so it's better to consider references now to avoid breaking the compilation | ||
63 | let dot_receiver = include_references(dot_receiver); | ||
64 | let receiver_text = | ||
65 | get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal); | ||
53 | 66 | ||
54 | postfix_snippet( | 67 | postfix_snippet( |
55 | ctx, | 68 | ctx, |
69 | &dot_receiver, | ||
56 | "match", | 70 | "match", |
57 | "match expr {}", | 71 | "match expr {}", |
58 | &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), | 72 | &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), |
59 | ) | 73 | ) |
60 | .add_to(acc); | 74 | .add_to(acc); |
61 | 75 | ||
62 | postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); | 76 | postfix_snippet( |
77 | ctx, | ||
78 | &dot_receiver, | ||
79 | "box", | ||
80 | "Box::new(expr)", | ||
81 | &format!("Box::new({})", receiver_text), | ||
82 | ) | ||
83 | .add_to(acc); | ||
63 | 84 | ||
64 | postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text)) | 85 | postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)) |
65 | .add_to(acc); | 86 | .add_to(acc); |
66 | } | 87 | } |
67 | 88 | ||
68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { | 89 | fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { |
90 | if receiver_is_ambiguous_float_literal { | ||
91 | let text = receiver.syntax().text(); | ||
92 | let without_dot = ..text.len() - TextUnit::of_char('.'); | ||
93 | text.slice(without_dot).to_string() | ||
94 | } else { | ||
95 | receiver.to_string() | ||
96 | } | ||
97 | } | ||
98 | |||
99 | fn include_references(initial_element: &ast::Expr) -> ast::Expr { | ||
100 | let mut resulting_element = initial_element.clone(); | ||
101 | while let Some(parent_ref_element) = | ||
102 | resulting_element.syntax().parent().and_then(ast::RefExpr::cast) | ||
103 | { | ||
104 | resulting_element = ast::Expr::from(parent_ref_element); | ||
105 | } | ||
106 | resulting_element | ||
107 | } | ||
108 | |||
109 | fn postfix_snippet( | ||
110 | ctx: &CompletionContext, | ||
111 | receiver: &ast::Expr, | ||
112 | label: &str, | ||
113 | detail: &str, | ||
114 | snippet: &str, | ||
115 | ) -> Builder { | ||
69 | let edit = { | 116 | let edit = { |
70 | let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax(); | 117 | let receiver_syntax = receiver.syntax(); |
71 | let receiver_range = ctx.sema.original_range(receiver_syntax).range; | 118 | let receiver_range = ctx.sema.original_range(receiver_syntax).range; |
72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); | 119 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); |
73 | TextEdit::replace(delete_range, snippet.to_string()) | 120 | TextEdit::replace(delete_range, snippet.to_string()) |
@@ -340,4 +387,63 @@ mod tests { | |||
340 | "### | 387 | "### |
341 | ); | 388 | ); |
342 | } | 389 | } |
390 | |||
391 | #[test] | ||
392 | fn postfix_completion_for_references() { | ||
393 | assert_debug_snapshot!( | ||
394 | do_postfix_completion( | ||
395 | r#" | ||
396 | fn main() { | ||
397 | &&&&42.<|> | ||
398 | } | ||
399 | "#, | ||
400 | ), | ||
401 | @r###" | ||
402 | [ | ||
403 | CompletionItem { | ||
404 | label: "box", | ||
405 | source_range: [56; 56), | ||
406 | delete: [49; 56), | ||
407 | insert: "Box::new(&&&&42)", | ||
408 | detail: "Box::new(expr)", | ||
409 | }, | ||
410 | CompletionItem { | ||
411 | label: "dbg", | ||
412 | source_range: [56; 56), | ||
413 | delete: [49; 56), | ||
414 | insert: "dbg!(&&&&42)", | ||
415 | detail: "dbg!(expr)", | ||
416 | }, | ||
417 | CompletionItem { | ||
418 | label: "match", | ||
419 | source_range: [56; 56), | ||
420 | delete: [49; 56), | ||
421 | insert: "match &&&&42 {\n ${1:_} => {$0\\},\n}", | ||
422 | detail: "match expr {}", | ||
423 | }, | ||
424 | CompletionItem { | ||
425 | label: "not", | ||
426 | source_range: [56; 56), | ||
427 | delete: [53; 56), | ||
428 | insert: "!42", | ||
429 | detail: "!expr", | ||
430 | }, | ||
431 | CompletionItem { | ||
432 | label: "ref", | ||
433 | source_range: [56; 56), | ||
434 | delete: [53; 56), | ||
435 | insert: "&42", | ||
436 | detail: "&expr", | ||
437 | }, | ||
438 | CompletionItem { | ||
439 | label: "refm", | ||
440 | source_range: [56; 56), | ||
441 | delete: [53; 56), | ||
442 | insert: "&mut 42", | ||
443 | detail: "&mut expr", | ||
444 | }, | ||
445 | ] | ||
446 | "### | ||
447 | ); | ||
448 | } | ||
343 | } | 449 | } |
diff --git a/editors/code/package.json b/editors/code/package.json index eb5748515..1d113ebb6 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 | } |
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 08d821dd0..82ca749f3 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -99,8 +99,10 @@ export async function createClient(config: Config, serverPath: string): Promise< | |||
99 | // Note that while the CallHierarchyFeature is stable the LSP protocol is not. | 99 | // Note that while the CallHierarchyFeature is stable the LSP protocol is not. |
100 | res.registerFeature(new CallHierarchyFeature(res)); | 100 | res.registerFeature(new CallHierarchyFeature(res)); |
101 | 101 | ||
102 | if (config.highlightingSemanticTokens) { | 102 | if (config.package.enableProposedApi) { |
103 | res.registerFeature(new SemanticTokensFeature(res)); | 103 | if (config.highlightingSemanticTokens) { |
104 | res.registerFeature(new SemanticTokensFeature(res)); | ||
105 | } | ||
104 | } | 106 | } |
105 | 107 | ||
106 | return res; | 108 | return res; |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index be5296fcf..602538ea1 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -38,17 +38,11 @@ export class Config { | |||
38 | ] | 38 | ] |
39 | .map(opt => `${this.rootSection}.${opt}`); | 39 | .map(opt => `${this.rootSection}.${opt}`); |
40 | 40 | ||
41 | readonly packageJsonVersion: string = vscode | 41 | readonly package: { |
42 | .extensions | 42 | version: string; |
43 | .getExtension(this.extensionId)! | 43 | releaseTag: string | undefined; |
44 | .packageJSON | 44 | enableProposedApi: boolean | undefined; |
45 | .version; | 45 | } = 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 | 46 | ||
53 | private cfg!: vscode.WorkspaceConfiguration; | 47 | private cfg!: vscode.WorkspaceConfiguration; |
54 | 48 | ||
@@ -62,7 +56,7 @@ export class Config { | |||
62 | const enableLogging = this.cfg.get("trace.extension") as boolean; | 56 | const enableLogging = this.cfg.get("trace.extension") as boolean; |
63 | log.setEnabled(enableLogging); | 57 | log.setEnabled(enableLogging); |
64 | log.debug( | 58 | log.debug( |
65 | "Extension version:", this.packageJsonVersion, | 59 | "Extension version:", this.package.version, |
66 | "using configuration:", this.cfg | 60 | "using configuration:", this.cfg |
67 | ); | 61 | ); |
68 | } | 62 | } |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 5d2da9a76..7b7c19dfc 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 | ||
112 | async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> { | 112 | async 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. |
117 | To switch to stable, uninstall the extension and re-install it from the marketplace`); | 117 | To switch to stable, uninstall the extension and re-install it from the marketplace`); |
118 | } | 118 | } |
@@ -185,7 +185,7 @@ 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 === "x32") { |
@@ -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 | } |