Skip to content

Commit b6fa615

Browse files
committed
add whitespace heuristics same as ted to SyntaxEditor via insert_with_whitespace and insert_all_with_whitespace
1 parent 12e079e commit b6fa615

1 file changed

Lines changed: 94 additions & 1 deletion

File tree

crates/syntax/src/syntax_editor.rs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ use std::{
1414
use rowan::TextRange;
1515
use rustc_hash::FxHashMap;
1616

17-
use crate::{AstNode, SyntaxElement, SyntaxNode, SyntaxToken};
17+
use crate::{
18+
AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T,
19+
ast::{self, edit::IndentLevel, make},
20+
};
1821

1922
mod edit_algo;
2023
mod edits;
@@ -101,6 +104,28 @@ impl SyntaxEditor {
101104
self.changes.push(Change::InsertAll(position, elements))
102105
}
103106

107+
pub fn insert_with_whitespace(&mut self, position: Position, element: impl Element) {
108+
self.insert_all_with_whitespace(position, vec![element.syntax_element()])
109+
}
110+
111+
pub fn insert_all_with_whitespace(
112+
&mut self,
113+
position: Position,
114+
mut elements: Vec<SyntaxElement>,
115+
) {
116+
if let Some(first) = elements.first()
117+
&& let Some(ws) = ws_before(&position, first)
118+
{
119+
elements.insert(0, ws.into());
120+
}
121+
if let Some(last) = elements.last()
122+
&& let Some(ws) = ws_after(&position, last)
123+
{
124+
elements.push(ws.into());
125+
}
126+
self.insert_all(position, elements)
127+
}
128+
104129
pub fn delete(&mut self, element: impl Element) {
105130
let element = element.syntax_element();
106131
debug_assert!(is_ancestor_or_self_of_element(&element, &self.root));
@@ -412,6 +437,74 @@ impl Element for SyntaxToken {
412437
}
413438
}
414439

440+
fn ws_before(position: &Position, new: &SyntaxElement) -> Option<SyntaxToken> {
441+
let prev = match &position.repr {
442+
PositionRepr::FirstChild(_) => return None,
443+
PositionRepr::After(it) => it,
444+
};
445+
446+
if prev.kind() == T!['{']
447+
&& new.kind() == SyntaxKind::USE
448+
&& let Some(item_list) = prev.parent().and_then(ast::ItemList::cast)
449+
{
450+
let mut indent = IndentLevel::from_element(&item_list.syntax().clone().into());
451+
indent.0 += 1;
452+
return Some(make::tokens::whitespace(&format!("\n{indent}")));
453+
}
454+
455+
if prev.kind() == T!['{']
456+
&& ast::Stmt::can_cast(new.kind())
457+
&& let Some(stmt_list) = prev.parent().and_then(ast::StmtList::cast)
458+
{
459+
let mut indent = IndentLevel::from_element(&stmt_list.syntax().clone().into());
460+
indent.0 += 1;
461+
return Some(make::tokens::whitespace(&format!("\n{indent}")));
462+
}
463+
464+
ws_between(prev, new)
465+
}
466+
467+
fn ws_after(position: &Position, new: &SyntaxElement) -> Option<SyntaxToken> {
468+
let next = match &position.repr {
469+
PositionRepr::FirstChild(parent) => parent.first_child_or_token()?,
470+
PositionRepr::After(sibling) => sibling.next_sibling_or_token()?,
471+
};
472+
ws_between(new, &next)
473+
}
474+
475+
fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option<SyntaxToken> {
476+
if left.kind() == SyntaxKind::WHITESPACE || right.kind() == SyntaxKind::WHITESPACE {
477+
return None;
478+
}
479+
if right.kind() == T![;] || right.kind() == T![,] {
480+
return None;
481+
}
482+
if left.kind() == T![<] || right.kind() == T![>] {
483+
return None;
484+
}
485+
if left.kind() == T![&] && right.kind() == SyntaxKind::LIFETIME {
486+
return None;
487+
}
488+
if right.kind() == SyntaxKind::GENERIC_ARG_LIST {
489+
return None;
490+
}
491+
if right.kind() == SyntaxKind::USE {
492+
let mut indent = IndentLevel::from_element(left);
493+
if left.kind() == SyntaxKind::USE {
494+
indent.0 = IndentLevel::from_element(right).0.max(indent.0);
495+
}
496+
return Some(make::tokens::whitespace(&format!("\n{indent}")));
497+
}
498+
if left.kind() == SyntaxKind::ATTR {
499+
let mut indent = IndentLevel::from_element(right);
500+
if right.kind() == SyntaxKind::ATTR {
501+
indent.0 = IndentLevel::from_element(left).0.max(indent.0);
502+
}
503+
return Some(make::tokens::whitespace(&format!("\n{indent}")));
504+
}
505+
Some(make::tokens::single_space())
506+
}
507+
415508
fn is_ancestor_or_self(node: &SyntaxNode, ancestor: &SyntaxNode) -> bool {
416509
node == ancestor || node.ancestors().any(|it| &it == ancestor)
417510
}

0 commit comments

Comments
 (0)