aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/libeditor/src/code_actions.rs27
-rw-r--r--crates/libeditor/src/lib.rs2
-rw-r--r--crates/libsyntax2/src/algo/search.rs136
-rw-r--r--crates/libsyntax2/src/ast/generated.rs25
-rw-r--r--crates/libsyntax2/src/grammar.ron4
-rw-r--r--crates/server/src/main_loop/handlers.rs107
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 @@
1use {TextUnit, File, EditBuilder, Edit}; 1use {TextUnit, File, EditBuilder, Edit};
2use libsyntax2::{ 2use 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
28pub 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
27fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> { 39fn 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
45fn 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
50fn 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 @@
1use {Node, NodeType, TextUnit, TextRange};
2use ::visitor::{visitor, process_subtree_bottom_up};
3
4pub fn child_of_type(node: Node, ty: NodeType) -> Option<Node> {
5 node.children().find(|n| n.ty() == ty)
6}
7
8pub 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
12pub 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
16pub 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
24pub 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
34pub fn ancestors(node: Node) -> Ancestors {
35 Ancestors(Some(node))
36}
37
38pub struct Ancestors<'f>(Option<Node<'f>>);
39
40impl<'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
50pub 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)]
56pub enum Direction {
57 Left, Right
58}
59
60pub 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
69pub 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
105pub 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
121fn 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
128fn 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
268impl<R: TreeRoot> NeverType<R> {} 268impl<R: TreeRoot> NeverType<R> {}
269 269
270// NominalDef
271#[derive(Debug, Clone, Copy)]
272pub enum NominalDef<R: TreeRoot = Arc<SyntaxRoot>> {
273 StructDef(StructDef<R>),
274 EnumDef(EnumDef<R>),
275}
276
277impl<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
293impl<R: TreeRoot> NominalDef<R> {}
294
270// ParenType 295// ParenType
271#[derive(Debug, Clone, Copy)] 296#[derive(Debug, Clone, Copy)]
272pub struct ParenType<R: TreeRoot = Arc<SyntaxRoot>> { 297pub 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
103pub fn handle_workspace_symbol( 106pub 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
136pub fn handle_goto_definition( 147pub 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)]
209enum ActionId { 220enum ActionId {
210 FlipComma 221 FlipComma,
222 AddDerive,
211} 223}
212 224
213impl ActionId { 225impl 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}