Skip to content

Commit ed098fd

Browse files
committed
EditTree holds nodes with meta info
1 parent 3b9e0d1 commit ed098fd

File tree

9 files changed

+394
-255
lines changed

9 files changed

+394
-255
lines changed

crates/ra_fmt2/src/dsl.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use crate::{pattern::{Pattern, PatternSet}};
2-
use ra_syntax::{SyntaxElement, SyntaxKind::*,};
1+
use crate::pattern::{Pattern, PatternSet};
2+
use ra_syntax::{SyntaxElement, SyntaxKind::*};
33
use std::iter::successors;
44

55
/// `SpacingRule` describes whitespace requirements between `SyntaxElement` Note
@@ -93,7 +93,7 @@ impl SpacingDsl {
9393
self
9494
}
9595
}
96-
/// A builder to conveniently specify a single rule.
96+
/// A builder to conveniently specify a single rule.
9797
pub(crate) struct SpacingRuleBuilder<'a> {
9898
dsl: &'a mut SpacingDsl,
9999
parent: Pattern,

crates/ra_fmt2/src/engine.rs

+64-101
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,77 @@
1-
use crate::dsl;
1+
use crate::dsl::{self, SpaceLoc, SpaceValue, SpacingRule, SpacingDsl};
2+
use crate::fmt_model::{FmtModel, SpaceBlock, BlockPosition};
3+
use crate::pattern::PatternSet;
4+
use crate::trav_util::{has_newline};
5+
26
use ra_syntax::{
37
ast::{self, AstNode, AstToken},
4-
Parse, SmolStr, SourceFile,
5-
SyntaxElement, TextUnit,
6-
SyntaxKind, SyntaxKind::*,
7-
SyntaxNode, SyntaxToken, T,
8-
WalkEvent, TextRange,
8+
Parse, SmolStr, SourceFile, SyntaxElement, SyntaxKind,
9+
SyntaxKind::*,
10+
SyntaxNode, SyntaxToken, TextRange, TextUnit, WalkEvent, T,
911
};
1012
use std::collections::HashMap;
1113

12-
#[derive(Debug)]
13-
14-
struct FmtTree {
15-
/// owned original root node
16-
original_node: SyntaxNode,
17-
/// We store `SpaceBlock`s in array. With this setup, we can refer to a
18-
/// specific block by index, dodging many lifetime issues.
19-
blocks: Vec<SpaceBlock>,
20-
/// Maps offset to an index of the block, for which the offset is the start
21-
/// offset.
22-
by_start_offset: HashMap<TextUnit, usize>,
23-
/// Maps offset to an index of the block, for which the offset is the end
24-
/// offset.
25-
by_end_offset: HashMap<TextUnit, usize>,
26-
// Arbitrary non-whitespace edits created by the last formatter phase.
27-
//fixes: Vec<AtomEdit>,
28-
}
29-
30-
#[derive(Debug)]
31-
pub(super) struct SpaceBlock {
32-
original: OriginalSpace,
33-
/// Block's textual content, which is seen and modified by formatting rules.
34-
new_text: Option<SmolStr>,
35-
/// If this block requires a newline to preserve semantics.
36-
///
37-
/// True for blocks after comments. The engine takes care to never remove
38-
/// newline, even if some interaction of rules asks us to do so.
39-
semantic_newline: bool,
40-
}
41-
42-
43-
44-
#[derive(Debug, Clone, Copy)]
45-
pub(super) enum BlockPosition {
46-
Before,
47-
After,
48-
}
49-
50-
/// Original whitespace token, if any, that backs a `SpaceBlock.
51-
#[derive(Debug)]
52-
pub(super) enum OriginalSpace {
53-
Some(SyntaxToken),
54-
None { offset: TextUnit },
55-
}
56-
57-
impl OriginalSpace {
58-
fn text_range(&self) -> TextRange {
14+
impl SpaceLoc {
15+
fn is_before(self) -> bool {
5916
match self {
60-
OriginalSpace::Some(token) => token.text_range(),
61-
OriginalSpace::None { offset } => TextRange::from_to(*offset, *offset),
17+
SpaceLoc::Before | SpaceLoc::Around => true,
18+
SpaceLoc::After => false,
19+
}
20+
}
21+
fn is_after(self) -> bool {
22+
match self {
23+
SpaceLoc::After | SpaceLoc::Around => true,
24+
SpaceLoc::Before => false,
6225
}
6326
}
6427
}
6528

66-
impl SpaceBlock {
67-
fn new(original: OriginalSpace) -> SpaceBlock {
68-
let semantic_newline = match &original {
69-
OriginalSpace::Some(token) => {
70-
token.text().contains('\n')
29+
fn ensure_space(element: &SyntaxElement, block: &mut SpaceBlock, value: SpaceValue) {
30+
match value {
31+
SpaceValue::Single => block.set_text(" "),
32+
SpaceValue::SingleOptionalNewline => {
33+
if !block.has_newline() {
34+
block.set_text(" ")
7135
}
72-
OriginalSpace::None { .. } => false,
73-
};
74-
SpaceBlock { original, new_text: None, semantic_newline }
75-
}
76-
pub(super) fn set_line_break_preserving_existing_newlines(&mut self) {
77-
if self.has_newline() {
78-
return;
79-
}
80-
self.set_text("\n");
81-
}
82-
pub(super) fn set_text(&mut self, text: &str) {
83-
if self.semantic_newline && !text.contains('\n') {
84-
return;
8536
}
86-
match &self.original {
87-
OriginalSpace::Some(token) if token.text() == text && self.new_text.is_none() => return,
88-
_ => self.new_text = Some(text.into()),
37+
SpaceValue::Newline => block.set_text("\n"),
38+
SpaceValue::None => block.set_text(""),
39+
SpaceValue::NoneOptionalNewline => {
40+
if !block.has_newline() {
41+
block.set_text("")
42+
}
8943
}
90-
}
91-
pub(super) fn text(&self) -> &str {
92-
if let Some(text) = self.new_text.as_ref() {
93-
return text.as_str();
44+
SpaceValue::SingleOrNewline => {
45+
let parent_is_multiline = element.parent().map_or(false, |it| has_newline(&it));
46+
if parent_is_multiline {
47+
block.set_line_break_preserving_existing_newlines()
48+
} else {
49+
block.set_text(" ")
50+
}
9451
}
95-
self.original_text()
96-
}
97-
pub(crate) fn original_text(&self) -> &str {
98-
match &self.original {
99-
OriginalSpace::Some(token) => token.text().as_str(),
100-
OriginalSpace::None { .. } => "",
52+
SpaceValue::NoneOrNewline => {
53+
let parent_is_multiline = element.parent().map_or(false, |it| has_newline(&it));
54+
if parent_is_multiline {
55+
block.set_line_break_preserving_existing_newlines()
56+
} else {
57+
block.set_text("")
58+
}
10159
}
10260
}
103-
pub(super) fn has_newline(&self) -> bool {
104-
self.text().contains('\n')
105-
}
10661
}
10762

108-
impl FmtTree {
109-
pub(super) fn new(original_node: SyntaxNode) -> Self {
110-
Self {
111-
original_node,
112-
blocks: vec![],
113-
by_start_offset: HashMap::default(),
114-
by_end_offset: HashMap::default(),
63+
impl SpacingRule {
64+
pub(super) fn apply(&self, ele: &SyntaxElement, model: &mut FmtModel) {
65+
if !self.pattern.matches(ele) {
66+
return;
67+
}
68+
if self.space.loc.is_before() {
69+
let block = model.block_for(ele, BlockPosition::Before);
70+
ensure_space(ele, block, self.space.value);
71+
}
72+
if self.space.loc.is_after() {
73+
let block = model.block_for(ele, BlockPosition::After);
74+
ensure_space(ele, block, self.space.value);
11575
}
11676
}
11777
}
@@ -126,11 +86,14 @@ fn walk(node: &SyntaxNode) -> impl Iterator<Item = SyntaxElement> {
12686
})
12787
}
12888

129-
fn format_pass(space_rules: &dsl::SpacingDsl, root: &SyntaxNode) {
130-
let mut fmt_root = FmtTree::new(root.clone());
89+
pub fn format_pass(space_rules: &SpacingDsl, root: &SyntaxNode) {
90+
let mut fmt_root = FmtModel::new(root.clone());
91+
92+
let rules_set = PatternSet::new(space_rules.rules.iter());
93+
13194
for node in walk(root) {
132-
for rule in space_rules.matching(node.clone()) {
133-
rule.apply(&node, &mut fmt_root)
134-
}
95+
for rule in rules_set.matching(node.clone()) {
96+
rule.apply(&node, &mut fmt_root)
97+
}
13598
}
13699
}

crates/ra_fmt2/src/fmt_diff.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use crate::trav_util::walk_tokens;
2+
3+
use ra_syntax::{SyntaxNode, TextRange, SmolStr};
4+
5+
/// The result of formatting.
6+
///
7+
/// From this Diff, you can get either the resulting `String`, or the
8+
/// reformatted syntax node.
9+
#[derive(Debug)]
10+
pub struct FmtDiff {
11+
original_node: SyntaxNode,
12+
edits: Vec<AtomEdit>,
13+
}
14+
15+
#[derive(Debug, Clone, PartialEq, Eq)]
16+
struct AtomEdit {
17+
delete: TextRange,
18+
insert: SmolStr,
19+
}
20+
21+
impl FmtDiff {
22+
23+
pub fn reformat_node(node: &SyntaxNode) -> Self {
24+
let spacing = rules::spacing();
25+
engine::format_pass(space_rules: &SpacingDsl, root: &SyntaxNode)
26+
}
27+
28+
fn replace(&mut self, range: TextRange, text: SmolStr) {
29+
self.edits.push(AtomEdit { delete: range, insert: text })
30+
}
31+
}

0 commit comments

Comments
 (0)