diff options
author | Aleksey Kladov <[email protected]> | 2018-08-15 22:23:22 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-15 22:23:22 +0100 |
commit | c631b585a7358d1569a051f2529ecaae222e95cd (patch) | |
tree | 2e8332d166900c29cf485297b8510451b97accd0 | |
parent | aa0d344581dcfd7f18c595688a4b2709b0f2421e (diff) |
matching brace
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | code/package.json | 5 | ||||
-rw-r--r-- | code/src/extension.ts | 21 | ||||
-rw-r--r-- | crates/libeditor/src/lib.rs | 26 | ||||
-rw-r--r-- | crates/libeditor/tests/test.rs | 43 | ||||
-rw-r--r-- | crates/server/src/conv.rs | 8 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 21 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 4 | ||||
-rw-r--r-- | crates/server/src/req.rs | 17 |
9 files changed, 135 insertions, 14 deletions
diff --git a/Cargo.toml b/Cargo.toml index 9748fd578..5cfc064b5 100644 --- a/Cargo.toml +++ b/Cargo.toml | |||
@@ -1,3 +1,5 @@ | |||
1 | [workspace] | 1 | [workspace] |
2 | members = [ "crates/*" ] | 2 | members = [ "crates/*" ] |
3 | exclude = [ "crates/indxr" ] | 3 | |
4 | [profile.release] | ||
5 | debug = true | ||
diff --git a/code/package.json b/code/package.json index 8563ba73c..fd3b8e423 100644 --- a/code/package.json +++ b/code/package.json | |||
@@ -36,6 +36,11 @@ | |||
36 | { | 36 | { |
37 | "command": "libsyntax-rust.extendSelection", | 37 | "command": "libsyntax-rust.extendSelection", |
38 | "title": "Rust Extend Selection" | 38 | "title": "Rust Extend Selection" |
39 | }, | ||
40 | { | ||
41 | "command": "libsyntax-rust.matchingBrace", | ||
42 | "key": "ctrl+shift+m", | ||
43 | "title": "Rust Matching Brace" | ||
39 | } | 44 | } |
40 | ], | 45 | ], |
41 | "keybindings": [ | 46 | "keybindings": [ |
diff --git a/code/src/extension.ts b/code/src/extension.ts index bb724539d..afcbccf63 100644 --- a/code/src/extension.ts +++ b/code/src/extension.ts | |||
@@ -35,6 +35,22 @@ export function activate(context: vscode.ExtensionContext) { | |||
35 | return new vscode.Selection(r.start, r.end) | 35 | return new vscode.Selection(r.start, r.end) |
36 | }) | 36 | }) |
37 | }) | 37 | }) |
38 | registerCommand('libsyntax-rust.matchingBrace', async () => { | ||
39 | let editor = vscode.window.activeTextEditor | ||
40 | if (editor == null || editor.document.languageId != "rust") return | ||
41 | let request: FindMatchingBraceParams = { | ||
42 | textDocument: { uri: editor.document.uri.toString() }, | ||
43 | offsets: editor.selections.map((s) => { | ||
44 | return client.code2ProtocolConverter.asPosition(s.active) | ||
45 | }) | ||
46 | } | ||
47 | let response = await client.sendRequest<lc.Position[]>("m/findMatchingBrace", request) | ||
48 | editor.selections = editor.selections.map((sel, idx) => { | ||
49 | let active = client.protocol2CodeConverter.asPosition(response[idx]) | ||
50 | let anchor = sel.isEmpty ? active : sel.anchor | ||
51 | return new vscode.Selection(anchor, active) | ||
52 | }) | ||
53 | }) | ||
38 | 54 | ||
39 | dispose(vscode.workspace.registerTextDocumentContentProvider( | 55 | dispose(vscode.workspace.registerTextDocumentContentProvider( |
40 | 'libsyntax-rust', | 56 | 'libsyntax-rust', |
@@ -184,6 +200,11 @@ interface ExtendSelectionResult { | |||
184 | selections: lc.Range[]; | 200 | selections: lc.Range[]; |
185 | } | 201 | } |
186 | 202 | ||
203 | interface FindMatchingBraceParams { | ||
204 | textDocument: lc.TextDocumentIdentifier; | ||
205 | offsets: lc.Position[]; | ||
206 | } | ||
207 | |||
187 | interface PublishDecorationsParams { | 208 | interface PublishDecorationsParams { |
188 | uri: string, | 209 | uri: string, |
189 | decorations: Decoration[], | 210 | decorations: Decoration[], |
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index 9e44f5d92..28da457d1 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs | |||
@@ -12,8 +12,8 @@ mod code_actions; | |||
12 | use libsyntax2::{ | 12 | use libsyntax2::{ |
13 | ast::{self, NameOwner}, | 13 | ast::{self, NameOwner}, |
14 | AstNode, | 14 | AstNode, |
15 | algo::walk, | 15 | algo::{walk, find_leaf_at_offset}, |
16 | SyntaxKind::*, | 16 | SyntaxKind::{self, *}, |
17 | }; | 17 | }; |
18 | pub use libsyntax2::{File, TextRange, TextUnit}; | 18 | pub use libsyntax2::{File, TextRange, TextUnit}; |
19 | pub use self::{ | 19 | pub use self::{ |
@@ -52,6 +52,28 @@ pub fn parse(text: &str) -> ast::File { | |||
52 | ast::File::parse(text) | 52 | ast::File::parse(text) |
53 | } | 53 | } |
54 | 54 | ||
55 | pub fn matching_brace(file: &ast::File, offset: TextUnit) -> Option<TextUnit> { | ||
56 | const BRACES: &[SyntaxKind] = &[ | ||
57 | L_CURLY, R_CURLY, | ||
58 | L_BRACK, R_BRACK, | ||
59 | L_PAREN, R_PAREN, | ||
60 | L_ANGLE, R_ANGLE, | ||
61 | ]; | ||
62 | let syntax = file.syntax(); | ||
63 | let syntax = syntax.as_ref(); | ||
64 | let (brace_node, brace_idx) = find_leaf_at_offset(syntax, offset) | ||
65 | .filter_map(|node| { | ||
66 | let idx = BRACES.iter().position(|&brace| brace == node.kind())?; | ||
67 | Some((node, idx)) | ||
68 | }) | ||
69 | .next()?; | ||
70 | let parent = brace_node.parent()?; | ||
71 | let matching_kind = BRACES[brace_idx ^ 1]; | ||
72 | let matching_node = parent.children() | ||
73 | .find(|node| node.kind() == matching_kind)?; | ||
74 | Some(matching_node.range().start()) | ||
75 | } | ||
76 | |||
55 | pub fn highlight(file: &ast::File) -> Vec<HighlightedRange> { | 77 | pub fn highlight(file: &ast::File) -> Vec<HighlightedRange> { |
56 | let syntax = file.syntax(); | 78 | let syntax = file.syntax(); |
57 | let mut res = Vec::new(); | 79 | let mut res = Vec::new(); |
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index 7063425ce..d5df9d0cc 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs | |||
@@ -9,7 +9,7 @@ use itertools::Itertools; | |||
9 | use libeditor::{ | 9 | use libeditor::{ |
10 | File, TextUnit, TextRange, ActionResult, CursorPosition, | 10 | File, TextUnit, TextRange, ActionResult, CursorPosition, |
11 | highlight, runnables, extend_selection, file_structure, | 11 | highlight, runnables, extend_selection, file_structure, |
12 | flip_comma, add_derive, | 12 | flip_comma, add_derive, matching_brace, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | #[test] | 15 | #[test] |
@@ -119,6 +119,25 @@ fn test_add_derive() { | |||
119 | ) | 119 | ) |
120 | } | 120 | } |
121 | 121 | ||
122 | #[test] | ||
123 | fn test_matching_brace() { | ||
124 | fn do_check(before: &str, after: &str) { | ||
125 | let (pos, before) = extract_cursor(before); | ||
126 | let file = file(&before); | ||
127 | let new_pos = match matching_brace(&file, pos) { | ||
128 | None => pos, | ||
129 | Some(pos) => pos, | ||
130 | }; | ||
131 | let actual = add_cursor(&before, new_pos); | ||
132 | assert_eq_text!(after, &actual); | ||
133 | } | ||
134 | |||
135 | do_check( | ||
136 | "struct Foo { a: i32, }<|>", | ||
137 | "struct Foo <|>{ a: i32, }", | ||
138 | ); | ||
139 | } | ||
140 | |||
122 | fn file(text: &str) -> File { | 141 | fn file(text: &str) -> File { |
123 | File::parse(text) | 142 | File::parse(text) |
124 | } | 143 | } |
@@ -138,16 +157,12 @@ fn check_action<F: Fn(&File, TextUnit) -> Option<ActionResult>>( | |||
138 | let file = file(&before); | 157 | let file = file(&before); |
139 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); | 158 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); |
140 | let actual = result.edit.apply(&before); | 159 | let actual = result.edit.apply(&before); |
141 | let actual_cursor_pos: u32 = match result.cursor_position { | 160 | let actual_cursor_pos = match result.cursor_position { |
142 | CursorPosition::Same => result.edit.apply_to_offset(before_cursor_pos).unwrap(), | 161 | CursorPosition::Same => result.edit.apply_to_offset(before_cursor_pos).unwrap(), |
143 | CursorPosition::Offset(off) => off, | 162 | CursorPosition::Offset(off) => off, |
144 | }.into(); | 163 | }; |
145 | let actual_cursor_pos = actual_cursor_pos as usize; | 164 | let actual = add_cursor(&actual, actual_cursor_pos); |
146 | let mut actual_with_cursor = String::new(); | 165 | assert_eq_text!(after, &actual); |
147 | actual_with_cursor.push_str(&actual[..actual_cursor_pos]); | ||
148 | actual_with_cursor.push_str("<|>"); | ||
149 | actual_with_cursor.push_str(&actual[actual_cursor_pos..]); | ||
150 | assert_eq_text!(after, &actual_with_cursor); | ||
151 | } | 166 | } |
152 | 167 | ||
153 | fn extract_cursor(text: &str) -> (TextUnit, String) { | 168 | fn extract_cursor(text: &str) -> (TextUnit, String) { |
@@ -162,3 +177,13 @@ fn extract_cursor(text: &str) -> (TextUnit, String) { | |||
162 | let cursor_pos = TextUnit::from(cursor_pos as u32); | 177 | let cursor_pos = TextUnit::from(cursor_pos as u32); |
163 | (cursor_pos, new_text) | 178 | (cursor_pos, new_text) |
164 | } | 179 | } |
180 | |||
181 | fn add_cursor(text: &str, offset: TextUnit) -> String { | ||
182 | let offset: u32 = offset.into(); | ||
183 | let offset: usize = offset as usize; | ||
184 | let mut res = String::new(); | ||
185 | res.push_str(&text[..offset]); | ||
186 | res.push_str("<|>"); | ||
187 | res.push_str(&text[offset..]); | ||
188 | res | ||
189 | } | ||
diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs index bbe512ece..b3709ccaf 100644 --- a/crates/server/src/conv.rs +++ b/crates/server/src/conv.rs | |||
@@ -117,6 +117,14 @@ impl ConvWith for AtomEdit { | |||
117 | } | 117 | } |
118 | } | 118 | } |
119 | 119 | ||
120 | impl<T: ConvWith> ConvWith for Option<T> { | ||
121 | type Ctx = <T as ConvWith>::Ctx; | ||
122 | type Output = Option<<T as ConvWith>::Output>; | ||
123 | fn conv_with(self, ctx: &Self::Ctx) -> Self::Output { | ||
124 | self.map(|x| ConvWith::conv_with(x, ctx)) | ||
125 | } | ||
126 | } | ||
127 | |||
120 | impl<'a> TryConvWith for &'a Url { | 128 | impl<'a> TryConvWith for &'a Url { |
121 | type Ctx = PathMap; | 129 | type Ctx = PathMap; |
122 | type Output = FileId; | 130 | type Output = FileId; |
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 078abfbfa..d7b78b4fa 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
@@ -3,7 +3,7 @@ use std::collections::HashMap; | |||
3 | use languageserver_types::{ | 3 | use languageserver_types::{ |
4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, | 4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, |
5 | Command, TextDocumentIdentifier, WorkspaceEdit, | 5 | Command, TextDocumentIdentifier, WorkspaceEdit, |
6 | SymbolInformation, | 6 | SymbolInformation, Position, |
7 | }; | 7 | }; |
8 | use libanalysis::{World, Query}; | 8 | use libanalysis::{World, Query}; |
9 | use libeditor; | 9 | use libeditor; |
@@ -42,6 +42,25 @@ pub fn handle_extend_selection( | |||
42 | Ok(req::ExtendSelectionResult { selections }) | 42 | Ok(req::ExtendSelectionResult { selections }) |
43 | } | 43 | } |
44 | 44 | ||
45 | pub fn handle_find_matching_brace( | ||
46 | world: World, | ||
47 | path_map: PathMap, | ||
48 | params: req::FindMatchingBraceParams, | ||
49 | ) -> Result<Vec<Position>> { | ||
50 | let file_id = params.text_document.try_conv_with(&path_map)?; | ||
51 | let file = world.file_syntax(file_id)?; | ||
52 | let line_index = world.file_line_index(file_id)?; | ||
53 | let res = params.offsets | ||
54 | .into_iter() | ||
55 | .map_conv_with(&line_index) | ||
56 | .map(|offset| { | ||
57 | libeditor::matching_brace(&file, offset).unwrap_or(offset) | ||
58 | }) | ||
59 | .map_conv_with(&line_index) | ||
60 | .collect(); | ||
61 | Ok(res) | ||
62 | } | ||
63 | |||
45 | pub fn handle_document_symbol( | 64 | pub fn handle_document_symbol( |
46 | world: World, | 65 | world: World, |
47 | path_map: PathMap, | 66 | path_map: PathMap, |
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index 2a31297be..4d5dfb437 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -26,6 +26,7 @@ use { | |||
26 | handle_execute_command, | 26 | handle_execute_command, |
27 | handle_workspace_symbol, | 27 | handle_workspace_symbol, |
28 | handle_goto_definition, | 28 | handle_goto_definition, |
29 | handle_find_matching_brace, | ||
29 | }, | 30 | }, |
30 | }; | 31 | }; |
31 | 32 | ||
@@ -148,6 +149,9 @@ fn on_request( | |||
148 | handle_request_on_threadpool::<req::ExtendSelection>( | 149 | handle_request_on_threadpool::<req::ExtendSelection>( |
149 | &mut req, pool, path_map, world, sender, handle_extend_selection, | 150 | &mut req, pool, path_map, world, sender, handle_extend_selection, |
150 | )?; | 151 | )?; |
152 | handle_request_on_threadpool::<req::FindMatchingBrace>( | ||
153 | &mut req, pool, path_map, world, sender, handle_find_matching_brace, | ||
154 | )?; | ||
151 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( | 155 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( |
152 | &mut req, pool, path_map, world, sender, handle_document_symbol, | 156 | &mut req, pool, path_map, world, sender, handle_document_symbol, |
153 | )?; | 157 | )?; |
diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index 17ef10e43..c3efc7489 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use serde::{ser::Serialize, de::DeserializeOwned}; | 1 | use serde::{ser::Serialize, de::DeserializeOwned}; |
2 | use languageserver_types::{TextDocumentIdentifier, Range, Url}; | 2 | use languageserver_types::{TextDocumentIdentifier, Range, Url, Position}; |
3 | use url_serde; | 3 | use url_serde; |
4 | 4 | ||
5 | pub use languageserver_types::{ | 5 | pub use languageserver_types::{ |
@@ -65,6 +65,21 @@ pub struct ExtendSelectionResult { | |||
65 | pub selections: Vec<Range>, | 65 | pub selections: Vec<Range>, |
66 | } | 66 | } |
67 | 67 | ||
68 | pub enum FindMatchingBrace {} | ||
69 | |||
70 | impl Request for FindMatchingBrace { | ||
71 | type Params = FindMatchingBraceParams; | ||
72 | type Result = Vec<Position>; | ||
73 | const METHOD: &'static str = "m/findMatchingBrace"; | ||
74 | } | ||
75 | |||
76 | #[derive(Deserialize, Debug)] | ||
77 | #[serde(rename_all = "camelCase")] | ||
78 | pub struct FindMatchingBraceParams { | ||
79 | pub text_document: TextDocumentIdentifier, | ||
80 | pub offsets: Vec<Position>, | ||
81 | } | ||
82 | |||
68 | pub enum PublishDecorations {} | 83 | pub enum PublishDecorations {} |
69 | 84 | ||
70 | impl Notification for PublishDecorations { | 85 | impl Notification for PublishDecorations { |