diff options
-rw-r--r-- | crates/libeditor/src/code_actions.rs | 27 | ||||
-rw-r--r-- | crates/libeditor/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/libsyntax2/src/algo/search.rs | 136 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/generated.rs | 25 | ||||
-rw-r--r-- | crates/libsyntax2/src/grammar.ron | 4 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 107 |
6 files changed, 114 insertions, 187 deletions
diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs index 7c9874588..6e2c73f29 100644 --- a/crates/libeditor/src/code_actions.rs +++ b/crates/libeditor/src/code_actions.rs | |||
@@ -1,11 +1,12 @@ | |||
1 | use {TextUnit, File, EditBuilder, Edit}; | 1 | use {TextUnit, File, EditBuilder, Edit}; |
2 | use libsyntax2::{ | 2 | use libsyntax2::{ |
3 | ast::AstNode, | 3 | ast::{self, AstNode}, |
4 | SyntaxKind::COMMA, | 4 | SyntaxKind::COMMA, |
5 | SyntaxNodeRef, | 5 | SyntaxNodeRef, |
6 | SyntaxRoot, | ||
6 | algo::{ | 7 | algo::{ |
7 | Direction, siblings, | 8 | Direction, siblings, |
8 | find_leaf_at_offset, | 9 | find_leaf_at_offset, ancestors, |
9 | }, | 10 | }, |
10 | }; | 11 | }; |
11 | 12 | ||
@@ -24,10 +25,32 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() | |||
24 | }) | 25 | }) |
25 | } | 26 | } |
26 | 27 | ||
28 | pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> Edit + 'a> { | ||
29 | let syntax = file.syntax(); | ||
30 | let syntax = syntax.as_ref(); | ||
31 | let nominal = find_node::<ast::NominalDef<_>>(syntax, offset)?; | ||
32 | Some(move || { | ||
33 | let mut edit = EditBuilder::new(); | ||
34 | edit.insert(nominal.syntax().range().start(), "#[derive()]\n".to_string()); | ||
35 | edit.finish() | ||
36 | }) | ||
37 | } | ||
38 | |||
27 | fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> { | 39 | fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> { |
28 | siblings(node, direction) | 40 | siblings(node, direction) |
29 | .skip(1) | 41 | .skip(1) |
30 | .find(|node| !node.kind().is_trivia()) | 42 | .find(|node| !node.kind().is_trivia()) |
31 | } | 43 | } |
32 | 44 | ||
45 | fn find_non_trivia_leaf(syntax: SyntaxNodeRef, offset: TextUnit) -> Option<SyntaxNodeRef> { | ||
46 | find_leaf_at_offset(syntax, offset) | ||
47 | .find(|leaf| !leaf.kind().is_trivia()) | ||
48 | } | ||
49 | |||
50 | fn find_node<'a, N: AstNode<&'a SyntaxRoot>>(syntax: SyntaxNodeRef<'a>, offset: TextUnit) -> Option<N> { | ||
51 | let leaf = find_non_trivia_leaf(syntax, offset)?; | ||
52 | ancestors(leaf) | ||
53 | .filter_map(N::cast) | ||
54 | .next() | ||
55 | } | ||
33 | 56 | ||
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index b40db2c66..2c46ca45f 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs | |||
@@ -21,7 +21,7 @@ pub use self::{ | |||
21 | extend_selection::extend_selection, | 21 | extend_selection::extend_selection, |
22 | symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, | 22 | symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, |
23 | edit::{EditBuilder, Edit, AtomEdit}, | 23 | edit::{EditBuilder, Edit, AtomEdit}, |
24 | code_actions::{flip_comma}, | 24 | code_actions::{flip_comma, add_derive}, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | #[derive(Debug)] | 27 | #[derive(Debug)] |
diff --git a/crates/libsyntax2/src/algo/search.rs b/crates/libsyntax2/src/algo/search.rs deleted file mode 100644 index 46404f537..000000000 --- a/crates/libsyntax2/src/algo/search.rs +++ /dev/null | |||
@@ -1,136 +0,0 @@ | |||
1 | use {Node, NodeType, TextUnit, TextRange}; | ||
2 | use ::visitor::{visitor, process_subtree_bottom_up}; | ||
3 | |||
4 | pub fn child_of_type(node: Node, ty: NodeType) -> Option<Node> { | ||
5 | node.children().find(|n| n.ty() == ty) | ||
6 | } | ||
7 | |||
8 | pub fn children_of_type<'f>(node: Node<'f>, ty: NodeType) -> Box<Iterator<Item=Node<'f>> + 'f> { | ||
9 | Box::new(node.children().filter(move |n| n.ty() == ty)) | ||
10 | } | ||
11 | |||
12 | pub fn subtree<'f>(node: Node<'f>) -> Box<Iterator<Item=Node<'f>> + 'f> { | ||
13 | Box::new(node.children().flat_map(subtree).chain(::std::iter::once(node))) | ||
14 | } | ||
15 | |||
16 | pub fn descendants_of_type<'f>(node: Node<'f>, ty: NodeType) -> Vec<Node<'f>> { | ||
17 | process_subtree_bottom_up( | ||
18 | node, | ||
19 | visitor(Vec::new()) | ||
20 | .visit_nodes(&[ty], |node, nodes| nodes.push(node)) | ||
21 | ) | ||
22 | } | ||
23 | |||
24 | pub fn child_of_type_exn(node: Node, ty: NodeType) -> Node { | ||
25 | child_of_type(node, ty).unwrap_or_else(|| { | ||
26 | panic!("No child of type {:?} for {:?}\ | ||
27 | ----\ | ||
28 | {}\ | ||
29 | ----", ty, node.ty(), node.text()) | ||
30 | }) | ||
31 | } | ||
32 | |||
33 | |||
34 | pub fn ancestors(node: Node) -> Ancestors { | ||
35 | Ancestors(Some(node)) | ||
36 | } | ||
37 | |||
38 | pub struct Ancestors<'f>(Option<Node<'f>>); | ||
39 | |||
40 | impl<'f> Iterator for Ancestors<'f> { | ||
41 | type Item = Node<'f>; | ||
42 | |||
43 | fn next(&mut self) -> Option<Self::Item> { | ||
44 | let current = self.0; | ||
45 | self.0 = current.and_then(|n| n.parent()); | ||
46 | current | ||
47 | } | ||
48 | } | ||
49 | |||
50 | pub fn is_leaf(node: Node) -> bool { | ||
51 | node.children().next().is_none() && !node.range().is_empty() | ||
52 | } | ||
53 | |||
54 | |||
55 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
56 | pub enum Direction { | ||
57 | Left, Right | ||
58 | } | ||
59 | |||
60 | pub fn sibling(node: Node, dir: Direction) -> Option<Node> { | ||
61 | let (parent, idx) = child_position(node)?; | ||
62 | let idx = match dir { | ||
63 | Direction::Left => idx.checked_sub(1)?, | ||
64 | Direction::Right => idx + 1, | ||
65 | }; | ||
66 | parent.children().nth(idx) | ||
67 | } | ||
68 | |||
69 | pub mod ast { | ||
70 | use {Node, AstNode, TextUnit, AstChildren}; | ||
71 | use visitor::{visitor, process_subtree_bottom_up}; | ||
72 | use super::{ancestors, find_leaf_at_offset, LeafAtOffset}; | ||
73 | |||
74 | pub fn ancestor<'f, T: AstNode<'f>>(node: Node<'f>) -> Option<T> { | ||
75 | ancestors(node) | ||
76 | .filter_map(T::wrap) | ||
77 | .next() | ||
78 | } | ||
79 | |||
80 | pub fn ancestor_exn<'f, T: AstNode<'f>>(node: Node<'f>) -> T { | ||
81 | ancestor(node).unwrap() | ||
82 | } | ||
83 | |||
84 | pub fn children_of_type<'f, N: AstNode<'f>>(node: Node<'f>) -> AstChildren<N> { | ||
85 | AstChildren::new(node.children()) | ||
86 | } | ||
87 | |||
88 | pub fn descendants_of_type<'f, N: AstNode<'f>>(node: Node<'f>) -> Vec<N> { | ||
89 | process_subtree_bottom_up( | ||
90 | node, | ||
91 | visitor(Vec::new()) | ||
92 | .visit::<N, _>(|node, acc| acc.push(node)) | ||
93 | ) | ||
94 | } | ||
95 | |||
96 | pub fn node_at_offset<'f, T: AstNode<'f>>(node: Node<'f>, offset: TextUnit) -> Option<T> { | ||
97 | match find_leaf_at_offset(node, offset) { | ||
98 | LeafAtOffset::None => None, | ||
99 | LeafAtOffset::Single(node) => ancestor(node), | ||
100 | LeafAtOffset::Between(left, right) => ancestor(left).or_else(|| ancestor(right)), | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | pub mod traversal { | ||
106 | use {Node}; | ||
107 | |||
108 | pub fn bottom_up<'f, F: FnMut(Node<'f>)>(node: Node<'f>, mut f: F) | ||
109 | { | ||
110 | go(node, &mut f); | ||
111 | |||
112 | fn go<'f, F: FnMut(Node<'f>)>(node: Node<'f>, f: &mut F) { | ||
113 | for child in node.children() { | ||
114 | go(child, f) | ||
115 | } | ||
116 | f(node); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | fn child_position(child: Node) -> Option<(Node, usize)> { | ||
122 | child.parent() | ||
123 | .map(|parent| { | ||
124 | (parent, parent.children().position(|n| n == child).unwrap()) | ||
125 | }) | ||
126 | } | ||
127 | |||
128 | fn common_ancestor<'f>(n1: Node<'f>, n2: Node<'f>) -> Node<'f> { | ||
129 | for p in ancestors(n1) { | ||
130 | if ancestors(n2).any(|a| a == p) { | ||
131 | return p; | ||
132 | } | ||
133 | } | ||
134 | panic!("Can't find common ancestor of {:?} and {:?}", n1, n2) | ||
135 | } | ||
136 | |||
diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index 3e6c673ab..80670ce71 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs | |||
@@ -267,6 +267,31 @@ impl<R: TreeRoot> AstNode<R> for NeverType<R> { | |||
267 | 267 | ||
268 | impl<R: TreeRoot> NeverType<R> {} | 268 | impl<R: TreeRoot> NeverType<R> {} |
269 | 269 | ||
270 | // NominalDef | ||
271 | #[derive(Debug, Clone, Copy)] | ||
272 | pub enum NominalDef<R: TreeRoot = Arc<SyntaxRoot>> { | ||
273 | StructDef(StructDef<R>), | ||
274 | EnumDef(EnumDef<R>), | ||
275 | } | ||
276 | |||
277 | impl<R: TreeRoot> AstNode<R> for NominalDef<R> { | ||
278 | fn cast(syntax: SyntaxNode<R>) -> Option<Self> { | ||
279 | match syntax.kind() { | ||
280 | STRUCT_DEF => Some(NominalDef::StructDef(StructDef { syntax })), | ||
281 | ENUM_DEF => Some(NominalDef::EnumDef(EnumDef { syntax })), | ||
282 | _ => None, | ||
283 | } | ||
284 | } | ||
285 | fn syntax(&self) -> &SyntaxNode<R> { | ||
286 | match self { | ||
287 | NominalDef::StructDef(inner) => inner.syntax(), | ||
288 | NominalDef::EnumDef(inner) => inner.syntax(), | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | impl<R: TreeRoot> NominalDef<R> {} | ||
294 | |||
270 | // ParenType | 295 | // ParenType |
271 | #[derive(Debug, Clone, Copy)] | 296 | #[derive(Debug, Clone, Copy)] |
272 | pub struct ParenType<R: TreeRoot = Arc<SyntaxRoot>> { | 297 | pub struct ParenType<R: TreeRoot = Arc<SyntaxRoot>> { |
diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index 3641b65e2..3ae403bb5 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron | |||
@@ -261,6 +261,8 @@ Grammar( | |||
261 | "ForType", | 261 | "ForType", |
262 | "ImplTraitType", | 262 | "ImplTraitType", |
263 | "DynTraitType", | 263 | "DynTraitType", |
264 | ]) | 264 | ]), |
265 | |||
266 | "NominalDef": ( enum: ["StructDef", "EnumDef"]), | ||
265 | }, | 267 | }, |
266 | ) | 268 | ) |
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 8789ef0d2..9de6f480b 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
@@ -87,27 +87,28 @@ pub fn handle_code_action( | |||
87 | let file = world.file_syntax(&path)?; | 87 | let file = world.file_syntax(&path)?; |
88 | let line_index = world.file_line_index(&path)?; | 88 | let line_index = world.file_line_index(&path)?; |
89 | let offset = params.range.conv_with(&line_index).start(); | 89 | let offset = params.range.conv_with(&line_index).start(); |
90 | let ret = if libeditor::flip_comma(&file, offset).is_some() { | 90 | let mut ret = Vec::new(); |
91 | let cmd = apply_code_action_cmd( | 91 | |
92 | ActionId::FlipComma, | 92 | let actions = &[ |
93 | params.text_document, | 93 | (ActionId::FlipComma, libeditor::flip_comma(&file, offset).is_some()), |
94 | offset, | 94 | (ActionId::AddDerive, libeditor::add_derive(&file, offset).is_some()), |
95 | ); | 95 | ]; |
96 | Some(vec![cmd]) | 96 | |
97 | } else { | 97 | for (id, edit) in actions { |
98 | None | 98 | if *edit { |
99 | }; | 99 | let cmd = apply_code_action_cmd(*id, params.text_document.clone(), offset); |
100 | Ok(ret) | 100 | ret.push(cmd); |
101 | } | ||
102 | } | ||
103 | return Ok(Some(ret)); | ||
101 | } | 104 | } |
102 | 105 | ||
103 | pub fn handle_workspace_symbol( | 106 | pub fn handle_workspace_symbol( |
104 | world: World, | 107 | world: World, |
105 | params: req::WorkspaceSymbolParams, | 108 | params: req::WorkspaceSymbolParams, |
106 | ) -> Result<Option<Vec<SymbolInformation>>> { | 109 | ) -> Result<Option<Vec<SymbolInformation>>> { |
107 | let mut acc = Vec::new(); | 110 | let all_symbols = params.query.contains("#"); |
108 | |||
109 | let query = { | 111 | let query = { |
110 | let all_symbols = params.query.contains("#"); | ||
111 | let query: String = params.query.chars() | 112 | let query: String = params.query.chars() |
112 | .filter(|&c| c != '#') | 113 | .filter(|&c| c != '#') |
113 | .collect(); | 114 | .collect(); |
@@ -118,19 +119,29 @@ pub fn handle_workspace_symbol( | |||
118 | q.limit(128); | 119 | q.limit(128); |
119 | q | 120 | q |
120 | }; | 121 | }; |
122 | let mut res = exec_query(&world, query)?; | ||
123 | if res.is_empty() && !all_symbols { | ||
124 | let mut query = Query::new(params.query); | ||
125 | query.limit(128); | ||
126 | res = exec_query(&world, query)?; | ||
127 | } | ||
121 | 128 | ||
122 | for (path, symbol) in world.world_symbols(query) { | 129 | return Ok(Some(res)); |
123 | let line_index = world.file_line_index(path)?; | ||
124 | let info = SymbolInformation { | ||
125 | name: symbol.name.to_string(), | ||
126 | kind: symbol.kind.conv(), | ||
127 | location: (path, symbol.node_range).try_conv_with(&line_index)?, | ||
128 | container_name: None, | ||
129 | }; | ||
130 | acc.push(info); | ||
131 | }; | ||
132 | 130 | ||
133 | Ok(Some(acc)) | 131 | fn exec_query(world: &World, query: Query) -> Result<Vec<SymbolInformation>> { |
132 | let mut res = Vec::new(); | ||
133 | for (path, symbol) in world.world_symbols(query) { | ||
134 | let line_index = world.file_line_index(path)?; | ||
135 | let info = SymbolInformation { | ||
136 | name: symbol.name.to_string(), | ||
137 | kind: symbol.kind.conv(), | ||
138 | location: (path, symbol.node_range).try_conv_with(&line_index)?, | ||
139 | container_name: None, | ||
140 | }; | ||
141 | res.push(info); | ||
142 | }; | ||
143 | Ok(res) | ||
144 | } | ||
134 | } | 145 | } |
135 | 146 | ||
136 | pub fn handle_goto_definition( | 147 | pub fn handle_goto_definition( |
@@ -161,28 +172,28 @@ pub fn handle_execute_command( | |||
161 | } | 172 | } |
162 | let arg = params.arguments.pop().unwrap(); | 173 | let arg = params.arguments.pop().unwrap(); |
163 | let arg: ActionRequest = from_value(arg)?; | 174 | let arg: ActionRequest = from_value(arg)?; |
164 | match arg.id { | 175 | let path = arg.text_document.file_path()?; |
165 | ActionId::FlipComma => { | 176 | let file = world.file_syntax(&path)?; |
166 | let path = arg.text_document.file_path()?; | 177 | let edit = match arg.id { |
167 | let file = world.file_syntax(&path)?; | 178 | ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|edit| edit()), |
168 | let line_index = world.file_line_index(&path)?; | 179 | ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|edit| edit()), |
169 | let edit = match libeditor::flip_comma(&file, arg.offset) { | 180 | }; |
170 | Some(edit) => edit(), | 181 | let edit = match edit { |
171 | None => bail!("command not applicable"), | 182 | Some(edit) => edit, |
172 | }; | 183 | None => bail!("command not applicable"), |
173 | let mut changes = HashMap::new(); | 184 | }; |
174 | changes.insert( | 185 | let line_index = world.file_line_index(&path)?; |
175 | arg.text_document.uri, | 186 | let mut changes = HashMap::new(); |
176 | edit.conv_with(&line_index), | 187 | changes.insert( |
177 | ); | 188 | arg.text_document.uri, |
178 | let edit = WorkspaceEdit { | 189 | edit.conv_with(&line_index), |
179 | changes: Some(changes), | 190 | ); |
180 | document_changes: None, | 191 | let edit = WorkspaceEdit { |
181 | }; | 192 | changes: Some(changes), |
193 | document_changes: None, | ||
194 | }; | ||
182 | 195 | ||
183 | Ok(req::ApplyWorkspaceEditParams { edit }) | 196 | Ok(req::ApplyWorkspaceEditParams { edit }) |
184 | } | ||
185 | } | ||
186 | } | 197 | } |
187 | 198 | ||
188 | #[derive(Serialize, Deserialize)] | 199 | #[derive(Serialize, Deserialize)] |
@@ -207,13 +218,15 @@ fn apply_code_action_cmd(id: ActionId, doc: TextDocumentIdentifier, offset: Text | |||
207 | 218 | ||
208 | #[derive(Serialize, Deserialize, Clone, Copy)] | 219 | #[derive(Serialize, Deserialize, Clone, Copy)] |
209 | enum ActionId { | 220 | enum ActionId { |
210 | FlipComma | 221 | FlipComma, |
222 | AddDerive, | ||
211 | } | 223 | } |
212 | 224 | ||
213 | impl ActionId { | 225 | impl ActionId { |
214 | fn title(&self) -> &'static str { | 226 | fn title(&self) -> &'static str { |
215 | match *self { | 227 | match *self { |
216 | ActionId::FlipComma => "Flip `,`", | 228 | ActionId::FlipComma => "Flip `,`", |
229 | ActionId::AddDerive => "Add `#[derive]`", | ||
217 | } | 230 | } |
218 | } | 231 | } |
219 | } | 232 | } |