From f5a81ec4683613bd62624811733345d627f2127b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 30 Jan 2021 18:19:21 +0300 Subject: Upgrade rowan Notably, new rowan comes with support for mutable syntax trees. --- crates/syntax/src/ast/edit_in_place.rs | 105 +++++++++++++++++++++++++++++++++ crates/syntax/src/ast/make.rs | 14 ++++- crates/syntax/src/ast/node_ext.rs | 4 +- 3 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 crates/syntax/src/ast/edit_in_place.rs (limited to 'crates/syntax/src/ast') diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs new file mode 100644 index 000000000..06cde591d --- /dev/null +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -0,0 +1,105 @@ +//! Structural editing for ast. + +use std::iter::empty; + +use ast::{edit::AstNodeEdit, make, GenericParamsOwner, WhereClause}; +use parser::T; + +use crate::{ + ast, + ted::{self, Position}, + AstNode, Direction, SyntaxKind, +}; + +use super::NameOwner; + +pub trait GenericParamsOwnerEdit: ast::GenericParamsOwner + AstNodeEdit { + fn get_or_create_where_clause(&self) -> ast::WhereClause; +} + +impl GenericParamsOwnerEdit for ast::Fn { + fn get_or_create_where_clause(&self) -> WhereClause { + if self.where_clause().is_none() { + let position = if let Some(ty) = self.ret_type() { + Position::after(ty.syntax().clone()) + } else if let Some(param_list) = self.param_list() { + Position::after(param_list.syntax().clone()) + } else { + Position::last_child_of(self.syntax().clone()) + }; + create_where_clause(position) + } + self.where_clause().unwrap() + } +} + +impl GenericParamsOwnerEdit for ast::Impl { + fn get_or_create_where_clause(&self) -> WhereClause { + if self.where_clause().is_none() { + let position = if let Some(ty) = self.self_ty() { + Position::after(ty.syntax().clone()) + } else { + Position::last_child_of(self.syntax().clone()) + }; + create_where_clause(position) + } + self.where_clause().unwrap() + } +} +impl GenericParamsOwnerEdit for ast::Struct { + fn get_or_create_where_clause(&self) -> WhereClause { + if self.where_clause().is_none() { + let tfl = self.field_list().and_then(|fl| match fl { + ast::FieldList::RecordFieldList(_) => None, + ast::FieldList::TupleFieldList(it) => Some(it), + }); + let position = if let Some(tfl) = tfl { + Position::after(tfl.syntax().clone()) + } else if let Some(gpl) = self.generic_param_list() { + Position::after(gpl.syntax().clone()) + } else if let Some(name) = self.name() { + Position::after(name.syntax().clone()) + } else { + Position::last_child_of(self.syntax().clone()) + }; + create_where_clause(position) + } + self.where_clause().unwrap() + } +} + +fn create_where_clause(position: Position) { + let elements = vec![ + make::tokens::single_space().into(), + make::where_clause(empty()).clone_for_update().syntax().clone().into(), + ]; + ted::insert_all(position, elements); +} + +impl ast::WhereClause { + pub fn add_predicate(&self, predicate: ast::WherePred) { + if let Some(pred) = self.predicates().last() { + if !pred.syntax().siblings_with_tokens(Direction::Next).any(|it| it.kind() == T![,]) { + ted::append_child(self.syntax().clone(), make::token(T![,])); + } + } + if self.syntax().children_with_tokens().last().map(|it| it.kind()) + != Some(SyntaxKind::WHITESPACE) + { + ted::append_child(self.syntax().clone(), make::tokens::single_space()); + } + ted::append_child(self.syntax().clone(), predicate.syntax().clone()) + } +} + +impl ast::TypeBoundList { + pub fn remove(&self) { + if let Some(colon) = + self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) + { + ted::remove_all(colon..=self.syntax().clone().into()) + } else { + ted::remove(self.syntax().clone()) + } + } +} diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 05a6b0b25..810c8d4c8 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -174,6 +174,11 @@ pub fn block_expr( pub fn expr_unit() -> ast::Expr { expr_from_text("()") } +pub fn expr_literal(text: &str) -> ast::Literal { + assert_eq!(text.trim(), text); + ast_from_text(&format!("fn f() {{ let _ = {}; }}", text)) +} + pub fn expr_empty_block() -> ast::Expr { expr_from_text("{}") } @@ -390,6 +395,7 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken { tokens::SOURCE_FILE .tree() .syntax() + .clone_for_update() .descendants_with_tokens() .filter_map(|it| it.into_token()) .find(|it| it.kind() == kind) @@ -544,6 +550,7 @@ pub mod tokens { SOURCE_FILE .tree() .syntax() + .clone_for_update() .descendants_with_tokens() .filter_map(|it| it.into_token()) .find(|it| it.kind() == WHITESPACE && it.text() == " ") @@ -569,13 +576,16 @@ pub mod tokens { } pub fn single_newline() -> SyntaxToken { - SOURCE_FILE + let res = SOURCE_FILE .tree() .syntax() + .clone_for_update() .descendants_with_tokens() .filter_map(|it| it.into_token()) .find(|it| it.kind() == WHITESPACE && it.text() == "\n") - .unwrap() + .unwrap(); + res.detach(); + res } pub fn blank_line() -> SyntaxToken { diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 52ac97c84..0b0d39a75 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -34,7 +34,9 @@ impl ast::NameRef { } fn text_of_first_token(node: &SyntaxNode) -> &str { - node.green().children().next().and_then(|it| it.into_token()).unwrap().text() + let t = + node.green().children().next().and_then(|it| it.into_token()).unwrap().text().to_string(); + Box::leak(Box::new(t)) } pub enum Macro { -- cgit v1.2.3