aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_editor/src/line_index_utils.rs139
1 files changed, 139 insertions, 0 deletions
diff --git a/crates/ra_editor/src/line_index_utils.rs b/crates/ra_editor/src/line_index_utils.rs
index e4fad04b2..6bd6a397e 100644
--- a/crates/ra_editor/src/line_index_utils.rs
+++ b/crates/ra_editor/src/line_index_utils.rs
@@ -1,6 +1,7 @@
1use ra_text_edit::{AtomTextEdit}; 1use ra_text_edit::{AtomTextEdit};
2use ra_syntax::{TextUnit, TextRange}; 2use ra_syntax::{TextUnit, TextRange};
3use crate::{LineIndex, LineCol}; 3use crate::{LineIndex, LineCol};
4use superslice::Ext;
4 5
5#[derive(Debug)] 6#[derive(Debug)]
6struct OffsetNewlineIter<'a> { 7struct OffsetNewlineIter<'a> {
@@ -58,6 +59,136 @@ trait TranslatedNewlineIterator {
58 } 59 }
59} 60}
60 61
62#[derive(Debug)]
63struct AltEdit<'a> {
64 insert_newlines: OffsetNewlineIter<'a>,
65 delete: TextRange,
66 diff: i64,
67}
68
69fn translate_range_by(range: TextRange, diff: i64) -> TextRange {
70 if diff == 0 {
71 range
72 } else {
73 let start = translate_by(range.start(), diff);
74 let end = translate_by(range.end(), diff);
75 TextRange::from_to(start, end)
76 }
77}
78
79fn translate_by(x: TextUnit, diff: i64) -> TextUnit {
80 if diff == 0 {
81 x
82 } else {
83 TextUnit::from((x.to_usize() as i64 + diff) as u32)
84 }
85}
86
87fn to_alt_edits<'a>(offset: TextUnit, edits: &'a [AtomTextEdit]) -> Vec<AltEdit<'a>> {
88 let mut xs: Vec<AltEdit<'a>> = Vec::with_capacity(edits.len());
89 // collect and sort edits
90 for edit in edits {
91 // TODO discard after translating?
92 // if edit.delete.start() >= offset {
93 // continue;
94 // }
95 let insert_index = xs.upper_bound_by_key(&edit.delete.start(), |x| x.delete.start());
96 let diff = edit.insert.len() as i64 - edit.delete.len().to_usize() as i64;
97 xs.insert(
98 insert_index,
99 AltEdit {
100 insert_newlines: OffsetNewlineIter {
101 offset: edit.delete.start(),
102 text: &edit.insert,
103 },
104 delete: edit.delete,
105 diff: diff,
106 },
107 );
108 }
109 // translate edits by previous edits
110 for i in 1..xs.len() {
111 let (x, prevs) = xs[0..=i].split_last_mut().unwrap();
112 for prev in prevs {
113 x.delete = translate_range_by(x.delete, prev.diff);
114 x.insert_newlines.offset = translate_by(x.insert_newlines.offset, prev.diff);
115 }
116 }
117 xs
118}
119
120#[derive(Debug)]
121enum NextNewline {
122 Use,
123 Discard,
124 Replace(TextUnit),
125 New(TextUnit),
126}
127
128fn next_newline(candidate: Option<TextUnit>, edits: &mut [AltEdit]) -> NextNewline {
129 let mut candidate = match candidate {
130 None => {
131 for edit in edits {
132 if let Some(inserted) = edit.insert_newlines.next() {
133 return NextNewline::New(inserted);
134 }
135 }
136 return NextNewline::Use; // END
137 }
138 Some(x) => x,
139 };
140
141 for edit in edits {
142 if candidate <= edit.delete.start() {
143 return NextNewline::Replace(candidate);
144 } else if candidate <= edit.delete.end() {
145 return match edit.insert_newlines.next() {
146 Some(x) => NextNewline::Replace(x),
147 None => NextNewline::Discard,
148 };
149 } else {
150 if let Some(inserted) = edit.insert_newlines.next() {
151 return NextNewline::New(inserted);
152 }
153 candidate = translate_by(candidate, edit.diff);
154 }
155 }
156 return NextNewline::Replace(candidate);
157}
158
159fn count_newlines(offset: TextUnit, line_index: &LineIndex, edits: &[AtomTextEdit]) -> u32 {
160 let mut edits = to_alt_edits(offset, edits);
161 let mut orig_newlines = line_index.newlines().iter().map(|x| *x).peekable();
162
163 let mut count = 0;
164
165 loop {
166 let res = next_newline(orig_newlines.peek().map(|x| *x), &mut edits);
167 let next = match res {
168 NextNewline::Use => orig_newlines.next(),
169 NextNewline::Discard => {
170 orig_newlines.next();
171 continue;
172 }
173 NextNewline::Replace(new) => {
174 orig_newlines.next();
175 Some(new)
176 }
177 NextNewline::New(new) => Some(new),
178 };
179 match next {
180 Some(n) if n <= offset => {
181 count += 1;
182 }
183 _ => {
184 break;
185 }
186 }
187 }
188
189 count
190}
191
61struct TranslatedAtomEdit<'a> { 192struct TranslatedAtomEdit<'a> {
62 delete: TextRange, 193 delete: TextRange,
63 insert: &'a str, 194 insert: &'a str,
@@ -256,5 +387,13 @@ mod test {
256 let actual = translate_offset_with_edit(&line_index, x.offset, &x.edits); 387 let actual = translate_offset_with_edit(&line_index, x.offset, &x.edits);
257 assert_eq!(actual.line, expected.line); 388 assert_eq!(actual.line, expected.line);
258 } 389 }
390
391 #[test]
392 fn test_translate_offset_with_edit_alt(x in arb_text_with_offset_and_edits()) {
393 let line_index = LineIndex::new(&x.text);
394 let expected = translate_after_edit(&x.text, x.offset, x.edits.clone());
395 let actual_lines = count_newlines(x.offset, &line_index, &x.edits);
396 assert_eq!(actual_lines, expected.line);
397 }
259 } 398 }
260} 399}