switch to upstream rowan's API
This commit is contained in:
@@ -1,17 +1,18 @@
|
||||
pub mod visit;
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{AstNode, Direction, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextUnit};
|
||||
use crate::{
|
||||
AstNode, Direction, InsertPosition, NodeOrToken, SourceFile, SyntaxElement, SyntaxNode,
|
||||
SyntaxNodePtr, SyntaxToken, TextRange, TextUnit,
|
||||
};
|
||||
|
||||
pub use rowan::TokenAtOffset;
|
||||
|
||||
pub fn find_token_at_offset(node: &SyntaxNode, offset: TextUnit) -> TokenAtOffset<SyntaxToken> {
|
||||
match node.0.token_at_offset(offset) {
|
||||
TokenAtOffset::None => TokenAtOffset::None,
|
||||
TokenAtOffset::Single(n) => TokenAtOffset::Single(SyntaxToken(n)),
|
||||
TokenAtOffset::Between(l, r) => TokenAtOffset::Between(SyntaxToken(l), SyntaxToken(r)),
|
||||
}
|
||||
node.token_at_offset(offset)
|
||||
}
|
||||
|
||||
/// Returns ancestors of the node at the offset, sorted by length. This should
|
||||
@@ -44,20 +45,110 @@ pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextUnit) ->
|
||||
/// Finds the first sibling in the given direction which is not `trivia`
|
||||
pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> {
|
||||
return match element {
|
||||
SyntaxElement::Node(node) => node.siblings_with_tokens(direction).skip(1).find(not_trivia),
|
||||
SyntaxElement::Token(token) => {
|
||||
token.siblings_with_tokens(direction).skip(1).find(not_trivia)
|
||||
}
|
||||
NodeOrToken::Node(node) => node.siblings_with_tokens(direction).skip(1).find(not_trivia),
|
||||
NodeOrToken::Token(token) => token.siblings_with_tokens(direction).skip(1).find(not_trivia),
|
||||
};
|
||||
|
||||
fn not_trivia(element: &SyntaxElement) -> bool {
|
||||
match element {
|
||||
SyntaxElement::Node(_) => true,
|
||||
SyntaxElement::Token(token) => !token.kind().is_trivia(),
|
||||
NodeOrToken::Node(_) => true,
|
||||
NodeOrToken::Token(token) => !token.kind().is_trivia(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxElement {
|
||||
SyntaxElement::new(root.0.covering_node(range))
|
||||
root.covering_element(range)
|
||||
}
|
||||
|
||||
/// Adds specified children (tokens or nodes) to the current node at the
|
||||
/// specific position.
|
||||
///
|
||||
/// This is a type-unsafe low-level editing API, if you need to use it,
|
||||
/// prefer to create a type-safe abstraction on top of it instead.
|
||||
pub fn insert_children(
|
||||
parent: &SyntaxNode,
|
||||
position: InsertPosition<SyntaxElement>,
|
||||
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||
) -> SyntaxNode {
|
||||
let mut delta = TextUnit::default();
|
||||
let to_insert = to_insert.map(|element| {
|
||||
delta += element.text_range().len();
|
||||
to_green_element(element)
|
||||
});
|
||||
|
||||
let old_children = parent.green().children();
|
||||
|
||||
let new_children = match &position {
|
||||
InsertPosition::First => {
|
||||
to_insert.chain(old_children.iter().cloned()).collect::<Box<[_]>>()
|
||||
}
|
||||
InsertPosition::Last => old_children.iter().cloned().chain(to_insert).collect::<Box<[_]>>(),
|
||||
InsertPosition::Before(anchor) | InsertPosition::After(anchor) => {
|
||||
let take_anchor = if let InsertPosition::After(_) = position { 1 } else { 0 };
|
||||
let split_at = position_of_child(parent, anchor.clone()) + take_anchor;
|
||||
let (before, after) = old_children.split_at(split_at);
|
||||
before
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(to_insert)
|
||||
.chain(after.iter().cloned())
|
||||
.collect::<Box<[_]>>()
|
||||
}
|
||||
};
|
||||
|
||||
with_children(parent, new_children)
|
||||
}
|
||||
|
||||
/// Replaces all nodes in `to_delete` with nodes from `to_insert`
|
||||
///
|
||||
/// This is a type-unsafe low-level editing API, if you need to use it,
|
||||
/// prefer to create a type-safe abstraction on top of it instead.
|
||||
pub fn replace_children(
|
||||
parent: &SyntaxNode,
|
||||
to_delete: RangeInclusive<SyntaxElement>,
|
||||
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||
) -> SyntaxNode {
|
||||
let start = position_of_child(parent, to_delete.start().clone());
|
||||
let end = position_of_child(parent, to_delete.end().clone());
|
||||
let old_children = parent.green().children();
|
||||
|
||||
let new_children = old_children[..start]
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(to_insert.map(to_green_element))
|
||||
.chain(old_children[end + 1..].iter().cloned())
|
||||
.collect::<Box<[_]>>();
|
||||
with_children(parent, new_children)
|
||||
}
|
||||
|
||||
fn with_children(
|
||||
parent: &SyntaxNode,
|
||||
new_children: Box<[NodeOrToken<rowan::GreenNode, rowan::GreenToken>]>,
|
||||
) -> SyntaxNode {
|
||||
let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>();
|
||||
let new_node =
|
||||
rowan::GreenNode::new(rowan::cursor::SyntaxKind(parent.kind() as u16), new_children);
|
||||
let new_file_node = parent.replace_with(new_node);
|
||||
let file = SourceFile::new(new_file_node);
|
||||
|
||||
// FIXME: use a more elegant way to re-fetch the node (#1185), make
|
||||
// `range` private afterwards
|
||||
let mut ptr = SyntaxNodePtr::new(parent);
|
||||
ptr.range = TextRange::offset_len(ptr.range().start(), len);
|
||||
ptr.to_node(file.syntax()).to_owned()
|
||||
}
|
||||
|
||||
fn position_of_child(parent: &SyntaxNode, child: SyntaxElement) -> usize {
|
||||
parent
|
||||
.children_with_tokens()
|
||||
.position(|it| it == child)
|
||||
.expect("element is not a child of current element")
|
||||
}
|
||||
|
||||
fn to_green_element(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
|
||||
match element {
|
||||
NodeOrToken::Node(it) => it.green().clone().into(),
|
||||
NodeOrToken::Token(it) => it.green().clone().into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
ast::{self, child_opt, children, AstChildren, AstNode},
|
||||
SmolStr, SyntaxElement,
|
||||
SmolStr,
|
||||
SyntaxKind::*,
|
||||
SyntaxToken, T,
|
||||
};
|
||||
@@ -229,14 +229,11 @@ pub enum LiteralKind {
|
||||
|
||||
impl ast::Literal {
|
||||
pub fn token(&self) -> SyntaxToken {
|
||||
let elem = self
|
||||
.syntax()
|
||||
self.syntax()
|
||||
.children_with_tokens()
|
||||
.find(|e| e.kind() != ATTR && !e.kind().is_trivia());
|
||||
match elem {
|
||||
Some(SyntaxElement::Token(token)) => token,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.find(|e| e.kind() != ATTR && !e.kind().is_trivia())
|
||||
.and_then(|e| e.into_token())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> LiteralKind {
|
||||
|
||||
@@ -24,10 +24,7 @@ impl ast::NameRef {
|
||||
}
|
||||
|
||||
fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
|
||||
match node.0.green().children().first() {
|
||||
Some(rowan::GreenElement::Token(it)) => it.text(),
|
||||
_ => panic!(),
|
||||
}
|
||||
node.green().children().first().and_then(|it| it.as_token()).unwrap().text()
|
||||
}
|
||||
|
||||
impl ast::Attr {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>
|
||||
|
||||
mod syntax_node;
|
||||
mod syntax_text;
|
||||
mod syntax_error;
|
||||
mod parsing;
|
||||
mod validation;
|
||||
@@ -43,14 +42,13 @@ pub use crate::{
|
||||
ptr::{AstPtr, SyntaxNodePtr},
|
||||
syntax_error::{Location, SyntaxError, SyntaxErrorKind},
|
||||
syntax_node::{
|
||||
Direction, InsertPosition, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder,
|
||||
WalkEvent,
|
||||
Direction, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
|
||||
SyntaxTreeBuilder, WalkEvent,
|
||||
},
|
||||
syntax_text::SyntaxText,
|
||||
};
|
||||
pub use ra_parser::SyntaxKind;
|
||||
pub use ra_parser::T;
|
||||
pub use rowan::{SmolStr, TextRange, TextUnit};
|
||||
pub use rowan::{SmolStr, SyntaxText, TextRange, TextUnit};
|
||||
|
||||
/// `Parse` is the result of the parsing: a syntax tree and a collection of
|
||||
/// errors.
|
||||
@@ -76,7 +74,7 @@ impl<T> Parse<T> {
|
||||
}
|
||||
|
||||
pub fn syntax_node(&self) -> SyntaxNode {
|
||||
SyntaxNode::new(self.green.clone())
|
||||
SyntaxNode::new_root(self.green.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +145,7 @@ pub use crate::ast::SourceFile;
|
||||
|
||||
impl SourceFile {
|
||||
fn new(green: GreenNode) -> SourceFile {
|
||||
let root = SyntaxNode::new(green);
|
||||
let root = SyntaxNode::new_root(green);
|
||||
if cfg!(debug_assertions) {
|
||||
validation::validate_block_structure(&root);
|
||||
}
|
||||
@@ -267,8 +265,8 @@ fn api_walkthrough() {
|
||||
match event {
|
||||
WalkEvent::Enter(node) => {
|
||||
let text = match &node {
|
||||
SyntaxElement::Node(it) => it.text().to_string(),
|
||||
SyntaxElement::Token(it) => it.text().to_string(),
|
||||
NodeOrToken::Node(it) => it.text().to_string(),
|
||||
NodeOrToken::Token(it) => it.text().to_string(),
|
||||
};
|
||||
buf += &format!("{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent);
|
||||
indent += 2;
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::{
|
||||
text_token_source::TextTokenSource,
|
||||
text_tree_sink::TextTreeSink,
|
||||
},
|
||||
syntax_node::{GreenNode, GreenToken, SyntaxElement, SyntaxNode},
|
||||
syntax_node::{GreenNode, GreenToken, NodeOrToken, SyntaxElement, SyntaxNode},
|
||||
SyntaxError,
|
||||
SyntaxKind::*,
|
||||
TextRange, TextUnit, T,
|
||||
@@ -70,7 +70,8 @@ fn reparse_token<'node>(
|
||||
}
|
||||
}
|
||||
|
||||
let new_token = GreenToken::new(rowan::SyntaxKind(token.kind().into()), text.into());
|
||||
let new_token =
|
||||
GreenToken::new(rowan::cursor::SyntaxKind(token.kind().into()), text.into());
|
||||
Some((token.replace_with(new_token), token.text_range()))
|
||||
}
|
||||
_ => None,
|
||||
@@ -98,8 +99,8 @@ fn get_text_after_edit(element: SyntaxElement, edit: &AtomTextEdit) -> String {
|
||||
let edit =
|
||||
AtomTextEdit::replace(edit.delete - element.text_range().start(), edit.insert.clone());
|
||||
let text = match element {
|
||||
SyntaxElement::Token(token) => token.text().to_string(),
|
||||
SyntaxElement::Node(node) => node.text().to_string(),
|
||||
NodeOrToken::Token(token) => token.text().to_string(),
|
||||
NodeOrToken::Node(node) => node.text().to_string(),
|
||||
};
|
||||
edit.apply(text)
|
||||
}
|
||||
@@ -114,8 +115,8 @@ fn is_contextual_kw(text: &str) -> bool {
|
||||
fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(SyntaxNode, Reparser)> {
|
||||
let node = algo::find_covering_element(node, range);
|
||||
let mut ancestors = match node {
|
||||
SyntaxElement::Token(it) => it.parent().ancestors(),
|
||||
SyntaxElement::Node(it) => it.ancestors(),
|
||||
NodeOrToken::Token(it) => it.parent().ancestors(),
|
||||
NodeOrToken::Node(it) => it.ancestors(),
|
||||
};
|
||||
ancestors.find_map(|node| {
|
||||
let first_child = node.first_child_or_token().map(|it| it.kind());
|
||||
|
||||
@@ -6,15 +6,12 @@
|
||||
//! The *real* implementation is in the (language-agnostic) `rowan` crate, this
|
||||
//! modules just wraps its API.
|
||||
|
||||
use std::{fmt, iter::successors, ops::RangeInclusive};
|
||||
|
||||
use ra_parser::ParseError;
|
||||
use rowan::GreenNodeBuilder;
|
||||
use rowan::{GreenNodeBuilder, Language};
|
||||
|
||||
use crate::{
|
||||
syntax_error::{SyntaxError, SyntaxErrorKind},
|
||||
AstNode, Parse, SmolStr, SourceFile, SyntaxKind, SyntaxNodePtr, SyntaxText, TextRange,
|
||||
TextUnit,
|
||||
Parse, SmolStr, SyntaxKind, TextUnit,
|
||||
};
|
||||
|
||||
pub use rowan::WalkEvent;
|
||||
@@ -28,465 +25,27 @@ pub enum InsertPosition<T> {
|
||||
After(T),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
pub struct SyntaxNode(pub(crate) rowan::cursor::SyntaxNode);
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum RustLanguage {}
|
||||
impl Language for RustLanguage {
|
||||
type Kind = SyntaxKind;
|
||||
|
||||
impl fmt::Debug for SyntaxNode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if f.alternate() {
|
||||
let mut level = 0;
|
||||
for event in self.preorder_with_tokens() {
|
||||
match event {
|
||||
WalkEvent::Enter(element) => {
|
||||
for _ in 0..level {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
match element {
|
||||
SyntaxElement::Node(node) => writeln!(f, "{:?}", node)?,
|
||||
SyntaxElement::Token(token) => writeln!(f, "{:?}", token)?,
|
||||
}
|
||||
level += 1;
|
||||
}
|
||||
WalkEvent::Leave(_) => level -= 1,
|
||||
}
|
||||
}
|
||||
assert_eq!(level, 0);
|
||||
Ok(())
|
||||
} else {
|
||||
write!(f, "{:?}@{:?}", self.kind(), self.text_range())
|
||||
}
|
||||
fn kind_from_raw(raw: rowan::cursor::SyntaxKind) -> SyntaxKind {
|
||||
SyntaxKind::from(raw.0)
|
||||
}
|
||||
|
||||
fn kind_to_raw(kind: SyntaxKind) -> rowan::cursor::SyntaxKind {
|
||||
rowan::cursor::SyntaxKind(kind.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SyntaxNode {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.text(), fmt)
|
||||
}
|
||||
}
|
||||
pub type SyntaxNode = rowan::SyntaxNode<RustLanguage>;
|
||||
pub type SyntaxToken = rowan::SyntaxToken<RustLanguage>;
|
||||
pub type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
|
||||
pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<RustLanguage>;
|
||||
pub type SyntaxElementChildren = rowan::SyntaxElementChildren<RustLanguage>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
Next,
|
||||
Prev,
|
||||
}
|
||||
|
||||
impl SyntaxNode {
|
||||
pub(crate) fn new(green: GreenNode) -> SyntaxNode {
|
||||
let inner = rowan::cursor::SyntaxNode::new_root(green);
|
||||
SyntaxNode(inner)
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> SyntaxKind {
|
||||
self.0.kind().0.into()
|
||||
}
|
||||
|
||||
pub fn text_range(&self) -> TextRange {
|
||||
self.0.text_range()
|
||||
}
|
||||
|
||||
pub fn text(&self) -> SyntaxText {
|
||||
SyntaxText::new(self.clone())
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<SyntaxNode> {
|
||||
self.0.parent().map(SyntaxNode)
|
||||
}
|
||||
|
||||
pub fn first_child(&self) -> Option<SyntaxNode> {
|
||||
self.0.first_child().map(SyntaxNode)
|
||||
}
|
||||
|
||||
pub fn first_child_or_token(&self) -> Option<SyntaxElement> {
|
||||
self.0.first_child_or_token().map(SyntaxElement::new)
|
||||
}
|
||||
|
||||
pub fn last_child(&self) -> Option<SyntaxNode> {
|
||||
self.0.last_child().map(SyntaxNode)
|
||||
}
|
||||
|
||||
pub fn last_child_or_token(&self) -> Option<SyntaxElement> {
|
||||
self.0.last_child_or_token().map(SyntaxElement::new)
|
||||
}
|
||||
|
||||
pub fn next_sibling(&self) -> Option<SyntaxNode> {
|
||||
self.0.next_sibling().map(SyntaxNode)
|
||||
}
|
||||
|
||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||
self.0.next_sibling_or_token().map(SyntaxElement::new)
|
||||
}
|
||||
|
||||
pub fn prev_sibling(&self) -> Option<SyntaxNode> {
|
||||
self.0.prev_sibling().map(SyntaxNode)
|
||||
}
|
||||
|
||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||
self.0.prev_sibling_or_token().map(SyntaxElement::new)
|
||||
}
|
||||
|
||||
pub fn children(&self) -> SyntaxNodeChildren {
|
||||
SyntaxNodeChildren(self.0.children())
|
||||
}
|
||||
|
||||
pub fn children_with_tokens(&self) -> SyntaxElementChildren {
|
||||
SyntaxElementChildren(self.0.children_with_tokens())
|
||||
}
|
||||
|
||||
pub fn first_token(&self) -> Option<SyntaxToken> {
|
||||
self.0.first_token().map(SyntaxToken)
|
||||
}
|
||||
|
||||
pub fn last_token(&self) -> Option<SyntaxToken> {
|
||||
self.0.last_token().map(SyntaxToken)
|
||||
}
|
||||
|
||||
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode> {
|
||||
successors(Some(self.clone()), |node| node.parent())
|
||||
}
|
||||
|
||||
pub fn descendants(&self) -> impl Iterator<Item = SyntaxNode> {
|
||||
self.preorder().filter_map(|event| match event {
|
||||
WalkEvent::Enter(node) => Some(node),
|
||||
WalkEvent::Leave(_) => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn descendants_with_tokens(&self) -> impl Iterator<Item = SyntaxElement> {
|
||||
self.preorder_with_tokens().filter_map(|event| match event {
|
||||
WalkEvent::Enter(it) => Some(it),
|
||||
WalkEvent::Leave(_) => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = SyntaxNode> {
|
||||
successors(Some(self.clone()), move |node| match direction {
|
||||
Direction::Next => node.next_sibling(),
|
||||
Direction::Prev => node.prev_sibling(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn siblings_with_tokens(
|
||||
&self,
|
||||
direction: Direction,
|
||||
) -> impl Iterator<Item = SyntaxElement> {
|
||||
let me: SyntaxElement = self.clone().into();
|
||||
successors(Some(me), move |el| match direction {
|
||||
Direction::Next => el.next_sibling_or_token(),
|
||||
Direction::Prev => el.prev_sibling_or_token(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<SyntaxNode>> {
|
||||
self.0.preorder().map(|event| match event {
|
||||
WalkEvent::Enter(n) => WalkEvent::Enter(SyntaxNode(n)),
|
||||
WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxNode(n)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn preorder_with_tokens(&self) -> impl Iterator<Item = WalkEvent<SyntaxElement>> {
|
||||
self.0.preorder_with_tokens().map(|event| match event {
|
||||
WalkEvent::Enter(n) => WalkEvent::Enter(SyntaxElement::new(n)),
|
||||
WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxElement::new(n)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
|
||||
self.0.replace_with(replacement)
|
||||
}
|
||||
|
||||
/// Adds specified children (tokens or nodes) to the current node at the
|
||||
/// specific position.
|
||||
///
|
||||
/// This is a type-unsafe low-level editing API, if you need to use it,
|
||||
/// prefer to create a type-safe abstraction on top of it instead.
|
||||
pub fn insert_children(
|
||||
&self,
|
||||
position: InsertPosition<SyntaxElement>,
|
||||
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||
) -> SyntaxNode {
|
||||
let mut delta = TextUnit::default();
|
||||
let to_insert = to_insert.map(|element| {
|
||||
delta += element.text_len();
|
||||
to_green_element(element)
|
||||
});
|
||||
|
||||
let old_children = self.0.green().children();
|
||||
|
||||
let new_children = match &position {
|
||||
InsertPosition::First => {
|
||||
to_insert.chain(old_children.iter().cloned()).collect::<Box<[_]>>()
|
||||
}
|
||||
InsertPosition::Last => {
|
||||
old_children.iter().cloned().chain(to_insert).collect::<Box<[_]>>()
|
||||
}
|
||||
InsertPosition::Before(anchor) | InsertPosition::After(anchor) => {
|
||||
let take_anchor = if let InsertPosition::After(_) = position { 1 } else { 0 };
|
||||
let split_at = self.position_of_child(anchor.clone()) + take_anchor;
|
||||
let (before, after) = old_children.split_at(split_at);
|
||||
before
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(to_insert)
|
||||
.chain(after.iter().cloned())
|
||||
.collect::<Box<[_]>>()
|
||||
}
|
||||
};
|
||||
|
||||
self.with_children(new_children)
|
||||
}
|
||||
|
||||
/// Replaces all nodes in `to_delete` with nodes from `to_insert`
|
||||
///
|
||||
/// This is a type-unsafe low-level editing API, if you need to use it,
|
||||
/// prefer to create a type-safe abstraction on top of it instead.
|
||||
pub fn replace_children(
|
||||
&self,
|
||||
to_delete: RangeInclusive<SyntaxElement>,
|
||||
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||
) -> SyntaxNode {
|
||||
let start = self.position_of_child(to_delete.start().clone());
|
||||
let end = self.position_of_child(to_delete.end().clone());
|
||||
let old_children = self.0.green().children();
|
||||
|
||||
let new_children = old_children[..start]
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(to_insert.map(to_green_element))
|
||||
.chain(old_children[end + 1..].iter().cloned())
|
||||
.collect::<Box<[_]>>();
|
||||
self.with_children(new_children)
|
||||
}
|
||||
|
||||
fn with_children(&self, new_children: Box<[rowan::GreenElement]>) -> SyntaxNode {
|
||||
let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>();
|
||||
let new_node = GreenNode::new(rowan::SyntaxKind(self.kind() as u16), new_children);
|
||||
let new_file_node = self.replace_with(new_node);
|
||||
let file = SourceFile::new(new_file_node);
|
||||
|
||||
// FIXME: use a more elegant way to re-fetch the node (#1185), make
|
||||
// `range` private afterwards
|
||||
let mut ptr = SyntaxNodePtr::new(self);
|
||||
ptr.range = TextRange::offset_len(ptr.range().start(), len);
|
||||
ptr.to_node(file.syntax()).to_owned()
|
||||
}
|
||||
|
||||
fn position_of_child(&self, child: SyntaxElement) -> usize {
|
||||
self.children_with_tokens()
|
||||
.position(|it| it == child)
|
||||
.expect("element is not a child of current element")
|
||||
}
|
||||
}
|
||||
|
||||
fn to_green_element(element: SyntaxElement) -> rowan::GreenElement {
|
||||
match element {
|
||||
SyntaxElement::Node(node) => node.0.green().clone().into(),
|
||||
SyntaxElement::Token(tok) => {
|
||||
GreenToken::new(rowan::SyntaxKind(tok.kind() as u16), tok.text().clone()).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SyntaxToken(pub(crate) rowan::cursor::SyntaxToken);
|
||||
|
||||
impl fmt::Debug for SyntaxToken {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{:?}@{:?}", self.kind(), self.text_range())?;
|
||||
if self.text().len() < 25 {
|
||||
return write!(fmt, " {:?}", self.text());
|
||||
}
|
||||
let text = self.text().as_str();
|
||||
for idx in 21..25 {
|
||||
if text.is_char_boundary(idx) {
|
||||
let text = format!("{} ...", &text[..idx]);
|
||||
return write!(fmt, " {:?}", text);
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SyntaxToken {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(self.text(), fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl SyntaxToken {
|
||||
pub fn kind(&self) -> SyntaxKind {
|
||||
self.0.kind().0.into()
|
||||
}
|
||||
|
||||
pub fn text(&self) -> &SmolStr {
|
||||
self.0.text()
|
||||
}
|
||||
|
||||
pub fn text_range(&self) -> TextRange {
|
||||
self.0.text_range()
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> SyntaxNode {
|
||||
SyntaxNode(self.0.parent())
|
||||
}
|
||||
|
||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||
self.0.next_sibling_or_token().map(SyntaxElement::new)
|
||||
}
|
||||
|
||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||
self.0.prev_sibling_or_token().map(SyntaxElement::new)
|
||||
}
|
||||
|
||||
pub fn siblings_with_tokens(
|
||||
&self,
|
||||
direction: Direction,
|
||||
) -> impl Iterator<Item = SyntaxElement> {
|
||||
let me: SyntaxElement = self.clone().into();
|
||||
successors(Some(me), move |el| match direction {
|
||||
Direction::Next => el.next_sibling_or_token(),
|
||||
Direction::Prev => el.prev_sibling_or_token(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn next_token(&self) -> Option<SyntaxToken> {
|
||||
self.0.next_token().map(SyntaxToken)
|
||||
}
|
||||
|
||||
pub fn prev_token(&self) -> Option<SyntaxToken> {
|
||||
self.0.prev_token().map(SyntaxToken)
|
||||
}
|
||||
|
||||
pub(crate) fn replace_with(&self, new_token: GreenToken) -> GreenNode {
|
||||
self.0.replace_with(new_token)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub enum SyntaxElement {
|
||||
Node(SyntaxNode),
|
||||
Token(SyntaxToken),
|
||||
}
|
||||
|
||||
impl From<SyntaxNode> for SyntaxElement {
|
||||
fn from(node: SyntaxNode) -> Self {
|
||||
SyntaxElement::Node(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SyntaxToken> for SyntaxElement {
|
||||
fn from(token: SyntaxToken) -> Self {
|
||||
SyntaxElement::Token(token)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SyntaxElement {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
SyntaxElement::Node(it) => fmt::Display::fmt(it, fmt),
|
||||
SyntaxElement::Token(it) => fmt::Display::fmt(it, fmt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SyntaxElement {
|
||||
pub(crate) fn new(el: rowan::cursor::SyntaxElement) -> Self {
|
||||
match el {
|
||||
rowan::cursor::SyntaxElement::Node(it) => SyntaxElement::Node(SyntaxNode(it)),
|
||||
rowan::cursor::SyntaxElement::Token(it) => SyntaxElement::Token(SyntaxToken(it)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> SyntaxKind {
|
||||
match self {
|
||||
SyntaxElement::Node(it) => it.kind(),
|
||||
SyntaxElement::Token(it) => it.kind(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_node(&self) -> Option<&SyntaxNode> {
|
||||
match self {
|
||||
SyntaxElement::Node(node) => Some(node),
|
||||
SyntaxElement::Token(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_node(self) -> Option<SyntaxNode> {
|
||||
match self {
|
||||
SyntaxElement::Node(node) => Some(node),
|
||||
SyntaxElement::Token(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_token(&self) -> Option<&SyntaxToken> {
|
||||
match self {
|
||||
SyntaxElement::Node(_) => None,
|
||||
SyntaxElement::Token(token) => Some(token),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_token(self) -> Option<SyntaxToken> {
|
||||
match self {
|
||||
SyntaxElement::Node(_) => None,
|
||||
SyntaxElement::Token(token) => Some(token),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||
match self {
|
||||
SyntaxElement::Node(it) => it.next_sibling_or_token(),
|
||||
SyntaxElement::Token(it) => it.next_sibling_or_token(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||
match self {
|
||||
SyntaxElement::Node(it) => it.prev_sibling_or_token(),
|
||||
SyntaxElement::Token(it) => it.prev_sibling_or_token(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode> {
|
||||
match self {
|
||||
SyntaxElement::Node(it) => it.clone(),
|
||||
SyntaxElement::Token(it) => it.parent(),
|
||||
}
|
||||
.ancestors()
|
||||
}
|
||||
|
||||
pub fn text_range(&self) -> TextRange {
|
||||
match self {
|
||||
SyntaxElement::Node(it) => it.text_range(),
|
||||
SyntaxElement::Token(it) => it.text_range(),
|
||||
}
|
||||
}
|
||||
|
||||
fn text_len(&self) -> TextUnit {
|
||||
match self {
|
||||
SyntaxElement::Node(node) => node.0.green().text_len(),
|
||||
SyntaxElement::Token(token) => TextUnit::of_str(token.0.text()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SyntaxNodeChildren(rowan::cursor::SyntaxNodeChildren);
|
||||
|
||||
impl Iterator for SyntaxNodeChildren {
|
||||
type Item = SyntaxNode;
|
||||
fn next(&mut self) -> Option<SyntaxNode> {
|
||||
self.0.next().map(SyntaxNode)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SyntaxElementChildren(rowan::cursor::SyntaxElementChildren);
|
||||
|
||||
impl Iterator for SyntaxElementChildren {
|
||||
type Item = SyntaxElement;
|
||||
fn next(&mut self) -> Option<SyntaxElement> {
|
||||
self.0.next().map(SyntaxElement::new)
|
||||
}
|
||||
}
|
||||
pub use rowan::{Direction, NodeOrToken};
|
||||
|
||||
pub struct SyntaxTreeBuilder {
|
||||
errors: Vec<SyntaxError>,
|
||||
@@ -507,19 +66,21 @@ impl SyntaxTreeBuilder {
|
||||
|
||||
pub fn finish(self) -> Parse<SyntaxNode> {
|
||||
let (green, errors) = self.finish_raw();
|
||||
let node = SyntaxNode::new(green);
|
||||
let node = SyntaxNode::new_root(green);
|
||||
if cfg!(debug_assertions) {
|
||||
crate::validation::validate_block_structure(&node);
|
||||
}
|
||||
Parse::new(node.0.green().clone(), errors)
|
||||
Parse::new(node.green().clone(), errors)
|
||||
}
|
||||
|
||||
pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
|
||||
self.inner.token(rowan::SyntaxKind(kind.into()), text)
|
||||
let kind = RustLanguage::kind_to_raw(kind);
|
||||
self.inner.token(kind, text)
|
||||
}
|
||||
|
||||
pub fn start_node(&mut self, kind: SyntaxKind) {
|
||||
self.inner.start_node(rowan::SyntaxKind(kind.into()))
|
||||
let kind = RustLanguage::kind_to_raw(kind);
|
||||
self.inner.start_node(kind)
|
||||
}
|
||||
|
||||
pub fn finish_node(&mut self) {
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
use std::{
|
||||
fmt,
|
||||
ops::{self, Bound},
|
||||
};
|
||||
|
||||
use crate::{SmolStr, SyntaxElement, SyntaxNode, TextRange, TextUnit};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SyntaxText {
|
||||
node: SyntaxNode,
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
impl SyntaxText {
|
||||
pub(crate) fn new(node: SyntaxNode) -> SyntaxText {
|
||||
let range = node.text_range();
|
||||
SyntaxText { node, range }
|
||||
}
|
||||
|
||||
pub fn try_fold_chunks<T, F, E>(&self, init: T, mut f: F) -> Result<T, E>
|
||||
where
|
||||
F: FnMut(T, &str) -> Result<T, E>,
|
||||
{
|
||||
self.node.descendants_with_tokens().try_fold(init, move |acc, element| {
|
||||
let res = match element {
|
||||
SyntaxElement::Token(token) => {
|
||||
let range = match self.range.intersection(&token.text_range()) {
|
||||
None => return Ok(acc),
|
||||
Some(it) => it,
|
||||
};
|
||||
let slice = if range == token.text_range() {
|
||||
token.text()
|
||||
} else {
|
||||
let range = range - token.text_range().start();
|
||||
&token.text()[range]
|
||||
};
|
||||
f(acc, slice)?
|
||||
}
|
||||
SyntaxElement::Node(_) => acc,
|
||||
};
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_for_each_chunk<F: FnMut(&str) -> Result<(), E>, E>(
|
||||
&self,
|
||||
mut f: F,
|
||||
) -> Result<(), E> {
|
||||
self.try_fold_chunks((), move |(), chunk| f(chunk))
|
||||
}
|
||||
|
||||
pub fn for_each_chunk<F: FnMut(&str)>(&self, mut f: F) {
|
||||
enum Void {}
|
||||
match self.try_for_each_chunk(|chunk| Ok::<(), Void>(f(chunk))) {
|
||||
Ok(()) => (),
|
||||
Err(void) => match void {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_smol_string(&self) -> SmolStr {
|
||||
self.to_string().into()
|
||||
}
|
||||
|
||||
pub fn contains_char(&self, c: char) -> bool {
|
||||
self.try_for_each_chunk(|chunk| if chunk.contains(c) { Err(()) } else { Ok(()) }).is_err()
|
||||
}
|
||||
|
||||
pub fn find_char(&self, c: char) -> Option<TextUnit> {
|
||||
let mut acc: TextUnit = 0.into();
|
||||
let res = self.try_for_each_chunk(|chunk| {
|
||||
if let Some(pos) = chunk.find(c) {
|
||||
let pos: TextUnit = (pos as u32).into();
|
||||
return Err(acc + pos);
|
||||
}
|
||||
acc += TextUnit::of_str(chunk);
|
||||
Ok(())
|
||||
});
|
||||
found(res)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> TextUnit {
|
||||
self.range.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.range.is_empty()
|
||||
}
|
||||
|
||||
pub fn slice(&self, range: impl ops::RangeBounds<TextUnit>) -> SyntaxText {
|
||||
let start = match range.start_bound() {
|
||||
Bound::Included(&b) => b,
|
||||
Bound::Excluded(_) => panic!("utf-aware slicing can't work this way"),
|
||||
Bound::Unbounded => 0.into(),
|
||||
};
|
||||
let end = match range.end_bound() {
|
||||
Bound::Included(_) => panic!("utf-aware slicing can't work this way"),
|
||||
Bound::Excluded(&b) => b,
|
||||
Bound::Unbounded => self.len(),
|
||||
};
|
||||
assert!(start <= end);
|
||||
let len = end - start;
|
||||
let start = self.range.start() + start;
|
||||
let end = start + len;
|
||||
assert!(
|
||||
start <= end,
|
||||
"invalid slice, range: {:?}, slice: {:?}",
|
||||
self.range,
|
||||
(range.start_bound(), range.end_bound()),
|
||||
);
|
||||
let range = TextRange::from_to(start, end);
|
||||
assert!(
|
||||
range.is_subrange(&self.range),
|
||||
"invalid slice, range: {:?}, slice: {:?}",
|
||||
self.range,
|
||||
range,
|
||||
);
|
||||
SyntaxText { node: self.node.clone(), range }
|
||||
}
|
||||
|
||||
pub fn char_at(&self, offset: impl Into<TextUnit>) -> Option<char> {
|
||||
let offset = offset.into();
|
||||
let mut start: TextUnit = 0.into();
|
||||
let res = self.try_for_each_chunk(|chunk| {
|
||||
let end = start + TextUnit::of_str(chunk);
|
||||
if start <= offset && offset < end {
|
||||
let off: usize = u32::from(offset - start) as usize;
|
||||
return Err(chunk[off..].chars().next().unwrap());
|
||||
}
|
||||
start = end;
|
||||
Ok(())
|
||||
});
|
||||
found(res)
|
||||
}
|
||||
}
|
||||
|
||||
fn found<T>(res: Result<(), T>) -> Option<T> {
|
||||
match res {
|
||||
Ok(()) => None,
|
||||
Err(it) => Some(it),
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SyntaxText {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.to_string(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SyntaxText {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.try_for_each_chunk(|chunk| fmt::Display::fmt(chunk, f))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SyntaxText> for String {
|
||||
fn from(text: SyntaxText) -> String {
|
||||
text.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for SyntaxText {
|
||||
fn eq(&self, mut rhs: &str) -> bool {
|
||||
self.try_for_each_chunk(|chunk| {
|
||||
if !rhs.starts_with(chunk) {
|
||||
return Err(());
|
||||
}
|
||||
rhs = &rhs[chunk.len()..];
|
||||
Ok(())
|
||||
})
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&'_ str> for SyntaxText {
|
||||
fn eq(&self, rhs: &&str) -> bool {
|
||||
self == *rhs
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user