From bd1f5ba222a1f5a44c20a9fcb70c3785a3758b20 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Apr 2019 10:03:19 +0300 Subject: move ast traits to a separate file --- crates/ra_syntax/src/ast.rs | 151 ++----------------------------------- crates/ra_syntax/src/ast/traits.rs | 148 ++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 144 deletions(-) create mode 100644 crates/ra_syntax/src/ast/traits.rs (limited to 'crates') diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index ffd115cef..3e81fa990 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -1,17 +1,22 @@ //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s mod generated; +mod traits; use std::marker::PhantomData; use itertools::Itertools; -pub use self::generated::*; use crate::{ - syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken, SyntaxElement, SyntaxElementChildren}, + syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken, SyntaxElement}, SmolStr, SyntaxKind::*, }; +pub use self::{ + generated::*, + traits::*, +}; + /// The main trait to go from untyped `SyntaxNode` to a typed ast. The /// conversion itself has zero runtime cost: ast and syntax nodes have exactly /// the same representation: a pointer to the tree root and a pointer to the @@ -25,137 +30,6 @@ pub trait AstNode: fn syntax(&self) -> &SyntaxNode; } -pub trait TypeAscriptionOwner: AstNode { - fn ascribed_type(&self) -> Option<&TypeRef> { - child_opt(self) - } -} - -pub trait NameOwner: AstNode { - fn name(&self) -> Option<&Name> { - child_opt(self) - } -} - -pub trait VisibilityOwner: AstNode { - fn visibility(&self) -> Option<&Visibility> { - child_opt(self) - } -} - -pub trait LoopBodyOwner: AstNode { - fn loop_body(&self) -> Option<&Block> { - child_opt(self) - } -} - -pub trait ArgListOwner: AstNode { - fn arg_list(&self) -> Option<&ArgList> { - child_opt(self) - } -} - -pub trait FnDefOwner: AstNode { - fn functions(&self) -> AstChildren { - children(self) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ItemOrMacro<'a> { - Item(&'a ModuleItem), - Macro(&'a MacroCall), -} - -pub trait ModuleItemOwner: AstNode { - fn items(&self) -> AstChildren { - children(self) - } - fn items_with_macros(&self) -> ItemOrMacroIter { - ItemOrMacroIter(self.syntax().children()) - } -} - -#[derive(Debug)] -pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>); - -impl<'a> Iterator for ItemOrMacroIter<'a> { - type Item = ItemOrMacro<'a>; - fn next(&mut self) -> Option> { - loop { - let n = self.0.next()?; - if let Some(item) = ModuleItem::cast(n) { - return Some(ItemOrMacro::Item(item)); - } - if let Some(call) = MacroCall::cast(n) { - return Some(ItemOrMacro::Macro(call)); - } - } - } -} - -pub trait TypeParamsOwner: AstNode { - fn type_param_list(&self) -> Option<&TypeParamList> { - child_opt(self) - } - - fn where_clause(&self) -> Option<&WhereClause> { - child_opt(self) - } -} - -pub trait TypeBoundsOwner: AstNode { - fn type_bound_list(&self) -> Option<&TypeBoundList> { - child_opt(self) - } -} - -pub trait AttrsOwner: AstNode { - fn attrs(&self) -> AstChildren { - children(self) - } - fn has_atom_attr(&self, atom: &str) -> bool { - self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom) - } -} - -pub trait DocCommentsOwner: AstNode { - fn doc_comments(&self) -> CommentIter { - CommentIter { iter: self.syntax().children_with_tokens() } - } - - /// Returns the textual content of a doc comment block as a single string. - /// That is, strips leading `///` (+ optional 1 character of whitespace) - /// and joins lines. - fn doc_comment_text(&self) -> Option { - let docs = self - .doc_comments() - .filter(|comment| comment.is_doc_comment()) - .map(|comment| { - let prefix_len = comment.prefix().len(); - - let line = comment.text().as_str(); - - // Determine if the prefix or prefix + 1 char is stripped - let pos = - if line.chars().nth(prefix_len).map(|c| c.is_whitespace()).unwrap_or(false) { - prefix_len + 1 - } else { - prefix_len - }; - - line[pos..].to_owned() - }) - .join("\n"); - - if docs.is_empty() { - None - } else { - Some(docs) - } - } -} - impl Attr { pub fn is_inner(&self) -> bool { let tt = match self.value() { @@ -245,17 +119,6 @@ impl<'a> Comment<'a> { } } -pub struct CommentIter<'a> { - iter: SyntaxElementChildren<'a>, -} - -impl<'a> Iterator for CommentIter<'a> { - type Item = Comment<'a>; - fn next(&mut self) -> Option> { - self.iter.by_ref().find_map(|el| el.as_token().and_then(Comment::cast)) - } -} - #[derive(Debug, PartialEq, Eq)] pub enum CommentFlavor { Line, diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs new file mode 100644 index 000000000..85fe6d5e1 --- /dev/null +++ b/crates/ra_syntax/src/ast/traits.rs @@ -0,0 +1,148 @@ +use itertools::Itertools; + +use crate::{ + syntax_node::{SyntaxNodeChildren, SyntaxElementChildren}, + ast::{self, child_opt, children, AstNode, AstChildren}, +}; + +pub trait TypeAscriptionOwner: AstNode { + fn ascribed_type(&self) -> Option<&ast::TypeRef> { + child_opt(self) + } +} + +pub trait NameOwner: AstNode { + fn name(&self) -> Option<&ast::Name> { + child_opt(self) + } +} + +pub trait VisibilityOwner: AstNode { + fn visibility(&self) -> Option<&ast::Visibility> { + child_opt(self) + } +} + +pub trait LoopBodyOwner: AstNode { + fn loop_body(&self) -> Option<&ast::Block> { + child_opt(self) + } +} + +pub trait ArgListOwner: AstNode { + fn arg_list(&self) -> Option<&ast::ArgList> { + child_opt(self) + } +} + +pub trait FnDefOwner: AstNode { + fn functions(&self) -> AstChildren { + children(self) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ItemOrMacro<'a> { + Item(&'a ast::ModuleItem), + Macro(&'a ast::MacroCall), +} + +pub trait ModuleItemOwner: AstNode { + fn items(&self) -> AstChildren { + children(self) + } + fn items_with_macros(&self) -> ItemOrMacroIter { + ItemOrMacroIter(self.syntax().children()) + } +} + +#[derive(Debug)] +pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>); + +impl<'a> Iterator for ItemOrMacroIter<'a> { + type Item = ItemOrMacro<'a>; + fn next(&mut self) -> Option> { + loop { + let n = self.0.next()?; + if let Some(item) = ast::ModuleItem::cast(n) { + return Some(ItemOrMacro::Item(item)); + } + if let Some(call) = ast::MacroCall::cast(n) { + return Some(ItemOrMacro::Macro(call)); + } + } + } +} + +pub trait TypeParamsOwner: AstNode { + fn type_param_list(&self) -> Option<&ast::TypeParamList> { + child_opt(self) + } + + fn where_clause(&self) -> Option<&ast::WhereClause> { + child_opt(self) + } +} + +pub trait TypeBoundsOwner: AstNode { + fn type_bound_list(&self) -> Option<&ast::TypeBoundList> { + child_opt(self) + } +} + +pub trait AttrsOwner: AstNode { + fn attrs(&self) -> AstChildren { + children(self) + } + fn has_atom_attr(&self, atom: &str) -> bool { + self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom) + } +} + +pub trait DocCommentsOwner: AstNode { + fn doc_comments(&self) -> CommentIter { + CommentIter { iter: self.syntax().children_with_tokens() } + } + + /// Returns the textual content of a doc comment block as a single string. + /// That is, strips leading `///` (+ optional 1 character of whitespace) + /// and joins lines. + fn doc_comment_text(&self) -> Option { + let docs = self + .doc_comments() + .filter(|comment| comment.is_doc_comment()) + .map(|comment| { + let prefix_len = comment.prefix().len(); + + let line = comment.text().as_str(); + + // Determine if the prefix or prefix + 1 char is stripped + let pos = + if line.chars().nth(prefix_len).map(|c| c.is_whitespace()).unwrap_or(false) { + prefix_len + 1 + } else { + prefix_len + }; + + line[pos..].to_owned() + }) + .join("\n"); + + if docs.is_empty() { + None + } else { + Some(docs) + } + } +} + +pub struct CommentIter<'a> { + iter: SyntaxElementChildren<'a>, +} + +impl<'a> Iterator for CommentIter<'a> { + type Item = ast::Comment<'a>; + fn next(&mut self) -> Option> { + self.iter.by_ref().find_map(|el| el.as_token().and_then(ast::Comment::cast)) + } +} -- cgit v1.2.3 From f874d372bb7f756dfa6ebc22ca838657c8ff1539 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Apr 2019 10:09:52 +0300 Subject: simplify --- crates/ra_syntax/src/ast.rs | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'crates') diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 3e81fa990..9950ab12d 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -30,6 +30,25 @@ pub trait AstNode: fn syntax(&self) -> &SyntaxNode; } +#[derive(Debug)] +pub struct AstChildren<'a, N> { + inner: SyntaxNodeChildren<'a>, + ph: PhantomData, +} + +impl<'a, N> AstChildren<'a, N> { + fn new(parent: &'a SyntaxNode) -> Self { + AstChildren { inner: parent.children(), ph: PhantomData } + } +} + +impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> { + type Item = &'a N; + fn next(&mut self) -> Option<&'a N> { + self.inner.by_ref().find_map(N::cast) + } +} + impl Attr { pub fn is_inner(&self) -> bool { let tt = match self.value() { @@ -331,29 +350,6 @@ fn children(parent: &P) -> AstChildren { AstChildren::new(parent.syntax()) } -#[derive(Debug)] -pub struct AstChildren<'a, N> { - inner: SyntaxNodeChildren<'a>, - ph: PhantomData, -} - -impl<'a, N> AstChildren<'a, N> { - fn new(parent: &'a SyntaxNode) -> Self { - AstChildren { inner: parent.children(), ph: PhantomData } - } -} - -impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> { - type Item = &'a N; - fn next(&mut self) -> Option<&'a N> { - loop { - if let Some(n) = N::cast(self.inner.next()?) { - return Some(n); - } - } - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub enum StructFlavor<'a> { Tuple(&'a PosFieldDefList), -- cgit v1.2.3 From ae282d8da63a82077361bc142b2b9a272a2eac64 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Apr 2019 10:23:18 +0300 Subject: add ast::tokens --- crates/ra_assists/src/add_missing_impl_members.rs | 2 +- crates/ra_assists/src/inline_local_variable.rs | 9 +- crates/ra_fmt/src/lib.rs | 3 +- crates/ra_ide_api/src/extend_selection.rs | 12 +-- crates/ra_ide_api/src/folding_ranges.rs | 10 +- crates/ra_ide_api/src/join_lines.rs | 4 +- crates/ra_ide_api/src/typing.rs | 2 +- crates/ra_syntax/src/ast.rs | 106 +++------------------- crates/ra_syntax/src/ast/tokens.rs | 93 +++++++++++++++++++ crates/ra_syntax/src/ast/traits.rs | 2 +- 10 files changed, 124 insertions(+), 119 deletions(-) create mode 100644 crates/ra_syntax/src/ast/tokens.rs (limited to 'crates') diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs index 5b01e898e..19a2d05bc 100644 --- a/crates/ra_assists/src/add_missing_impl_members.rs +++ b/crates/ra_assists/src/add_missing_impl_members.rs @@ -5,7 +5,7 @@ use crate::{Assist, AssistId, AssistCtx}; use hir::Resolver; use hir::db::HirDatabase; use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; -use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; +use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner}; use ra_db::FilePosition; use ra_fmt::{leading_indent, reindent}; diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs index 0d7b884b8..950c2910b 100644 --- a/crates/ra_assists/src/inline_local_variable.rs +++ b/crates/ra_assists/src/inline_local_variable.rs @@ -1,14 +1,9 @@ use hir::{ db::HirDatabase, - source_binder::function_from_child_node + source_binder::function_from_child_node, }; use ra_syntax::{ - ast::{ - self, - AstNode, - PatKind, - ExprKind - }, + ast::{self, AstNode, AstToken, PatKind, ExprKind}, TextRange, }; diff --git a/crates/ra_fmt/src/lib.rs b/crates/ra_fmt/src/lib.rs index ea90dc2b8..85b7ce250 100644 --- a/crates/ra_fmt/src/lib.rs +++ b/crates/ra_fmt/src/lib.rs @@ -2,9 +2,8 @@ //! use itertools::Itertools; use ra_syntax::{ - AstNode, SyntaxNode, SyntaxKind::*, SyntaxToken, SyntaxKind, - ast, + ast::{self, AstNode, AstToken}, algo::generate, }; diff --git a/crates/ra_ide_api/src/extend_selection.rs b/crates/ra_ide_api/src/extend_selection.rs index e743bf0fe..7293ba359 100644 --- a/crates/ra_ide_api/src/extend_selection.rs +++ b/crates/ra_ide_api/src/extend_selection.rs @@ -1,9 +1,9 @@ use ra_db::SourceDatabase; use ra_syntax::{ - Direction, SyntaxNode, TextRange, TextUnit, AstNode, SyntaxElement, + Direction, SyntaxNode, TextRange, TextUnit, SyntaxElement, algo::{find_covering_element, find_token_at_offset, TokenAtOffset}, SyntaxKind::*, SyntaxToken, - ast::Comment, + ast::{self, AstNode, AstToken}, }; use crate::{FileRange, db::RootDatabase}; @@ -55,7 +55,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option Option { None } -fn extend_comments(comment: Comment) -> Option { +fn extend_comments(comment: ast::Comment) -> Option { let prev = adj_comments(comment, Direction::Prev); let next = adj_comments(comment, Direction::Next); if prev != next { @@ -186,14 +186,14 @@ fn extend_comments(comment: Comment) -> Option { } } -fn adj_comments(comment: Comment, dir: Direction) -> Comment { +fn adj_comments(comment: ast::Comment, dir: Direction) -> ast::Comment { let mut res = comment; for element in comment.syntax().siblings_with_tokens(dir) { let token = match element.as_token() { None => break, Some(token) => token, }; - if let Some(c) = Comment::cast(token) { + if let Some(c) = ast::Comment::cast(token) { res = c } else if token.kind() != WHITESPACE || token.text().contains("\n\n") { break; diff --git a/crates/ra_ide_api/src/folding_ranges.rs b/crates/ra_ide_api/src/folding_ranges.rs index a6fe8a5d5..eada0b7de 100644 --- a/crates/ra_ide_api/src/folding_ranges.rs +++ b/crates/ra_ide_api/src/folding_ranges.rs @@ -1,9 +1,9 @@ use rustc_hash::FxHashSet; use ra_syntax::{ - AstNode, SourceFile, SyntaxNode, TextRange, Direction, SyntaxElement, + SourceFile, SyntaxNode, TextRange, Direction, SyntaxElement, SyntaxKind::{self, *}, - ast::{self, VisibilityOwner, Comment}, + ast::{self, AstNode, AstToken, VisibilityOwner}, }; #[derive(Debug, PartialEq, Eq)] @@ -139,8 +139,8 @@ fn contiguous_range_for_group_unless<'a>( } fn contiguous_range_for_comment<'a>( - first: Comment<'a>, - visited: &mut FxHashSet>, + first: ast::Comment<'a>, + visited: &mut FxHashSet>, ) -> Option { visited.insert(first); @@ -157,7 +157,7 @@ fn contiguous_range_for_comment<'a>( continue; } } - if let Some(c) = Comment::cast(token) { + if let Some(c) = ast::Comment::cast(token) { if c.flavor() == group_flavor { visited.insert(c); last = c; diff --git a/crates/ra_ide_api/src/join_lines.rs b/crates/ra_ide_api/src/join_lines.rs index 57b6f8384..598717311 100644 --- a/crates/ra_ide_api/src/join_lines.rs +++ b/crates/ra_ide_api/src/join_lines.rs @@ -1,9 +1,9 @@ use itertools::Itertools; use ra_syntax::{ - SourceFile, TextRange, TextUnit, AstNode, SyntaxNode, SyntaxElement, SyntaxToken, + SourceFile, TextRange, TextUnit, SyntaxNode, SyntaxElement, SyntaxToken, SyntaxKind::{self, WHITESPACE, COMMA, R_CURLY, R_PAREN, R_BRACK}, algo::{find_covering_element, non_trivia_sibling}, - ast, + ast::{self, AstNode, AstToken}, Direction, }; use ra_fmt::{ diff --git a/crates/ra_ide_api/src/typing.rs b/crates/ra_ide_api/src/typing.rs index 4510d663d..aeeeea082 100644 --- a/crates/ra_ide_api/src/typing.rs +++ b/crates/ra_ide_api/src/typing.rs @@ -2,7 +2,7 @@ use ra_syntax::{ AstNode, SourceFile, SyntaxKind::*, TextUnit, TextRange, SyntaxToken, algo::{find_node_at_offset, find_token_at_offset, TokenAtOffset}, - ast::{self}, + ast::{self, AstToken}, }; use ra_fmt::leading_indent; use ra_text_edit::{TextEdit, TextEditBuilder}; diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 9950ab12d..beef2c6e2 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -1,6 +1,7 @@ //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s mod generated; mod traits; +mod tokens; use std::marker::PhantomData; @@ -15,6 +16,7 @@ use crate::{ pub use self::{ generated::*, traits::*, + tokens::*, }; /// The main trait to go from untyped `SyntaxNode` to a typed ast. The @@ -49,6 +51,16 @@ impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> { } } +pub trait AstToken<'a> { + fn cast(token: SyntaxToken<'a>) -> Option + where + Self: Sized; + fn syntax(&self) -> SyntaxToken<'a>; + fn text(&self) -> &'a SmolStr { + self.syntax().text() + } +} + impl Attr { pub fn is_inner(&self) -> bool { let tt = match self.value() { @@ -96,100 +108,6 @@ impl Attr { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Comment<'a>(SyntaxToken<'a>); - -impl<'a> Comment<'a> { - pub fn cast(token: SyntaxToken<'a>) -> Option { - if token.kind() == COMMENT { - Some(Comment(token)) - } else { - None - } - } - - pub fn syntax(&self) -> SyntaxToken<'a> { - self.0 - } - - pub fn text(&self) -> &'a SmolStr { - self.0.text() - } - - pub fn flavor(&self) -> CommentFlavor { - let text = self.text(); - if text.starts_with("///") { - CommentFlavor::Doc - } else if text.starts_with("//!") { - CommentFlavor::ModuleDoc - } else if text.starts_with("//") { - CommentFlavor::Line - } else { - CommentFlavor::Multiline - } - } - - pub fn is_doc_comment(&self) -> bool { - self.flavor().is_doc_comment() - } - - pub fn prefix(&self) -> &'static str { - self.flavor().prefix() - } -} - -#[derive(Debug, PartialEq, Eq)] -pub enum CommentFlavor { - Line, - Doc, - ModuleDoc, - Multiline, -} - -impl CommentFlavor { - pub fn prefix(&self) -> &'static str { - use self::CommentFlavor::*; - match *self { - Line => "//", - Doc => "///", - ModuleDoc => "//!", - Multiline => "/*", - } - } - - pub fn is_doc_comment(&self) -> bool { - match self { - CommentFlavor::Doc | CommentFlavor::ModuleDoc => true, - _ => false, - } - } -} - -pub struct Whitespace<'a>(SyntaxToken<'a>); - -impl<'a> Whitespace<'a> { - pub fn cast(token: SyntaxToken<'a>) -> Option { - if token.kind() == WHITESPACE { - Some(Whitespace(token)) - } else { - None - } - } - - pub fn syntax(&self) -> SyntaxToken<'a> { - self.0 - } - - pub fn text(&self) -> &'a SmolStr { - self.0.text() - } - - pub fn spans_multiple_lines(&self) -> bool { - let text = self.text(); - text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) - } -} - impl Name { pub fn text(&self) -> &SmolStr { let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap(); diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs new file mode 100644 index 000000000..c830cdccf --- /dev/null +++ b/crates/ra_syntax/src/ast/tokens.rs @@ -0,0 +1,93 @@ +use crate::{ + SyntaxToken, + SyntaxKind::{COMMENT, WHITESPACE}, + ast::AstToken, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Comment<'a>(SyntaxToken<'a>); + +impl<'a> AstToken<'a> for Comment<'a> { + fn cast(token: SyntaxToken<'a>) -> Option { + if token.kind() == COMMENT { + Some(Comment(token)) + } else { + None + } + } + fn syntax(&self) -> SyntaxToken<'a> { + self.0 + } +} + +impl<'a> Comment<'a> { + pub fn flavor(&self) -> CommentFlavor { + let text = self.text(); + if text.starts_with("///") { + CommentFlavor::Doc + } else if text.starts_with("//!") { + CommentFlavor::ModuleDoc + } else if text.starts_with("//") { + CommentFlavor::Line + } else { + CommentFlavor::Multiline + } + } + + pub fn is_doc_comment(&self) -> bool { + self.flavor().is_doc_comment() + } + + pub fn prefix(&self) -> &'static str { + self.flavor().prefix() + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum CommentFlavor { + Line, + Doc, + ModuleDoc, + Multiline, +} + +impl CommentFlavor { + pub fn prefix(&self) -> &'static str { + use self::CommentFlavor::*; + match *self { + Line => "//", + Doc => "///", + ModuleDoc => "//!", + Multiline => "/*", + } + } + + pub fn is_doc_comment(&self) -> bool { + match self { + CommentFlavor::Doc | CommentFlavor::ModuleDoc => true, + _ => false, + } + } +} + +pub struct Whitespace<'a>(SyntaxToken<'a>); + +impl<'a> AstToken<'a> for Whitespace<'a> { + fn cast(token: SyntaxToken<'a>) -> Option { + if token.kind() == WHITESPACE { + Some(Whitespace(token)) + } else { + None + } + } + fn syntax(&self) -> SyntaxToken<'a> { + self.0 + } +} + +impl<'a> Whitespace<'a> { + pub fn spans_multiple_lines(&self) -> bool { + let text = self.text(); + text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) + } +} diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index 85fe6d5e1..f9021d7bf 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use crate::{ syntax_node::{SyntaxNodeChildren, SyntaxElementChildren}, - ast::{self, child_opt, children, AstNode, AstChildren}, + ast::{self, child_opt, children, AstNode, AstToken, AstChildren}, }; pub trait TypeAscriptionOwner: AstNode { -- cgit v1.2.3 From 99e6438660d665c9aa4d2831560ba2287becb13f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Apr 2019 10:34:34 +0300 Subject: allow empty doc comments --- crates/ra_syntax/src/ast/traits.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'crates') diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index f9021d7bf..43d1509fa 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs @@ -107,11 +107,13 @@ pub trait DocCommentsOwner: AstNode { /// Returns the textual content of a doc comment block as a single string. /// That is, strips leading `///` (+ optional 1 character of whitespace) /// and joins lines. - fn doc_comment_text(&self) -> Option { + fn doc_comment_text(&self) -> Option { + let mut has_comments = false; let docs = self .doc_comments() .filter(|comment| comment.is_doc_comment()) .map(|comment| { + has_comments = true; let prefix_len = comment.prefix().len(); let line = comment.text().as_str(); @@ -128,10 +130,10 @@ pub trait DocCommentsOwner: AstNode { }) .join("\n"); - if docs.is_empty() { - None - } else { + if has_comments { Some(docs) + } else { + None } } } -- cgit v1.2.3 From 3f3ff2f0f4c188c606a96506325d96726c842239 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Apr 2019 10:48:59 +0300 Subject: fix comment naming --- .../completion_item__enum_variant_with_details.snap | 9 ++++++--- crates/ra_syntax/src/ast/tokens.rs | 19 +++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap index 70ea96e1b..daccd9fba 100644 --- a/crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap @@ -1,6 +1,6 @@ --- -created: "2019-02-18T09:22:24.062138085Z" -creator: insta@0.6.2 +created: "2019-04-02T07:43:12.954637543Z" +creator: insta@0.7.4 source: crates/ra_ide_api/src/completion/completion_item.rs expression: kind_completions --- @@ -33,6 +33,9 @@ expression: kind_completions delete: [180; 180), insert: "S", kind: EnumVariant, - detail: "(S)" + detail: "(S)", + documentation: Documentation( + "" + ) } ] diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index c830cdccf..76a12cd64 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs @@ -24,9 +24,9 @@ impl<'a> Comment<'a> { pub fn flavor(&self) -> CommentFlavor { let text = self.text(); if text.starts_with("///") { - CommentFlavor::Doc + CommentFlavor::OuterDoc } else if text.starts_with("//!") { - CommentFlavor::ModuleDoc + CommentFlavor::InnerDoc } else if text.starts_with("//") { CommentFlavor::Line } else { @@ -46,25 +46,24 @@ impl<'a> Comment<'a> { #[derive(Debug, PartialEq, Eq)] pub enum CommentFlavor { Line, - Doc, - ModuleDoc, + OuterDoc, + InnerDoc, Multiline, } impl CommentFlavor { pub fn prefix(&self) -> &'static str { - use self::CommentFlavor::*; match *self { - Line => "//", - Doc => "///", - ModuleDoc => "//!", - Multiline => "/*", + CommentFlavor::Line => "//", + CommentFlavor::OuterDoc => "///", + CommentFlavor::InnerDoc => "//!", + CommentFlavor::Multiline => "/*", } } pub fn is_doc_comment(&self) -> bool { match self { - CommentFlavor::Doc | CommentFlavor::ModuleDoc => true, + CommentFlavor::OuterDoc | CommentFlavor::InnerDoc => true, _ => false, } } -- cgit v1.2.3