1085: add ast::tokens r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-04-02 07:50:09 +00:00
commit d21a677715
11 changed files with 293 additions and 277 deletions

View file

@ -5,7 +5,7 @@ use crate::{Assist, AssistId, AssistCtx};
use hir::Resolver; use hir::Resolver;
use hir::db::HirDatabase; use hir::db::HirDatabase;
use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; 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_db::FilePosition;
use ra_fmt::{leading_indent, reindent}; use ra_fmt::{leading_indent, reindent};

View file

@ -1,14 +1,9 @@
use hir::{ use hir::{
db::HirDatabase, db::HirDatabase,
source_binder::function_from_child_node source_binder::function_from_child_node,
}; };
use ra_syntax::{ use ra_syntax::{
ast::{ ast::{self, AstNode, AstToken, PatKind, ExprKind},
self,
AstNode,
PatKind,
ExprKind
},
TextRange, TextRange,
}; };

View file

@ -2,9 +2,8 @@
//! //!
use itertools::Itertools; use itertools::Itertools;
use ra_syntax::{ use ra_syntax::{
AstNode,
SyntaxNode, SyntaxKind::*, SyntaxToken, SyntaxKind, SyntaxNode, SyntaxKind::*, SyntaxToken, SyntaxKind,
ast, ast::{self, AstNode, AstToken},
algo::generate, algo::generate,
}; };

View file

@ -1,6 +1,6 @@
--- ---
created: "2019-02-18T09:22:24.062138085Z" created: "2019-04-02T07:43:12.954637543Z"
creator: insta@0.6.2 creator: insta@0.7.4
source: crates/ra_ide_api/src/completion/completion_item.rs source: crates/ra_ide_api/src/completion/completion_item.rs
expression: kind_completions expression: kind_completions
--- ---
@ -33,6 +33,9 @@ expression: kind_completions
delete: [180; 180), delete: [180; 180),
insert: "S", insert: "S",
kind: EnumVariant, kind: EnumVariant,
detail: "(S)" detail: "(S)",
documentation: Documentation(
""
)
} }
] ]

View file

@ -1,9 +1,9 @@
use ra_db::SourceDatabase; use ra_db::SourceDatabase;
use ra_syntax::{ use ra_syntax::{
Direction, SyntaxNode, TextRange, TextUnit, AstNode, SyntaxElement, Direction, SyntaxNode, TextRange, TextUnit, SyntaxElement,
algo::{find_covering_element, find_token_at_offset, TokenAtOffset}, algo::{find_covering_element, find_token_at_offset, TokenAtOffset},
SyntaxKind::*, SyntaxToken, SyntaxKind::*, SyntaxToken,
ast::Comment, ast::{self, AstNode, AstToken},
}; };
use crate::{FileRange, db::RootDatabase}; use crate::{FileRange, db::RootDatabase};
@ -55,7 +55,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
if token.range() != range { if token.range() != range {
return Some(token.range()); return Some(token.range());
} }
if let Some(comment) = Comment::cast(token) { if let Some(comment) = ast::Comment::cast(token) {
if let Some(range) = extend_comments(comment) { if let Some(range) = extend_comments(comment) {
return Some(range); return Some(range);
} }
@ -176,7 +176,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
None None
} }
fn extend_comments(comment: Comment) -> Option<TextRange> { fn extend_comments(comment: ast::Comment) -> Option<TextRange> {
let prev = adj_comments(comment, Direction::Prev); let prev = adj_comments(comment, Direction::Prev);
let next = adj_comments(comment, Direction::Next); let next = adj_comments(comment, Direction::Next);
if prev != next { if prev != next {
@ -186,14 +186,14 @@ fn extend_comments(comment: Comment) -> Option<TextRange> {
} }
} }
fn adj_comments(comment: Comment, dir: Direction) -> Comment { fn adj_comments(comment: ast::Comment, dir: Direction) -> ast::Comment {
let mut res = comment; let mut res = comment;
for element in comment.syntax().siblings_with_tokens(dir) { for element in comment.syntax().siblings_with_tokens(dir) {
let token = match element.as_token() { let token = match element.as_token() {
None => break, None => break,
Some(token) => token, Some(token) => token,
}; };
if let Some(c) = Comment::cast(token) { if let Some(c) = ast::Comment::cast(token) {
res = c res = c
} else if token.kind() != WHITESPACE || token.text().contains("\n\n") { } else if token.kind() != WHITESPACE || token.text().contains("\n\n") {
break; break;

View file

@ -1,9 +1,9 @@
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use ra_syntax::{ use ra_syntax::{
AstNode, SourceFile, SyntaxNode, TextRange, Direction, SyntaxElement, SourceFile, SyntaxNode, TextRange, Direction, SyntaxElement,
SyntaxKind::{self, *}, SyntaxKind::{self, *},
ast::{self, VisibilityOwner, Comment}, ast::{self, AstNode, AstToken, VisibilityOwner},
}; };
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@ -139,8 +139,8 @@ fn contiguous_range_for_group_unless<'a>(
} }
fn contiguous_range_for_comment<'a>( fn contiguous_range_for_comment<'a>(
first: Comment<'a>, first: ast::Comment<'a>,
visited: &mut FxHashSet<Comment<'a>>, visited: &mut FxHashSet<ast::Comment<'a>>,
) -> Option<TextRange> { ) -> Option<TextRange> {
visited.insert(first); visited.insert(first);
@ -157,7 +157,7 @@ fn contiguous_range_for_comment<'a>(
continue; continue;
} }
} }
if let Some(c) = Comment::cast(token) { if let Some(c) = ast::Comment::cast(token) {
if c.flavor() == group_flavor { if c.flavor() == group_flavor {
visited.insert(c); visited.insert(c);
last = c; last = c;

View file

@ -1,9 +1,9 @@
use itertools::Itertools; use itertools::Itertools;
use ra_syntax::{ 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}, SyntaxKind::{self, WHITESPACE, COMMA, R_CURLY, R_PAREN, R_BRACK},
algo::{find_covering_element, non_trivia_sibling}, algo::{find_covering_element, non_trivia_sibling},
ast, ast::{self, AstNode, AstToken},
Direction, Direction,
}; };
use ra_fmt::{ use ra_fmt::{

View file

@ -2,7 +2,7 @@ use ra_syntax::{
AstNode, SourceFile, SyntaxKind::*, AstNode, SourceFile, SyntaxKind::*,
TextUnit, TextRange, SyntaxToken, TextUnit, TextRange, SyntaxToken,
algo::{find_node_at_offset, find_token_at_offset, TokenAtOffset}, algo::{find_node_at_offset, find_token_at_offset, TokenAtOffset},
ast::{self}, ast::{self, AstToken},
}; };
use ra_fmt::leading_indent; use ra_fmt::leading_indent;
use ra_text_edit::{TextEdit, TextEditBuilder}; use ra_text_edit::{TextEdit, TextEditBuilder};

View file

@ -1,17 +1,24 @@
//! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s
mod generated; mod generated;
mod traits;
mod tokens;
use std::marker::PhantomData; use std::marker::PhantomData;
use itertools::Itertools; use itertools::Itertools;
pub use self::generated::*;
use crate::{ use crate::{
syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken, SyntaxElement, SyntaxElementChildren}, syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken, SyntaxElement},
SmolStr, SmolStr,
SyntaxKind::*, SyntaxKind::*,
}; };
pub use self::{
generated::*,
traits::*,
tokens::*,
};
/// The main trait to go from untyped `SyntaxNode` to a typed ast. The /// 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 /// 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 /// the same representation: a pointer to the tree root and a pointer to the
@ -25,134 +32,32 @@ pub trait AstNode:
fn syntax(&self) -> &SyntaxNode; 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<FnDef> {
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<ModuleItem> {
children(self)
}
fn items_with_macros(&self) -> ItemOrMacroIter {
ItemOrMacroIter(self.syntax().children())
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>); pub struct AstChildren<'a, N> {
inner: SyntaxNodeChildren<'a>,
ph: PhantomData<N>,
}
impl<'a> Iterator for ItemOrMacroIter<'a> { impl<'a, N> AstChildren<'a, N> {
type Item = ItemOrMacro<'a>; fn new(parent: &'a SyntaxNode) -> Self {
fn next(&mut self) -> Option<ItemOrMacro<'a>> { AstChildren { inner: parent.children(), ph: PhantomData }
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 { impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> {
fn type_param_list(&self) -> Option<&TypeParamList> { type Item = &'a N;
child_opt(self) fn next(&mut self) -> Option<&'a N> {
} self.inner.by_ref().find_map(N::cast)
fn where_clause(&self) -> Option<&WhereClause> {
child_opt(self)
} }
} }
pub trait TypeBoundsOwner: AstNode { pub trait AstToken<'a> {
fn type_bound_list(&self) -> Option<&TypeBoundList> { fn cast(token: SyntaxToken<'a>) -> Option<Self>
child_opt(self) where
} Self: Sized;
} fn syntax(&self) -> SyntaxToken<'a>;
fn text(&self) -> &'a SmolStr {
pub trait AttrsOwner: AstNode { self.syntax().text()
fn attrs(&self) -> AstChildren<Attr> {
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<std::string::String> {
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)
}
} }
} }
@ -203,111 +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<Self> {
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()
}
}
pub struct CommentIter<'a> {
iter: SyntaxElementChildren<'a>,
}
impl<'a> Iterator for CommentIter<'a> {
type Item = Comment<'a>;
fn next(&mut self) -> Option<Comment<'a>> {
self.iter.by_ref().find_map(|el| el.as_token().and_then(Comment::cast))
}
}
#[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<Self> {
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 { impl Name {
pub fn text(&self) -> &SmolStr { pub fn text(&self) -> &SmolStr {
let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap(); let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap();
@ -468,29 +268,6 @@ fn children<P: AstNode, C: AstNode>(parent: &P) -> AstChildren<C> {
AstChildren::new(parent.syntax()) AstChildren::new(parent.syntax())
} }
#[derive(Debug)]
pub struct AstChildren<'a, N> {
inner: SyntaxNodeChildren<'a>,
ph: PhantomData<N>,
}
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)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructFlavor<'a> { pub enum StructFlavor<'a> {
Tuple(&'a PosFieldDefList), Tuple(&'a PosFieldDefList),

View file

@ -0,0 +1,92 @@
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<Self> {
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::OuterDoc
} else if text.starts_with("//!") {
CommentFlavor::InnerDoc
} 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,
OuterDoc,
InnerDoc,
Multiline,
}
impl CommentFlavor {
pub fn prefix(&self) -> &'static str {
match *self {
CommentFlavor::Line => "//",
CommentFlavor::OuterDoc => "///",
CommentFlavor::InnerDoc => "//!",
CommentFlavor::Multiline => "/*",
}
}
pub fn is_doc_comment(&self) -> bool {
match self {
CommentFlavor::OuterDoc | CommentFlavor::InnerDoc => true,
_ => false,
}
}
}
pub struct Whitespace<'a>(SyntaxToken<'a>);
impl<'a> AstToken<'a> for Whitespace<'a> {
fn cast(token: SyntaxToken<'a>) -> Option<Self> {
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'))
}
}

View file

@ -0,0 +1,150 @@
use itertools::Itertools;
use crate::{
syntax_node::{SyntaxNodeChildren, SyntaxElementChildren},
ast::{self, child_opt, children, AstNode, AstToken, 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<ast::FnDef> {
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<ast::ModuleItem> {
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<ItemOrMacro<'a>> {
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<ast::Attr> {
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<String> {
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();
// 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 has_comments {
Some(docs)
} else {
None
}
}
}
pub struct CommentIter<'a> {
iter: SyntaxElementChildren<'a>,
}
impl<'a> Iterator for CommentIter<'a> {
type Item = ast::Comment<'a>;
fn next(&mut self) -> Option<ast::Comment<'a>> {
self.iter.by_ref().find_map(|el| el.as_token().and_then(ast::Comment::cast))
}
}