diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-03-04 10:50:40 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-03-04 10:50:40 +0000 |
commit | 698aa9b3f6420351a41a3fb4819b871fec3c891c (patch) | |
tree | c782b2b62dcfaa253b8ed55824772ea7bf8fa16d | |
parent | 17aaece6b39c2fb525be0eccce4626fc622e8236 (diff) | |
parent | 1ef2c0613134633ef0fe0d515f7d416e482f07fb (diff) |
Merge #924
924: Improve show syntax tree r=matklad a=vipentti
This implements some of the features discussed in #820.
You can now select a range of syntax in a file and then use "Show Syntax Tree" to show its syntax. In addition you can select a range of syntax that is inside a string (typically test cases) and show its syntax as well.
Previous behavior is still available, simply use "Show Syntax Tree" without a selection, and you get the live updating syntax tree. Additionally now the live updating tree will update when the active file is changed. Previously you had to type something in the new file to get the syntax tree to update.
Co-authored-by: Ville Penttinen <[email protected]>
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ra_ide_api/src/syntax_tree.rs | 87 | ||||
-rw-r--r-- | crates/ra_ide_api/tests/test/main.rs | 254 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 4 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 1 | ||||
-rw-r--r-- | editors/code/package.json | 2 | ||||
-rw-r--r-- | editors/code/src/commands/syntaxTree.ts | 46 | ||||
-rw-r--r-- | editors/code/src/events/change_active_text_editor.ts | 39 | ||||
-rw-r--r-- | editors/code/src/events/change_text_document.ts | 10 | ||||
-rw-r--r-- | editors/code/src/extension.ts | 16 |
10 files changed, 423 insertions, 43 deletions
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 6546d0644..b8a4adbce 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -32,13 +32,14 @@ mod references; | |||
32 | mod impls; | 32 | mod impls; |
33 | mod assists; | 33 | mod assists; |
34 | mod diagnostics; | 34 | mod diagnostics; |
35 | mod syntax_tree; | ||
35 | 36 | ||
36 | #[cfg(test)] | 37 | #[cfg(test)] |
37 | mod marks; | 38 | mod marks; |
38 | 39 | ||
39 | use std::sync::Arc; | 40 | use std::sync::Arc; |
40 | 41 | ||
41 | use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit, AstNode}; | 42 | use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit}; |
42 | use ra_text_edit::TextEdit; | 43 | use ra_text_edit::TextEdit; |
43 | use ra_db::{ | 44 | use ra_db::{ |
44 | SourceDatabase, CheckCanceled, | 45 | SourceDatabase, CheckCanceled, |
@@ -245,8 +246,8 @@ impl Analysis { | |||
245 | 246 | ||
246 | /// Returns a syntax tree represented as `String`, for debug purposes. | 247 | /// Returns a syntax tree represented as `String`, for debug purposes. |
247 | // FIXME: use a better name here. | 248 | // FIXME: use a better name here. |
248 | pub fn syntax_tree(&self, file_id: FileId) -> String { | 249 | pub fn syntax_tree(&self, file_id: FileId, text_range: Option<TextRange>) -> String { |
249 | self.db.parse(file_id).syntax().debug_dump() | 250 | syntax_tree::syntax_tree(&self.db, file_id, text_range) |
250 | } | 251 | } |
251 | 252 | ||
252 | /// Returns an edit to remove all newlines in the range, cleaning up minor | 253 | /// Returns an edit to remove all newlines in the range, cleaning up minor |
diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs new file mode 100644 index 000000000..bbe9222b4 --- /dev/null +++ b/crates/ra_ide_api/src/syntax_tree.rs | |||
@@ -0,0 +1,87 @@ | |||
1 | use ra_db::SourceDatabase; | ||
2 | use crate::db::RootDatabase; | ||
3 | use ra_syntax::{ | ||
4 | SourceFile, SyntaxNode, TextRange, AstNode, | ||
5 | algo::{self, visit::{visitor, Visitor}}, ast::{self, AstToken} | ||
6 | }; | ||
7 | |||
8 | pub use ra_db::FileId; | ||
9 | |||
10 | pub(crate) fn syntax_tree( | ||
11 | db: &RootDatabase, | ||
12 | file_id: FileId, | ||
13 | text_range: Option<TextRange>, | ||
14 | ) -> String { | ||
15 | if let Some(text_range) = text_range { | ||
16 | let file = db.parse(file_id); | ||
17 | let node = algo::find_covering_node(file.syntax(), text_range); | ||
18 | |||
19 | if let Some(tree) = syntax_tree_for_string(node, text_range) { | ||
20 | return tree; | ||
21 | } | ||
22 | |||
23 | node.debug_dump() | ||
24 | } else { | ||
25 | db.parse(file_id).syntax().debug_dump() | ||
26 | } | ||
27 | } | ||
28 | |||
29 | /// Attempts parsing the selected contents of a string literal | ||
30 | /// as rust syntax and returns its syntax tree | ||
31 | fn syntax_tree_for_string(node: &SyntaxNode, text_range: TextRange) -> Option<String> { | ||
32 | // When the range is inside a string | ||
33 | // we'll attempt parsing it as rust syntax | ||
34 | // to provide the syntax tree of the contents of the string | ||
35 | visitor() | ||
36 | .visit(|node: &ast::String| syntax_tree_for_token(node, text_range)) | ||
37 | .visit(|node: &ast::RawString| syntax_tree_for_token(node, text_range)) | ||
38 | .accept(node)? | ||
39 | } | ||
40 | |||
41 | fn syntax_tree_for_token<T: AstToken>(node: &T, text_range: TextRange) -> Option<String> { | ||
42 | // Range of the full node | ||
43 | let node_range = node.syntax().range(); | ||
44 | let text = node.text().to_string(); | ||
45 | |||
46 | // We start at some point inside the node | ||
47 | // Either we have selected the whole string | ||
48 | // or our selection is inside it | ||
49 | let start = text_range.start() - node_range.start(); | ||
50 | |||
51 | // how many characters we have selected | ||
52 | let len = text_range.len().to_usize(); | ||
53 | |||
54 | let node_len = node_range.len().to_usize(); | ||
55 | |||
56 | let start = start.to_usize(); | ||
57 | |||
58 | // We want to cap our length | ||
59 | let len = len.min(node_len); | ||
60 | |||
61 | // Ensure our slice is inside the actual string | ||
62 | let end = if start + len < text.len() { start + len } else { text.len() - start }; | ||
63 | |||
64 | let text = &text[start..end]; | ||
65 | |||
66 | // Remove possible extra string quotes from the start | ||
67 | // and the end of the string | ||
68 | let text = text | ||
69 | .trim_start_matches('r') | ||
70 | .trim_start_matches('#') | ||
71 | .trim_start_matches('"') | ||
72 | .trim_end_matches('#') | ||
73 | .trim_end_matches('"') | ||
74 | .trim() | ||
75 | // Remove custom markers | ||
76 | .replace("<|>", ""); | ||
77 | |||
78 | let parsed = SourceFile::parse(&text); | ||
79 | |||
80 | // If the "file" parsed without errors, | ||
81 | // return its syntax | ||
82 | if parsed.errors().is_empty() { | ||
83 | return Some(parsed.syntax().debug_dump()); | ||
84 | } | ||
85 | |||
86 | None | ||
87 | } | ||
diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs index ff1a0e46b..0f0766f62 100644 --- a/crates/ra_ide_api/tests/test/main.rs +++ b/crates/ra_ide_api/tests/test/main.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use insta::assert_debug_snapshot_matches; | 1 | use insta::assert_debug_snapshot_matches; |
2 | use ra_ide_api::{ | 2 | use ra_ide_api::{ |
3 | mock_analysis::{single_file, single_file_with_position, MockAnalysis}, | 3 | mock_analysis::{single_file, single_file_with_position, single_file_with_range, MockAnalysis}, |
4 | AnalysisChange, CrateGraph, Edition::Edition2018, Query, NavigationTarget, | 4 | AnalysisChange, CrateGraph, Edition::Edition2018, Query, NavigationTarget, |
5 | ReferenceSearchResult, | 5 | ReferenceSearchResult, |
6 | }; | 6 | }; |
@@ -138,3 +138,255 @@ mod foo { | |||
138 | assert_eq!(s.name(), "FooInner"); | 138 | assert_eq!(s.name(), "FooInner"); |
139 | assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); | 139 | assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); |
140 | } | 140 | } |
141 | |||
142 | #[test] | ||
143 | fn test_syntax_tree_without_range() { | ||
144 | // Basic syntax | ||
145 | let (analysis, file_id) = single_file(r#"fn foo() {}"#); | ||
146 | let syn = analysis.syntax_tree(file_id, None); | ||
147 | |||
148 | assert_eq!( | ||
149 | syn.trim(), | ||
150 | r#" | ||
151 | SOURCE_FILE@[0; 11) | ||
152 | FN_DEF@[0; 11) | ||
153 | FN_KW@[0; 2) | ||
154 | WHITESPACE@[2; 3) | ||
155 | NAME@[3; 6) | ||
156 | IDENT@[3; 6) "foo" | ||
157 | PARAM_LIST@[6; 8) | ||
158 | L_PAREN@[6; 7) | ||
159 | R_PAREN@[7; 8) | ||
160 | WHITESPACE@[8; 9) | ||
161 | BLOCK@[9; 11) | ||
162 | L_CURLY@[9; 10) | ||
163 | R_CURLY@[10; 11) | ||
164 | "# | ||
165 | .trim() | ||
166 | ); | ||
167 | |||
168 | let (analysis, file_id) = single_file( | ||
169 | r#" | ||
170 | fn test() { | ||
171 | assert!(" | ||
172 | fn foo() { | ||
173 | } | ||
174 | ", ""); | ||
175 | }"# | ||
176 | .trim(), | ||
177 | ); | ||
178 | let syn = analysis.syntax_tree(file_id, None); | ||
179 | |||
180 | assert_eq!( | ||
181 | syn.trim(), | ||
182 | r#" | ||
183 | SOURCE_FILE@[0; 60) | ||
184 | FN_DEF@[0; 60) | ||
185 | FN_KW@[0; 2) | ||
186 | WHITESPACE@[2; 3) | ||
187 | NAME@[3; 7) | ||
188 | IDENT@[3; 7) "test" | ||
189 | PARAM_LIST@[7; 9) | ||
190 | L_PAREN@[7; 8) | ||
191 | R_PAREN@[8; 9) | ||
192 | WHITESPACE@[9; 10) | ||
193 | BLOCK@[10; 60) | ||
194 | L_CURLY@[10; 11) | ||
195 | WHITESPACE@[11; 16) | ||
196 | EXPR_STMT@[16; 58) | ||
197 | MACRO_CALL@[16; 57) | ||
198 | PATH@[16; 22) | ||
199 | PATH_SEGMENT@[16; 22) | ||
200 | NAME_REF@[16; 22) | ||
201 | IDENT@[16; 22) "assert" | ||
202 | EXCL@[22; 23) | ||
203 | TOKEN_TREE@[23; 57) | ||
204 | L_PAREN@[23; 24) | ||
205 | STRING@[24; 52) | ||
206 | COMMA@[52; 53) | ||
207 | WHITESPACE@[53; 54) | ||
208 | STRING@[54; 56) | ||
209 | R_PAREN@[56; 57) | ||
210 | SEMI@[57; 58) | ||
211 | WHITESPACE@[58; 59) | ||
212 | R_CURLY@[59; 60) | ||
213 | "# | ||
214 | .trim() | ||
215 | ); | ||
216 | } | ||
217 | |||
218 | #[test] | ||
219 | fn test_syntax_tree_with_range() { | ||
220 | let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); | ||
221 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
222 | |||
223 | assert_eq!( | ||
224 | syn.trim(), | ||
225 | r#" | ||
226 | FN_DEF@[0; 11) | ||
227 | FN_KW@[0; 2) | ||
228 | WHITESPACE@[2; 3) | ||
229 | NAME@[3; 6) | ||
230 | IDENT@[3; 6) "foo" | ||
231 | PARAM_LIST@[6; 8) | ||
232 | L_PAREN@[6; 7) | ||
233 | R_PAREN@[7; 8) | ||
234 | WHITESPACE@[8; 9) | ||
235 | BLOCK@[9; 11) | ||
236 | L_CURLY@[9; 10) | ||
237 | R_CURLY@[10; 11) | ||
238 | "# | ||
239 | .trim() | ||
240 | ); | ||
241 | |||
242 | let (analysis, range) = single_file_with_range( | ||
243 | r#"fn test() { | ||
244 | <|>assert!(" | ||
245 | fn foo() { | ||
246 | } | ||
247 | ", "");<|> | ||
248 | }"# | ||
249 | .trim(), | ||
250 | ); | ||
251 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
252 | |||
253 | assert_eq!( | ||
254 | syn.trim(), | ||
255 | r#" | ||
256 | EXPR_STMT@[16; 58) | ||
257 | MACRO_CALL@[16; 57) | ||
258 | PATH@[16; 22) | ||
259 | PATH_SEGMENT@[16; 22) | ||
260 | NAME_REF@[16; 22) | ||
261 | IDENT@[16; 22) "assert" | ||
262 | EXCL@[22; 23) | ||
263 | TOKEN_TREE@[23; 57) | ||
264 | L_PAREN@[23; 24) | ||
265 | STRING@[24; 52) | ||
266 | COMMA@[52; 53) | ||
267 | WHITESPACE@[53; 54) | ||
268 | STRING@[54; 56) | ||
269 | R_PAREN@[56; 57) | ||
270 | SEMI@[57; 58) | ||
271 | "# | ||
272 | .trim() | ||
273 | ); | ||
274 | } | ||
275 | |||
276 | #[test] | ||
277 | fn test_syntax_tree_inside_string() { | ||
278 | let (analysis, range) = single_file_with_range( | ||
279 | r#"fn test() { | ||
280 | assert!(" | ||
281 | <|>fn foo() { | ||
282 | }<|> | ||
283 | fn bar() { | ||
284 | } | ||
285 | ", ""); | ||
286 | }"# | ||
287 | .trim(), | ||
288 | ); | ||
289 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
290 | assert_eq!( | ||
291 | syn.trim(), | ||
292 | r#" | ||
293 | SOURCE_FILE@[0; 12) | ||
294 | FN_DEF@[0; 12) | ||
295 | FN_KW@[0; 2) | ||
296 | WHITESPACE@[2; 3) | ||
297 | NAME@[3; 6) | ||
298 | IDENT@[3; 6) "foo" | ||
299 | PARAM_LIST@[6; 8) | ||
300 | L_PAREN@[6; 7) | ||
301 | R_PAREN@[7; 8) | ||
302 | WHITESPACE@[8; 9) | ||
303 | BLOCK@[9; 12) | ||
304 | L_CURLY@[9; 10) | ||
305 | WHITESPACE@[10; 11) | ||
306 | R_CURLY@[11; 12) | ||
307 | "# | ||
308 | .trim() | ||
309 | ); | ||
310 | |||
311 | // With a raw string | ||
312 | let (analysis, range) = single_file_with_range( | ||
313 | r###"fn test() { | ||
314 | assert!(r#" | ||
315 | <|>fn foo() { | ||
316 | }<|> | ||
317 | fn bar() { | ||
318 | } | ||
319 | "#, ""); | ||
320 | }"### | ||
321 | .trim(), | ||
322 | ); | ||
323 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
324 | assert_eq!( | ||
325 | syn.trim(), | ||
326 | r#" | ||
327 | SOURCE_FILE@[0; 12) | ||
328 | FN_DEF@[0; 12) | ||
329 | FN_KW@[0; 2) | ||
330 | WHITESPACE@[2; 3) | ||
331 | NAME@[3; 6) | ||
332 | IDENT@[3; 6) "foo" | ||
333 | PARAM_LIST@[6; 8) | ||
334 | L_PAREN@[6; 7) | ||
335 | R_PAREN@[7; 8) | ||
336 | WHITESPACE@[8; 9) | ||
337 | BLOCK@[9; 12) | ||
338 | L_CURLY@[9; 10) | ||
339 | WHITESPACE@[10; 11) | ||
340 | R_CURLY@[11; 12) | ||
341 | "# | ||
342 | .trim() | ||
343 | ); | ||
344 | |||
345 | // With a raw string | ||
346 | let (analysis, range) = single_file_with_range( | ||
347 | r###"fn test() { | ||
348 | assert!(r<|>#" | ||
349 | fn foo() { | ||
350 | } | ||
351 | fn bar() { | ||
352 | }"<|>#, ""); | ||
353 | }"### | ||
354 | .trim(), | ||
355 | ); | ||
356 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
357 | assert_eq!( | ||
358 | syn.trim(), | ||
359 | r#" | ||
360 | SOURCE_FILE@[0; 25) | ||
361 | FN_DEF@[0; 12) | ||
362 | FN_KW@[0; 2) | ||
363 | WHITESPACE@[2; 3) | ||
364 | NAME@[3; 6) | ||
365 | IDENT@[3; 6) "foo" | ||
366 | PARAM_LIST@[6; 8) | ||
367 | L_PAREN@[6; 7) | ||
368 | R_PAREN@[7; 8) | ||
369 | WHITESPACE@[8; 9) | ||
370 | BLOCK@[9; 12) | ||
371 | L_CURLY@[9; 10) | ||
372 | WHITESPACE@[10; 11) | ||
373 | R_CURLY@[11; 12) | ||
374 | WHITESPACE@[12; 13) | ||
375 | FN_DEF@[13; 25) | ||
376 | FN_KW@[13; 15) | ||
377 | WHITESPACE@[15; 16) | ||
378 | NAME@[16; 19) | ||
379 | IDENT@[16; 19) "bar" | ||
380 | PARAM_LIST@[19; 21) | ||
381 | L_PAREN@[19; 20) | ||
382 | R_PAREN@[20; 21) | ||
383 | WHITESPACE@[21; 22) | ||
384 | BLOCK@[22; 25) | ||
385 | L_CURLY@[22; 23) | ||
386 | WHITESPACE@[23; 24) | ||
387 | R_CURLY@[24; 25) | ||
388 | |||
389 | "# | ||
390 | .trim() | ||
391 | ); | ||
392 | } | ||
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index dce6fcc67..89e96a33a 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -32,7 +32,9 @@ pub fn handle_analyzer_status(world: ServerWorld, _: ()) -> Result<String> { | |||
32 | 32 | ||
33 | pub fn handle_syntax_tree(world: ServerWorld, params: req::SyntaxTreeParams) -> Result<String> { | 33 | pub fn handle_syntax_tree(world: ServerWorld, params: req::SyntaxTreeParams) -> Result<String> { |
34 | let id = params.text_document.try_conv_with(&world)?; | 34 | let id = params.text_document.try_conv_with(&world)?; |
35 | let res = world.analysis().syntax_tree(id); | 35 | let line_index = world.analysis().file_line_index(id); |
36 | let text_range = params.range.map(|p| p.conv_with(&line_index)); | ||
37 | let res = world.analysis().syntax_tree(id, text_range); | ||
36 | Ok(res) | 38 | Ok(res) |
37 | } | 39 | } |
38 | 40 | ||
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index e224ede80..5c589f969 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -39,6 +39,7 @@ impl Request for SyntaxTree { | |||
39 | #[serde(rename_all = "camelCase")] | 39 | #[serde(rename_all = "camelCase")] |
40 | pub struct SyntaxTreeParams { | 40 | pub struct SyntaxTreeParams { |
41 | pub text_document: TextDocumentIdentifier, | 41 | pub text_document: TextDocumentIdentifier, |
42 | pub range: Option<Range>, | ||
42 | } | 43 | } |
43 | 44 | ||
44 | pub enum ExtendSelection {} | 45 | pub enum ExtendSelection {} |
diff --git a/editors/code/package.json b/editors/code/package.json index d4ce2ae2c..fda411810 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -75,7 +75,7 @@ | |||
75 | "commands": [ | 75 | "commands": [ |
76 | { | 76 | { |
77 | "command": "rust-analyzer.syntaxTree", | 77 | "command": "rust-analyzer.syntaxTree", |
78 | "title": "Show syntax tree for current file", | 78 | "title": "Show Syntax Tree", |
79 | "category": "Rust Analyzer" | 79 | "category": "Rust Analyzer" |
80 | }, | 80 | }, |
81 | { | 81 | { |
diff --git a/editors/code/src/commands/syntaxTree.ts b/editors/code/src/commands/syntaxTree.ts index c0baf08c5..2f50fe14b 100644 --- a/editors/code/src/commands/syntaxTree.ts +++ b/editors/code/src/commands/syntaxTree.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import { TextDocumentIdentifier } from 'vscode-languageclient'; | 2 | import { Range, TextDocumentIdentifier } from 'vscode-languageclient'; |
3 | 3 | ||
4 | import { Server } from '../server'; | 4 | import { Server } from '../server'; |
5 | 5 | ||
6 | export const syntaxTreeUri = vscode.Uri.parse('rust-analyzer://syntaxtree'); | 6 | export const syntaxTreeUri = vscode.Uri.parse('rust-analyzer://syntaxtree'); |
7 | 7 | ||
8 | export class TextDocumentContentProvider | 8 | export class SyntaxTreeContentProvider |
9 | implements vscode.TextDocumentContentProvider { | 9 | implements vscode.TextDocumentContentProvider { |
10 | public eventEmitter = new vscode.EventEmitter<vscode.Uri>(); | 10 | public eventEmitter = new vscode.EventEmitter<vscode.Uri>(); |
11 | public syntaxTree: string = 'Not available'; | 11 | public syntaxTree: string = 'Not available'; |
@@ -17,8 +17,21 @@ export class TextDocumentContentProvider | |||
17 | if (editor == null) { | 17 | if (editor == null) { |
18 | return ''; | 18 | return ''; |
19 | } | 19 | } |
20 | |||
21 | let range: Range | undefined; | ||
22 | |||
23 | // When the range based query is enabled we take the range of the selection | ||
24 | if (uri.query === 'range=true') { | ||
25 | range = editor.selection.isEmpty | ||
26 | ? undefined | ||
27 | : Server.client.code2ProtocolConverter.asRange( | ||
28 | editor.selection | ||
29 | ); | ||
30 | } | ||
31 | |||
20 | const request: SyntaxTreeParams = { | 32 | const request: SyntaxTreeParams = { |
21 | textDocument: { uri: editor.document.uri.toString() } | 33 | textDocument: { uri: editor.document.uri.toString() }, |
34 | range | ||
22 | }; | 35 | }; |
23 | return Server.client.sendRequest<SyntaxTreeResult>( | 36 | return Server.client.sendRequest<SyntaxTreeResult>( |
24 | 'rust-analyzer/syntaxTree', | 37 | 'rust-analyzer/syntaxTree', |
@@ -33,6 +46,7 @@ export class TextDocumentContentProvider | |||
33 | 46 | ||
34 | interface SyntaxTreeParams { | 47 | interface SyntaxTreeParams { |
35 | textDocument: TextDocumentIdentifier; | 48 | textDocument: TextDocumentIdentifier; |
49 | range?: Range; | ||
36 | } | 50 | } |
37 | 51 | ||
38 | type SyntaxTreeResult = string; | 52 | type SyntaxTreeResult = string; |
@@ -40,11 +54,23 @@ type SyntaxTreeResult = string; | |||
40 | // Opens the virtual file that will show the syntax tree | 54 | // Opens the virtual file that will show the syntax tree |
41 | // | 55 | // |
42 | // The contents of the file come from the `TextDocumentContentProvider` | 56 | // The contents of the file come from the `TextDocumentContentProvider` |
43 | export async function handle() { | 57 | export function createHandle(provider: SyntaxTreeContentProvider) { |
44 | const document = await vscode.workspace.openTextDocument(syntaxTreeUri); | 58 | return async () => { |
45 | return vscode.window.showTextDocument( | 59 | const editor = vscode.window.activeTextEditor; |
46 | document, | 60 | const rangeEnabled = !!(editor && !editor.selection.isEmpty); |
47 | vscode.ViewColumn.Two, | 61 | |
48 | true | 62 | const uri = rangeEnabled |
49 | ); | 63 | ? vscode.Uri.parse(`${syntaxTreeUri.toString()}?range=true`) |
64 | : syntaxTreeUri; | ||
65 | |||
66 | const document = await vscode.workspace.openTextDocument(uri); | ||
67 | |||
68 | provider.eventEmitter.fire(uri); | ||
69 | |||
70 | return vscode.window.showTextDocument( | ||
71 | document, | ||
72 | vscode.ViewColumn.Two, | ||
73 | true | ||
74 | ); | ||
75 | }; | ||
50 | } | 76 | } |
diff --git a/editors/code/src/events/change_active_text_editor.ts b/editors/code/src/events/change_active_text_editor.ts index af295b2ec..64be56225 100644 --- a/editors/code/src/events/change_active_text_editor.ts +++ b/editors/code/src/events/change_active_text_editor.ts | |||
@@ -1,23 +1,32 @@ | |||
1 | import { TextEditor } from 'vscode'; | 1 | import { TextEditor } from 'vscode'; |
2 | import { TextDocumentIdentifier } from 'vscode-languageclient'; | 2 | import { TextDocumentIdentifier } from 'vscode-languageclient'; |
3 | 3 | ||
4 | import { | ||
5 | SyntaxTreeContentProvider, | ||
6 | syntaxTreeUri | ||
7 | } from '../commands/syntaxTree'; | ||
4 | import { Decoration } from '../highlighting'; | 8 | import { Decoration } from '../highlighting'; |
5 | import { Server } from '../server'; | 9 | import { Server } from '../server'; |
6 | 10 | ||
7 | export async function handle(editor: TextEditor | undefined) { | 11 | export function makeHandler(syntaxTreeProvider: SyntaxTreeContentProvider) { |
8 | if ( | 12 | return async function handle(editor: TextEditor | undefined) { |
9 | !Server.config.highlightingOn || | 13 | if (!editor || editor.document.languageId !== 'rust') { |
10 | !editor || | 14 | return; |
11 | editor.document.languageId !== 'rust' | 15 | } |
12 | ) { | 16 | |
13 | return; | 17 | syntaxTreeProvider.eventEmitter.fire(syntaxTreeUri); |
14 | } | 18 | |
15 | const params: TextDocumentIdentifier = { | 19 | if (!Server.config.highlightingOn) { |
16 | uri: editor.document.uri.toString() | 20 | return; |
21 | } | ||
22 | |||
23 | const params: TextDocumentIdentifier = { | ||
24 | uri: editor.document.uri.toString() | ||
25 | }; | ||
26 | const decorations = await Server.client.sendRequest<Decoration[]>( | ||
27 | 'rust-analyzer/decorationsRequest', | ||
28 | params | ||
29 | ); | ||
30 | Server.highlighter.setHighlights(editor, decorations); | ||
17 | }; | 31 | }; |
18 | const decorations = await Server.client.sendRequest<Decoration[]>( | ||
19 | 'rust-analyzer/decorationsRequest', | ||
20 | params | ||
21 | ); | ||
22 | Server.highlighter.setHighlights(editor, decorations); | ||
23 | } | 32 | } |
diff --git a/editors/code/src/events/change_text_document.ts b/editors/code/src/events/change_text_document.ts index 6be057245..89488bc61 100644 --- a/editors/code/src/events/change_text_document.ts +++ b/editors/code/src/events/change_text_document.ts | |||
@@ -1,20 +1,18 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | 2 | ||
3 | import { | 3 | import { |
4 | syntaxTreeUri, | 4 | SyntaxTreeContentProvider, |
5 | TextDocumentContentProvider | 5 | syntaxTreeUri |
6 | } from '../commands/syntaxTree'; | 6 | } from '../commands/syntaxTree'; |
7 | 7 | ||
8 | export function createHandler( | 8 | export function createHandler(syntaxTreeProvider: SyntaxTreeContentProvider) { |
9 | textDocumentContentProvider: TextDocumentContentProvider | ||
10 | ) { | ||
11 | return (event: vscode.TextDocumentChangeEvent) => { | 9 | return (event: vscode.TextDocumentChangeEvent) => { |
12 | const doc = event.document; | 10 | const doc = event.document; |
13 | if (doc.languageId !== 'rust') { | 11 | if (doc.languageId !== 'rust') { |
14 | return; | 12 | return; |
15 | } | 13 | } |
16 | afterLs(() => { | 14 | afterLs(() => { |
17 | textDocumentContentProvider.eventEmitter.fire(syntaxTreeUri); | 15 | syntaxTreeProvider.eventEmitter.fire(syntaxTreeUri); |
18 | }); | 16 | }); |
19 | }; | 17 | }; |
20 | } | 18 | } |
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 8b332eeb2..941beba18 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts | |||
@@ -2,7 +2,7 @@ import * as vscode from 'vscode'; | |||
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | 3 | ||
4 | import * as commands from './commands'; | 4 | import * as commands from './commands'; |
5 | import { TextDocumentContentProvider } from './commands/syntaxTree'; | 5 | import { SyntaxTreeContentProvider } from './commands/syntaxTree'; |
6 | import * as events from './events'; | 6 | import * as events from './events'; |
7 | import * as notifications from './notifications'; | 7 | import * as notifications from './notifications'; |
8 | import { Server } from './server'; | 8 | import { Server } from './server'; |
@@ -52,7 +52,6 @@ export function activate(context: vscode.ExtensionContext) { | |||
52 | registerCommand('rust-analyzer.collectGarbage', () => | 52 | registerCommand('rust-analyzer.collectGarbage', () => |
53 | Server.client.sendRequest<null>('rust-analyzer/collectGarbage', null) | 53 | Server.client.sendRequest<null>('rust-analyzer/collectGarbage', null) |
54 | ); | 54 | ); |
55 | registerCommand('rust-analyzer.syntaxTree', commands.syntaxTree.handle); | ||
56 | registerCommand( | 55 | registerCommand( |
57 | 'rust-analyzer.extendSelection', | 56 | 'rust-analyzer.extendSelection', |
58 | commands.extendSelection.handle | 57 | commands.extendSelection.handle |
@@ -95,22 +94,27 @@ export function activate(context: vscode.ExtensionContext) { | |||
95 | notifications.publishDecorations.handle | 94 | notifications.publishDecorations.handle |
96 | ] | 95 | ] |
97 | ]; | 96 | ]; |
97 | const syntaxTreeContentProvider = new SyntaxTreeContentProvider(); | ||
98 | 98 | ||
99 | // The events below are plain old javascript events, triggered and handled by vscode | 99 | // The events below are plain old javascript events, triggered and handled by vscode |
100 | vscode.window.onDidChangeActiveTextEditor( | 100 | vscode.window.onDidChangeActiveTextEditor( |
101 | events.changeActiveTextEditor.handle | 101 | events.changeActiveTextEditor.makeHandler(syntaxTreeContentProvider) |
102 | ); | 102 | ); |
103 | 103 | ||
104 | const textDocumentContentProvider = new TextDocumentContentProvider(); | ||
105 | disposeOnDeactivation( | 104 | disposeOnDeactivation( |
106 | vscode.workspace.registerTextDocumentContentProvider( | 105 | vscode.workspace.registerTextDocumentContentProvider( |
107 | 'rust-analyzer', | 106 | 'rust-analyzer', |
108 | textDocumentContentProvider | 107 | syntaxTreeContentProvider |
109 | ) | 108 | ) |
110 | ); | 109 | ); |
111 | 110 | ||
111 | registerCommand( | ||
112 | 'rust-analyzer.syntaxTree', | ||
113 | commands.syntaxTree.createHandle(syntaxTreeContentProvider) | ||
114 | ); | ||
115 | |||
112 | vscode.workspace.onDidChangeTextDocument( | 116 | vscode.workspace.onDidChangeTextDocument( |
113 | events.changeTextDocument.createHandler(textDocumentContentProvider), | 117 | events.changeTextDocument.createHandler(syntaxTreeContentProvider), |
114 | null, | 118 | null, |
115 | context.subscriptions | 119 | context.subscriptions |
116 | ); | 120 | ); |