From 1141d448d960eedba0a5647d525910de706bf778 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 14 Aug 2018 13:33:44 +0300 Subject: Add derive intention --- crates/libeditor/src/code_actions.rs | 27 ++++++- crates/libeditor/src/lib.rs | 2 +- crates/libsyntax2/src/algo/search.rs | 136 -------------------------------- crates/libsyntax2/src/ast/generated.rs | 25 ++++++ crates/libsyntax2/src/grammar.ron | 4 +- crates/server/src/main_loop/handlers.rs | 107 ++++++++++++++----------- 6 files changed, 114 insertions(+), 187 deletions(-) delete mode 100644 crates/libsyntax2/src/algo/search.rs 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 @@ use {TextUnit, File, EditBuilder, Edit}; use libsyntax2::{ - ast::AstNode, + ast::{self, AstNode}, SyntaxKind::COMMA, SyntaxNodeRef, + SyntaxRoot, algo::{ Direction, siblings, - find_leaf_at_offset, + find_leaf_at_offset, ancestors, }, }; @@ -24,10 +25,32 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option(file: &'a File, offset: TextUnit) -> Option Edit + 'a> { + let syntax = file.syntax(); + let syntax = syntax.as_ref(); + let nominal = find_node::>(syntax, offset)?; + Some(move || { + let mut edit = EditBuilder::new(); + edit.insert(nominal.syntax().range().start(), "#[derive()]\n".to_string()); + edit.finish() + }) +} + fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option { siblings(node, direction) .skip(1) .find(|node| !node.kind().is_trivia()) } +fn find_non_trivia_leaf(syntax: SyntaxNodeRef, offset: TextUnit) -> Option { + find_leaf_at_offset(syntax, offset) + .find(|leaf| !leaf.kind().is_trivia()) +} + +fn find_node<'a, N: AstNode<&'a SyntaxRoot>>(syntax: SyntaxNodeRef<'a>, offset: TextUnit) -> Option { + let leaf = find_non_trivia_leaf(syntax, offset)?; + ancestors(leaf) + .filter_map(N::cast) + .next() +} 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::{ extend_selection::extend_selection, symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, edit::{EditBuilder, Edit, AtomEdit}, - code_actions::{flip_comma}, + code_actions::{flip_comma, add_derive}, }; #[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 @@ -use {Node, NodeType, TextUnit, TextRange}; -use ::visitor::{visitor, process_subtree_bottom_up}; - -pub fn child_of_type(node: Node, ty: NodeType) -> Option { - node.children().find(|n| n.ty() == ty) -} - -pub fn children_of_type<'f>(node: Node<'f>, ty: NodeType) -> Box> + 'f> { - Box::new(node.children().filter(move |n| n.ty() == ty)) -} - -pub fn subtree<'f>(node: Node<'f>) -> Box> + 'f> { - Box::new(node.children().flat_map(subtree).chain(::std::iter::once(node))) -} - -pub fn descendants_of_type<'f>(node: Node<'f>, ty: NodeType) -> Vec> { - process_subtree_bottom_up( - node, - visitor(Vec::new()) - .visit_nodes(&[ty], |node, nodes| nodes.push(node)) - ) -} - -pub fn child_of_type_exn(node: Node, ty: NodeType) -> Node { - child_of_type(node, ty).unwrap_or_else(|| { - panic!("No child of type {:?} for {:?}\ - ----\ - {}\ - ----", ty, node.ty(), node.text()) - }) -} - - -pub fn ancestors(node: Node) -> Ancestors { - Ancestors(Some(node)) -} - -pub struct Ancestors<'f>(Option>); - -impl<'f> Iterator for Ancestors<'f> { - type Item = Node<'f>; - - fn next(&mut self) -> Option { - let current = self.0; - self.0 = current.and_then(|n| n.parent()); - current - } -} - -pub fn is_leaf(node: Node) -> bool { - node.children().next().is_none() && !node.range().is_empty() -} - - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub enum Direction { - Left, Right -} - -pub fn sibling(node: Node, dir: Direction) -> Option { - let (parent, idx) = child_position(node)?; - let idx = match dir { - Direction::Left => idx.checked_sub(1)?, - Direction::Right => idx + 1, - }; - parent.children().nth(idx) -} - -pub mod ast { - use {Node, AstNode, TextUnit, AstChildren}; - use visitor::{visitor, process_subtree_bottom_up}; - use super::{ancestors, find_leaf_at_offset, LeafAtOffset}; - - pub fn ancestor<'f, T: AstNode<'f>>(node: Node<'f>) -> Option { - ancestors(node) - .filter_map(T::wrap) - .next() - } - - pub fn ancestor_exn<'f, T: AstNode<'f>>(node: Node<'f>) -> T { - ancestor(node).unwrap() - } - - pub fn children_of_type<'f, N: AstNode<'f>>(node: Node<'f>) -> AstChildren { - AstChildren::new(node.children()) - } - - pub fn descendants_of_type<'f, N: AstNode<'f>>(node: Node<'f>) -> Vec { - process_subtree_bottom_up( - node, - visitor(Vec::new()) - .visit::(|node, acc| acc.push(node)) - ) - } - - pub fn node_at_offset<'f, T: AstNode<'f>>(node: Node<'f>, offset: TextUnit) -> Option { - match find_leaf_at_offset(node, offset) { - LeafAtOffset::None => None, - LeafAtOffset::Single(node) => ancestor(node), - LeafAtOffset::Between(left, right) => ancestor(left).or_else(|| ancestor(right)), - } - } -} - -pub mod traversal { - use {Node}; - - pub fn bottom_up<'f, F: FnMut(Node<'f>)>(node: Node<'f>, mut f: F) - { - go(node, &mut f); - - fn go<'f, F: FnMut(Node<'f>)>(node: Node<'f>, f: &mut F) { - for child in node.children() { - go(child, f) - } - f(node); - } - } -} - -fn child_position(child: Node) -> Option<(Node, usize)> { - child.parent() - .map(|parent| { - (parent, parent.children().position(|n| n == child).unwrap()) - }) -} - -fn common_ancestor<'f>(n1: Node<'f>, n2: Node<'f>) -> Node<'f> { - for p in ancestors(n1) { - if ancestors(n2).any(|a| a == p) { - return p; - } - } - panic!("Can't find common ancestor of {:?} and {:?}", n1, n2) -} - 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 AstNode for NeverType { impl NeverType {} +// NominalDef +#[derive(Debug, Clone, Copy)] +pub enum NominalDef> { + StructDef(StructDef), + EnumDef(EnumDef), +} + +impl AstNode for NominalDef { + fn cast(syntax: SyntaxNode) -> Option { + match syntax.kind() { + STRUCT_DEF => Some(NominalDef::StructDef(StructDef { syntax })), + ENUM_DEF => Some(NominalDef::EnumDef(EnumDef { syntax })), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { + match self { + NominalDef::StructDef(inner) => inner.syntax(), + NominalDef::EnumDef(inner) => inner.syntax(), + } + } +} + +impl NominalDef {} + // ParenType #[derive(Debug, Clone, Copy)] pub struct ParenType> { 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( "ForType", "ImplTraitType", "DynTraitType", - ]) + ]), + + "NominalDef": ( enum: ["StructDef", "EnumDef"]), }, ) 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( let file = world.file_syntax(&path)?; let line_index = world.file_line_index(&path)?; let offset = params.range.conv_with(&line_index).start(); - let ret = if libeditor::flip_comma(&file, offset).is_some() { - let cmd = apply_code_action_cmd( - ActionId::FlipComma, - params.text_document, - offset, - ); - Some(vec![cmd]) - } else { - None - }; - Ok(ret) + let mut ret = Vec::new(); + + let actions = &[ + (ActionId::FlipComma, libeditor::flip_comma(&file, offset).is_some()), + (ActionId::AddDerive, libeditor::add_derive(&file, offset).is_some()), + ]; + + for (id, edit) in actions { + if *edit { + let cmd = apply_code_action_cmd(*id, params.text_document.clone(), offset); + ret.push(cmd); + } + } + return Ok(Some(ret)); } pub fn handle_workspace_symbol( world: World, params: req::WorkspaceSymbolParams, ) -> Result>> { - let mut acc = Vec::new(); - + let all_symbols = params.query.contains("#"); let query = { - let all_symbols = params.query.contains("#"); let query: String = params.query.chars() .filter(|&c| c != '#') .collect(); @@ -118,19 +119,29 @@ pub fn handle_workspace_symbol( q.limit(128); q }; + let mut res = exec_query(&world, query)?; + if res.is_empty() && !all_symbols { + let mut query = Query::new(params.query); + query.limit(128); + res = exec_query(&world, query)?; + } - for (path, symbol) in world.world_symbols(query) { - let line_index = world.file_line_index(path)?; - let info = SymbolInformation { - name: symbol.name.to_string(), - kind: symbol.kind.conv(), - location: (path, symbol.node_range).try_conv_with(&line_index)?, - container_name: None, - }; - acc.push(info); - }; + return Ok(Some(res)); - Ok(Some(acc)) + fn exec_query(world: &World, query: Query) -> Result> { + let mut res = Vec::new(); + for (path, symbol) in world.world_symbols(query) { + let line_index = world.file_line_index(path)?; + let info = SymbolInformation { + name: symbol.name.to_string(), + kind: symbol.kind.conv(), + location: (path, symbol.node_range).try_conv_with(&line_index)?, + container_name: None, + }; + res.push(info); + }; + Ok(res) + } } pub fn handle_goto_definition( @@ -161,28 +172,28 @@ pub fn handle_execute_command( } let arg = params.arguments.pop().unwrap(); let arg: ActionRequest = from_value(arg)?; - match arg.id { - ActionId::FlipComma => { - let path = arg.text_document.file_path()?; - let file = world.file_syntax(&path)?; - let line_index = world.file_line_index(&path)?; - let edit = match libeditor::flip_comma(&file, arg.offset) { - Some(edit) => edit(), - None => bail!("command not applicable"), - }; - let mut changes = HashMap::new(); - changes.insert( - arg.text_document.uri, - edit.conv_with(&line_index), - ); - let edit = WorkspaceEdit { - changes: Some(changes), - document_changes: None, - }; + let path = arg.text_document.file_path()?; + let file = world.file_syntax(&path)?; + let edit = match arg.id { + ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|edit| edit()), + ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|edit| edit()), + }; + let edit = match edit { + Some(edit) => edit, + None => bail!("command not applicable"), + }; + let line_index = world.file_line_index(&path)?; + let mut changes = HashMap::new(); + changes.insert( + arg.text_document.uri, + edit.conv_with(&line_index), + ); + let edit = WorkspaceEdit { + changes: Some(changes), + document_changes: None, + }; - Ok(req::ApplyWorkspaceEditParams { edit }) - } - } + Ok(req::ApplyWorkspaceEditParams { edit }) } #[derive(Serialize, Deserialize)] @@ -207,13 +218,15 @@ fn apply_code_action_cmd(id: ActionId, doc: TextDocumentIdentifier, offset: Text #[derive(Serialize, Deserialize, Clone, Copy)] enum ActionId { - FlipComma + FlipComma, + AddDerive, } impl ActionId { fn title(&self) -> &'static str { match *self { ActionId::FlipComma => "Flip `,`", + ActionId::AddDerive => "Add `#[derive]`", } } } -- cgit v1.2.3