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/traits.rs | 148 +++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 crates/ra_syntax/src/ast/traits.rs (limited to 'crates/ra_syntax/src/ast') 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 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_syntax/src/ast/tokens.rs | 93 ++++++++++++++++++++++++++++++++++++++ crates/ra_syntax/src/ast/traits.rs | 2 +- 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 crates/ra_syntax/src/ast/tokens.rs (limited to 'crates/ra_syntax/src/ast') 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/ra_syntax/src/ast') 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 --- crates/ra_syntax/src/ast/tokens.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'crates/ra_syntax/src/ast') 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