diff options
author | Aleksey Kladov <[email protected]> | 2019-09-30 07:27:26 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-09-30 07:27:26 +0100 |
commit | e010b144d5abcbd0947d0490123ef693a6a17c78 (patch) | |
tree | fed2cbe36e741a30a94486e47534b35690111a2b /crates/ra_syntax/src | |
parent | 0840ec038b2822a424acf238d8db5af569f99a21 (diff) |
move field list to ast/edit.rs
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 17 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 82 |
2 files changed, 96 insertions, 3 deletions
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 46680a08f..f33d2ad4e 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -3,6 +3,7 @@ pub mod visit; | |||
3 | use std::ops::RangeInclusive; | 3 | use std::ops::RangeInclusive; |
4 | 4 | ||
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use ra_text_edit::TextEditBuilder; | ||
6 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
7 | 8 | ||
8 | use crate::{ | 9 | use crate::{ |
@@ -63,6 +64,18 @@ pub enum InsertPosition<T> { | |||
63 | After(T), | 64 | After(T), |
64 | } | 65 | } |
65 | 66 | ||
67 | pub struct TreeDiff { | ||
68 | replacements: FxHashMap<SyntaxElement, SyntaxElement>, | ||
69 | } | ||
70 | |||
71 | impl TreeDiff { | ||
72 | pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { | ||
73 | for (from, to) in self.replacements.iter() { | ||
74 | builder.replace(from.text_range(), to.to_string()) | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
66 | /// Finds minimal the diff, which, applied to `from`, will result in `to`. | 79 | /// Finds minimal the diff, which, applied to `from`, will result in `to`. |
67 | /// | 80 | /// |
68 | /// Specifically, returns a map whose keys are descendants of `from` and values | 81 | /// Specifically, returns a map whose keys are descendants of `from` and values |
@@ -70,12 +83,12 @@ pub enum InsertPosition<T> { | |||
70 | /// | 83 | /// |
71 | /// A trivial solution is a singletom map `{ from: to }`, but this function | 84 | /// A trivial solution is a singletom map `{ from: to }`, but this function |
72 | /// tries to find a more fine-grained diff. | 85 | /// tries to find a more fine-grained diff. |
73 | pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> FxHashMap<SyntaxElement, SyntaxElement> { | 86 | pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { |
74 | let mut buf = FxHashMap::default(); | 87 | let mut buf = FxHashMap::default(); |
75 | // FIXME: this is both horrible inefficient and gives larger than | 88 | // FIXME: this is both horrible inefficient and gives larger than |
76 | // necessary diff. I bet there's a cool algorithm to diff trees properly. | 89 | // necessary diff. I bet there's a cool algorithm to diff trees properly. |
77 | go(&mut buf, from.clone().into(), to.clone().into()); | 90 | go(&mut buf, from.clone().into(), to.clone().into()); |
78 | return buf; | 91 | return TreeDiff { replacements: buf }; |
79 | 92 | ||
80 | fn go( | 93 | fn go( |
81 | buf: &mut FxHashMap<SyntaxElement, SyntaxElement>, | 94 | buf: &mut FxHashMap<SyntaxElement, SyntaxElement>, |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 2af6f573e..6e64c0675 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -12,7 +12,7 @@ use crate::{ | |||
12 | make::{self, tokens}, | 12 | make::{self, tokens}, |
13 | AstNode, | 13 | AstNode, |
14 | }, | 14 | }, |
15 | AstToken, InsertPosition, SmolStr, SyntaxElement, | 15 | AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, |
16 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, | 16 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, |
17 | SyntaxNode, T, | 17 | SyntaxNode, T, |
18 | }; | 18 | }; |
@@ -105,6 +105,86 @@ impl ast::ItemList { | |||
105 | } | 105 | } |
106 | } | 106 | } |
107 | 107 | ||
108 | impl ast::RecordFieldList { | ||
109 | #[must_use] | ||
110 | pub fn append_field(&self, field: &ast::RecordField) -> ast::RecordFieldList { | ||
111 | self.insert_field(InsertPosition::Last, field) | ||
112 | } | ||
113 | |||
114 | #[must_use] | ||
115 | pub fn insert_field( | ||
116 | &self, | ||
117 | position: InsertPosition<&'_ ast::RecordField>, | ||
118 | field: &ast::RecordField, | ||
119 | ) -> ast::RecordFieldList { | ||
120 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
121 | let ws; | ||
122 | let space = if is_multiline { | ||
123 | ws = tokens::WsBuilder::new(&format!( | ||
124 | "\n{} ", | ||
125 | leading_indent(self.syntax()).unwrap_or("".into()) | ||
126 | )); | ||
127 | ws.ws() | ||
128 | } else { | ||
129 | tokens::single_space() | ||
130 | }; | ||
131 | |||
132 | let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); | ||
133 | to_insert.push(space.into()); | ||
134 | to_insert.push(field.syntax().clone().into()); | ||
135 | to_insert.push(tokens::comma().into()); | ||
136 | |||
137 | macro_rules! after_l_curly { | ||
138 | () => {{ | ||
139 | let anchor = match self.l_curly() { | ||
140 | Some(it) => it, | ||
141 | None => return self.clone(), | ||
142 | }; | ||
143 | InsertPosition::After(anchor) | ||
144 | }}; | ||
145 | } | ||
146 | |||
147 | macro_rules! after_field { | ||
148 | ($anchor:expr) => { | ||
149 | if let Some(comma) = $anchor | ||
150 | .syntax() | ||
151 | .siblings_with_tokens(Direction::Next) | ||
152 | .find(|it| it.kind() == T![,]) | ||
153 | { | ||
154 | InsertPosition::After(comma) | ||
155 | } else { | ||
156 | to_insert.insert(0, tokens::comma().into()); | ||
157 | InsertPosition::After($anchor.syntax().clone().into()) | ||
158 | } | ||
159 | }; | ||
160 | }; | ||
161 | |||
162 | let position = match position { | ||
163 | InsertPosition::First => after_l_curly!(), | ||
164 | InsertPosition::Last => { | ||
165 | if !is_multiline { | ||
166 | // don't insert comma before curly | ||
167 | to_insert.pop(); | ||
168 | } | ||
169 | match self.fields().last() { | ||
170 | Some(it) => after_field!(it), | ||
171 | None => after_l_curly!(), | ||
172 | } | ||
173 | } | ||
174 | InsertPosition::Before(anchor) => { | ||
175 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
176 | } | ||
177 | InsertPosition::After(anchor) => after_field!(anchor), | ||
178 | }; | ||
179 | |||
180 | insert_children(self, position, to_insert.iter().cloned()) | ||
181 | } | ||
182 | |||
183 | fn l_curly(&self) -> Option<SyntaxElement> { | ||
184 | self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) | ||
185 | } | ||
186 | } | ||
187 | |||
108 | pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: N) -> N { | 188 | pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: N) -> N { |
109 | N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 189 | N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap() |
110 | } | 190 | } |