aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assist_ctx.rs4
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs4
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs4
-rw-r--r--crates/ra_ide_db/src/line_index_utils.rs6
-rw-r--r--crates/ra_syntax/src/fuzz.rs6
-rw-r--r--crates/ra_syntax/src/lib.rs14
-rw-r--r--crates/ra_syntax/src/parsing/reparsing.rs16
-rw-r--r--crates/ra_text_edit/src/lib.rs131
-rw-r--r--crates/ra_text_edit/src/text_edit.rs102
-rw-r--r--crates/rust-analyzer/src/conv.rs29
10 files changed, 154 insertions, 162 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 89547ce03..83dd270c6 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -4,14 +4,13 @@ use ra_db::FileRange;
4use ra_fmt::{leading_indent, reindent}; 4use ra_fmt::{leading_indent, reindent};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{self, find_covering_element, find_node_at_offset}, 7 algo::{self, find_covering_element, find_node_at_offset, SyntaxRewriter},
8 AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, 8 AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
9 TokenAtOffset, 9 TokenAtOffset,
10}; 10};
11use ra_text_edit::TextEditBuilder; 11use ra_text_edit::TextEditBuilder;
12 12
13use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; 13use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist};
14use algo::SyntaxRewriter;
15 14
16#[derive(Clone, Debug)] 15#[derive(Clone, Debug)]
17pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); 16pub(crate) struct Assist(pub(crate) Vec<AssistInfo>);
@@ -42,7 +41,6 @@ impl AssistInfo {
42 } 41 }
43} 42}
44 43
45
46/// `AssistCtx` allows to apply an assist or check if it could be applied. 44/// `AssistCtx` allows to apply an assist or check if it could be applied.
47/// 45///
48/// Assists use a somewhat over-engineered approach, given the current needs. The 46/// Assists use a somewhat over-engineered approach, given the current needs. The
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index dd87bd119..b6b9627de 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::Indel;
13 13
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15 15
@@ -76,7 +76,7 @@ impl<'a> CompletionContext<'a> {
76 // actual completion. 76 // actual completion.
77 let file_with_fake_ident = { 77 let file_with_fake_ident = {
78 let parse = db.parse(position.file_id); 78 let parse = db.parse(position.file_id);
79 let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); 79 let edit = Indel::insert(position.offset, "intellijRulezz".to_string());
80 parse.reparse(&edit).tree() 80 parse.reparse(&edit).tree()
81 }; 81 };
82 let fake_ident_token = 82 let fake_ident_token =
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index 5936fb8f7..383b23ac4 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -62,8 +62,8 @@ impl fmt::Debug for CompletionItem {
62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 let mut s = f.debug_struct("CompletionItem"); 63 let mut s = f.debug_struct("CompletionItem");
64 s.field("label", &self.label()).field("source_range", &self.source_range()); 64 s.field("label", &self.label()).field("source_range", &self.source_range());
65 if self.text_edit().as_atoms().len() == 1 { 65 if self.text_edit().as_indels().len() == 1 {
66 let atom = &self.text_edit().as_atoms()[0]; 66 let atom = &self.text_edit().as_indels()[0];
67 s.field("delete", &atom.delete); 67 s.field("delete", &atom.delete);
68 s.field("insert", &atom.insert); 68 s.field("insert", &atom.insert);
69 } else { 69 } else {
diff --git a/crates/ra_ide_db/src/line_index_utils.rs b/crates/ra_ide_db/src/line_index_utils.rs
index 039a12c0d..7fa6fc448 100644
--- a/crates/ra_ide_db/src/line_index_utils.rs
+++ b/crates/ra_ide_db/src/line_index_utils.rs
@@ -10,7 +10,7 @@
10use std::convert::TryInto; 10use std::convert::TryInto;
11 11
12use ra_syntax::{TextRange, TextSize}; 12use ra_syntax::{TextRange, TextSize};
13use ra_text_edit::{AtomTextEdit, TextEdit}; 13use ra_text_edit::{Indel, TextEdit};
14 14
15use crate::line_index::{LineCol, LineIndex, Utf16Char}; 15use crate::line_index::{LineCol, LineIndex, Utf16Char};
16 16
@@ -182,14 +182,14 @@ struct TranslatedEdit<'a> {
182} 182}
183 183
184struct Edits<'a> { 184struct Edits<'a> {
185 edits: &'a [AtomTextEdit], 185 edits: &'a [Indel],
186 current: Option<TranslatedEdit<'a>>, 186 current: Option<TranslatedEdit<'a>>,
187 acc_diff: i64, 187 acc_diff: i64,
188} 188}
189 189
190impl<'a> Edits<'a> { 190impl<'a> Edits<'a> {
191 fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> { 191 fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> {
192 let mut x = Edits { edits: text_edit.as_atoms(), current: None, acc_diff: 0 }; 192 let mut x = Edits { edits: text_edit.as_indels(), current: None, acc_diff: 0 };
193 x.advance_edit(); 193 x.advance_edit();
194 x 194 x
195 } 195 }
diff --git a/crates/ra_syntax/src/fuzz.rs b/crates/ra_syntax/src/fuzz.rs
index 10fbe3176..39f9b12ab 100644
--- a/crates/ra_syntax/src/fuzz.rs
+++ b/crates/ra_syntax/src/fuzz.rs
@@ -5,7 +5,7 @@ use std::{
5 str::{self, FromStr}, 5 str::{self, FromStr},
6}; 6};
7 7
8use ra_text_edit::AtomTextEdit; 8use ra_text_edit::Indel;
9 9
10use crate::{validation, AstNode, SourceFile, TextRange}; 10use crate::{validation, AstNode, SourceFile, TextRange};
11 11
@@ -22,7 +22,7 @@ pub fn check_parser(text: &str) {
22#[derive(Debug, Clone)] 22#[derive(Debug, Clone)]
23pub struct CheckReparse { 23pub struct CheckReparse {
24 text: String, 24 text: String,
25 edit: AtomTextEdit, 25 edit: Indel,
26 edited_text: String, 26 edited_text: String,
27} 27}
28 28
@@ -43,7 +43,7 @@ impl CheckReparse {
43 TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap()); 43 TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap());
44 let edited_text = 44 let edited_text =
45 format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]); 45 format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]);
46 let edit = AtomTextEdit { delete, insert }; 46 let edit = Indel { delete, insert };
47 Some(CheckReparse { text, edit, edited_text }) 47 Some(CheckReparse { text, edit, edited_text })
48 } 48 }
49 49
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index d0234cada..1a7348dac 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -39,7 +39,7 @@ pub mod fuzz;
39 39
40use std::{marker::PhantomData, sync::Arc}; 40use std::{marker::PhantomData, sync::Arc};
41 41
42use ra_text_edit::AtomTextEdit; 42use ra_text_edit::Indel;
43use stdx::format_to; 43use stdx::format_to;
44 44
45use crate::syntax_node::GreenNode; 45use crate::syntax_node::GreenNode;
@@ -126,13 +126,13 @@ impl Parse<SourceFile> {
126 buf 126 buf
127 } 127 }
128 128
129 pub fn reparse(&self, edit: &AtomTextEdit) -> Parse<SourceFile> { 129 pub fn reparse(&self, indel: &Indel) -> Parse<SourceFile> {
130 self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) 130 self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel))
131 } 131 }
132 132
133 fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<Parse<SourceFile>> { 133 fn incremental_reparse(&self, indel: &Indel) -> Option<Parse<SourceFile>> {
134 // FIXME: validation errors are not handled here 134 // FIXME: validation errors are not handled here
135 parsing::incremental_reparse(self.tree().syntax(), edit, self.errors.to_vec()).map( 135 parsing::incremental_reparse(self.tree().syntax(), indel, self.errors.to_vec()).map(
136 |(green_node, errors, _reparsed_range)| Parse { 136 |(green_node, errors, _reparsed_range)| Parse {
137 green: green_node, 137 green: green_node,
138 errors: Arc::new(errors), 138 errors: Arc::new(errors),
@@ -141,8 +141,8 @@ impl Parse<SourceFile> {
141 ) 141 )
142 } 142 }
143 143
144 fn full_reparse(&self, edit: &AtomTextEdit) -> Parse<SourceFile> { 144 fn full_reparse(&self, indel: &Indel) -> Parse<SourceFile> {
145 let text = edit.apply(self.tree().syntax().text().to_string()); 145 let text = indel.apply(self.tree().syntax().text().to_string());
146 SourceFile::parse(&text) 146 SourceFile::parse(&text)
147 } 147 }
148} 148}
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs
index ffff0a7b2..6257e3f33 100644
--- a/crates/ra_syntax/src/parsing/reparsing.rs
+++ b/crates/ra_syntax/src/parsing/reparsing.rs
@@ -7,7 +7,7 @@
7//! and try to parse only this block. 7//! and try to parse only this block.
8 8
9use ra_parser::Reparser; 9use ra_parser::Reparser;
10use ra_text_edit::AtomTextEdit; 10use ra_text_edit::Indel;
11 11
12use crate::{ 12use crate::{
13 algo, 13 algo,
@@ -24,7 +24,7 @@ use crate::{
24 24
25pub(crate) fn incremental_reparse( 25pub(crate) fn incremental_reparse(
26 node: &SyntaxNode, 26 node: &SyntaxNode,
27 edit: &AtomTextEdit, 27 edit: &Indel,
28 errors: Vec<SyntaxError>, 28 errors: Vec<SyntaxError>,
29) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 29) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
30 if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) { 30 if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) {
@@ -39,7 +39,7 @@ pub(crate) fn incremental_reparse(
39 39
40fn reparse_token<'node>( 40fn reparse_token<'node>(
41 root: &'node SyntaxNode, 41 root: &'node SyntaxNode,
42 edit: &AtomTextEdit, 42 edit: &Indel,
43) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 43) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
44 let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone(); 44 let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone();
45 let prev_token_kind = prev_token.kind(); 45 let prev_token_kind = prev_token.kind();
@@ -88,7 +88,7 @@ fn reparse_token<'node>(
88 88
89fn reparse_block<'node>( 89fn reparse_block<'node>(
90 root: &'node SyntaxNode, 90 root: &'node SyntaxNode,
91 edit: &AtomTextEdit, 91 edit: &Indel,
92) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 92) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
93 let (node, reparser) = find_reparsable_node(root, edit.delete)?; 93 let (node, reparser) = find_reparsable_node(root, edit.delete)?;
94 let text = get_text_after_edit(node.clone().into(), edit); 94 let text = get_text_after_edit(node.clone().into(), edit);
@@ -108,9 +108,9 @@ fn reparse_block<'node>(
108 Some((node.replace_with(green), new_parser_errors, node.text_range())) 108 Some((node.replace_with(green), new_parser_errors, node.text_range()))
109} 109}
110 110
111fn get_text_after_edit(element: SyntaxElement, edit: &AtomTextEdit) -> String { 111fn get_text_after_edit(element: SyntaxElement, edit: &Indel) -> String {
112 let edit = 112 let edit =
113 AtomTextEdit::replace(edit.delete - element.text_range().start(), edit.insert.clone()); 113 Indel::replace(edit.delete - element.text_range().start(), edit.insert.clone());
114 114
115 let text = match element { 115 let text = match element {
116 NodeOrToken::Token(token) => token.text().to_string(), 116 NodeOrToken::Token(token) => token.text().to_string(),
@@ -167,7 +167,7 @@ fn merge_errors(
167 old_errors: Vec<SyntaxError>, 167 old_errors: Vec<SyntaxError>,
168 new_errors: Vec<SyntaxError>, 168 new_errors: Vec<SyntaxError>,
169 range_before_reparse: TextRange, 169 range_before_reparse: TextRange,
170 edit: &AtomTextEdit, 170 edit: &Indel,
171) -> Vec<SyntaxError> { 171) -> Vec<SyntaxError> {
172 let mut res = Vec::new(); 172 let mut res = Vec::new();
173 173
@@ -198,7 +198,7 @@ mod tests {
198 198
199 fn do_check(before: &str, replace_with: &str, reparsed_len: u32) { 199 fn do_check(before: &str, replace_with: &str, reparsed_len: u32) {
200 let (range, before) = extract_range(before); 200 let (range, before) = extract_range(before);
201 let edit = AtomTextEdit::replace(range, replace_with.to_owned()); 201 let edit = Indel::replace(range, replace_with.to_owned());
202 let after = edit.apply(before.clone()); 202 let after = edit.apply(before.clone());
203 203
204 let fully_reparsed = SourceFile::parse(&after); 204 let fully_reparsed = SourceFile::parse(&after);
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs
index e656260c7..c41bf324b 100644
--- a/crates/ra_text_edit/src/lib.rs
+++ b/crates/ra_text_edit/src/lib.rs
@@ -1,30 +1,40 @@
1//! FIXME: write short doc here 1//! Representation of a `TextEdit`.
2 2//!
3mod text_edit; 3//! `rust-analyzer` never mutates text itself and only sends diffs to clients,
4//! so `TextEdit` is the ultimate representation of the work done by
5//! rust-analyzer.
4 6
5use text_size::{TextRange, TextSize}; 7use text_size::{TextRange, TextSize};
6 8
7pub use crate::text_edit::{TextEdit, TextEditBuilder}; 9/// `InsertDelete` -- a single "atomic" change to text
8 10///
9/// Must not overlap with other `AtomTextEdit`s 11/// Must not overlap with other `InDel`s
10#[derive(Debug, Clone)] 12#[derive(Debug, Clone)]
11pub struct AtomTextEdit { 13pub struct Indel {
14 pub insert: String,
12 /// Refers to offsets in the original text 15 /// Refers to offsets in the original text
13 pub delete: TextRange, 16 pub delete: TextRange,
14 pub insert: String,
15} 17}
16 18
17impl AtomTextEdit { 19#[derive(Debug, Clone)]
18 pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit { 20pub struct TextEdit {
19 AtomTextEdit { delete: range, insert: replace_with } 21 indels: Vec<Indel>,
20 } 22}
21 23
22 pub fn delete(range: TextRange) -> AtomTextEdit { 24#[derive(Debug, Default)]
23 AtomTextEdit::replace(range, String::new()) 25pub struct TextEditBuilder {
24 } 26 indels: Vec<Indel>,
27}
25 28
26 pub fn insert(offset: TextSize, text: String) -> AtomTextEdit { 29impl Indel {
27 AtomTextEdit::replace(TextRange::empty(offset), text) 30 pub fn insert(offset: TextSize, text: String) -> Indel {
31 Indel::replace(TextRange::empty(offset), text)
32 }
33 pub fn delete(range: TextRange) -> Indel {
34 Indel::replace(range, String::new())
35 }
36 pub fn replace(range: TextRange, replace_with: String) -> Indel {
37 Indel { delete: range, insert: replace_with }
28 } 38 }
29 39
30 pub fn apply(&self, mut text: String) -> String { 40 pub fn apply(&self, mut text: String) -> String {
@@ -34,3 +44,90 @@ impl AtomTextEdit {
34 text 44 text
35 } 45 }
36} 46}
47
48impl TextEdit {
49 pub fn insert(offset: TextSize, text: String) -> TextEdit {
50 let mut builder = TextEditBuilder::default();
51 builder.insert(offset, text);
52 builder.finish()
53 }
54
55 pub fn delete(range: TextRange) -> TextEdit {
56 let mut builder = TextEditBuilder::default();
57 builder.delete(range);
58 builder.finish()
59 }
60
61 pub fn replace(range: TextRange, replace_with: String) -> TextEdit {
62 let mut builder = TextEditBuilder::default();
63 builder.replace(range, replace_with);
64 builder.finish()
65 }
66
67 pub(crate) fn from_indels(mut indels: Vec<Indel>) -> TextEdit {
68 indels.sort_by_key(|a| (a.delete.start(), a.delete.end()));
69 for (a1, a2) in indels.iter().zip(indels.iter().skip(1)) {
70 assert!(a1.delete.end() <= a2.delete.start())
71 }
72 TextEdit { indels }
73 }
74
75 pub fn as_indels(&self) -> &[Indel] {
76 &self.indels
77 }
78
79 pub fn apply(&self, text: &str) -> String {
80 let mut total_len = TextSize::of(text);
81 for indel in self.indels.iter() {
82 total_len += TextSize::of(&indel.insert);
83 total_len -= indel.delete.end() - indel.delete.start();
84 }
85 let mut buf = String::with_capacity(total_len.into());
86 let mut prev = 0;
87 for indel in self.indels.iter() {
88 let start: usize = indel.delete.start().into();
89 let end: usize = indel.delete.end().into();
90 if start > prev {
91 buf.push_str(&text[prev..start]);
92 }
93 buf.push_str(&indel.insert);
94 prev = end;
95 }
96 buf.push_str(&text[prev..text.len()]);
97 assert_eq!(TextSize::of(&buf), total_len);
98 buf
99 }
100
101 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> {
102 let mut res = offset;
103 for indel in self.indels.iter() {
104 if indel.delete.start() >= offset {
105 break;
106 }
107 if offset < indel.delete.end() {
108 return None;
109 }
110 res += TextSize::of(&indel.insert);
111 res -= indel.delete.len();
112 }
113 Some(res)
114 }
115}
116
117impl TextEditBuilder {
118 pub fn replace(&mut self, range: TextRange, replace_with: String) {
119 self.indels.push(Indel::replace(range, replace_with))
120 }
121 pub fn delete(&mut self, range: TextRange) {
122 self.indels.push(Indel::delete(range))
123 }
124 pub fn insert(&mut self, offset: TextSize, text: String) {
125 self.indels.push(Indel::insert(offset, text))
126 }
127 pub fn finish(self) -> TextEdit {
128 TextEdit::from_indels(self.indels)
129 }
130 pub fn invalidates_offset(&self, offset: TextSize) -> bool {
131 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
132 }
133}
diff --git a/crates/ra_text_edit/src/text_edit.rs b/crates/ra_text_edit/src/text_edit.rs
deleted file mode 100644
index eabab4b4d..000000000
--- a/crates/ra_text_edit/src/text_edit.rs
+++ /dev/null
@@ -1,102 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::AtomTextEdit;
4
5use text_size::{TextRange, TextSize};
6
7#[derive(Debug, Clone)]
8pub struct TextEdit {
9 atoms: Vec<AtomTextEdit>,
10}
11
12#[derive(Debug, Default)]
13pub struct TextEditBuilder {
14 atoms: Vec<AtomTextEdit>,
15}
16
17impl TextEditBuilder {
18 pub fn replace(&mut self, range: TextRange, replace_with: String) {
19 self.atoms.push(AtomTextEdit::replace(range, replace_with))
20 }
21 pub fn delete(&mut self, range: TextRange) {
22 self.atoms.push(AtomTextEdit::delete(range))
23 }
24 pub fn insert(&mut self, offset: TextSize, text: String) {
25 self.atoms.push(AtomTextEdit::insert(offset, text))
26 }
27 pub fn finish(self) -> TextEdit {
28 TextEdit::from_atoms(self.atoms)
29 }
30 pub fn invalidates_offset(&self, offset: TextSize) -> bool {
31 self.atoms.iter().any(|atom| atom.delete.contains_inclusive(offset))
32 }
33}
34
35impl TextEdit {
36 pub fn insert(offset: TextSize, text: String) -> TextEdit {
37 let mut builder = TextEditBuilder::default();
38 builder.insert(offset, text);
39 builder.finish()
40 }
41
42 pub fn delete(range: TextRange) -> TextEdit {
43 let mut builder = TextEditBuilder::default();
44 builder.delete(range);
45 builder.finish()
46 }
47
48 pub fn replace(range: TextRange, replace_with: String) -> TextEdit {
49 let mut builder = TextEditBuilder::default();
50 builder.replace(range, replace_with);
51 builder.finish()
52 }
53
54 pub(crate) fn from_atoms(mut atoms: Vec<AtomTextEdit>) -> TextEdit {
55 atoms.sort_by_key(|a| (a.delete.start(), a.delete.end()));
56 for (a1, a2) in atoms.iter().zip(atoms.iter().skip(1)) {
57 assert!(a1.delete.end() <= a2.delete.start())
58 }
59 TextEdit { atoms }
60 }
61
62 pub fn as_atoms(&self) -> &[AtomTextEdit] {
63 &self.atoms
64 }
65
66 pub fn apply(&self, text: &str) -> String {
67 let mut total_len = TextSize::of(text);
68 for atom in self.atoms.iter() {
69 total_len += TextSize::of(&atom.insert);
70 total_len -= atom.delete.end() - atom.delete.start();
71 }
72 let mut buf = String::with_capacity(total_len.into());
73 let mut prev = 0;
74 for atom in self.atoms.iter() {
75 let start: usize = atom.delete.start().into();
76 let end: usize = atom.delete.end().into();
77 if start > prev {
78 buf.push_str(&text[prev..start]);
79 }
80 buf.push_str(&atom.insert);
81 prev = end;
82 }
83 buf.push_str(&text[prev..text.len()]);
84 assert_eq!(TextSize::of(&buf), total_len);
85 buf
86 }
87
88 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> {
89 let mut res = offset;
90 for atom in self.atoms.iter() {
91 if atom.delete.start() >= offset {
92 break;
93 }
94 if offset < atom.delete.end() {
95 return None;
96 }
97 res += TextSize::of(&atom.insert);
98 res -= atom.delete.len();
99 }
100 Some(res)
101 }
102}
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index 7be5ebcdb..f64c90b5b 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -15,7 +15,7 @@ use ra_ide::{
15 ReferenceAccess, Severity, SourceChange, SourceFileEdit, 15 ReferenceAccess, Severity, SourceChange, SourceFileEdit,
16}; 16};
17use ra_syntax::{SyntaxKind, TextRange, TextSize}; 17use ra_syntax::{SyntaxKind, TextRange, TextSize};
18use ra_text_edit::{AtomTextEdit, TextEdit}; 18use ra_text_edit::{Indel, TextEdit};
19use ra_vfs::LineEndings; 19use ra_vfs::LineEndings;
20 20
21use crate::{ 21use crate::{
@@ -124,23 +124,22 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
124 let mut text_edit = None; 124 let mut text_edit = None;
125 // LSP does not allow arbitrary edits in completion, so we have to do a 125 // LSP does not allow arbitrary edits in completion, so we have to do a
126 // non-trivial mapping here. 126 // non-trivial mapping here.
127 for atom_edit in self.text_edit().as_atoms() { 127 for indel in self.text_edit().as_indels() {
128 if atom_edit.delete.contains_range(self.source_range()) { 128 if indel.delete.contains_range(self.source_range()) {
129 text_edit = Some(if atom_edit.delete == self.source_range() { 129 text_edit = Some(if indel.delete == self.source_range() {
130 atom_edit.conv_with((ctx.0, ctx.1)) 130 indel.conv_with((ctx.0, ctx.1))
131 } else { 131 } else {
132 assert!(self.source_range().end() == atom_edit.delete.end()); 132 assert!(self.source_range().end() == indel.delete.end());
133 let range1 = 133 let range1 = TextRange::new(indel.delete.start(), self.source_range().start());
134 TextRange::new(atom_edit.delete.start(), self.source_range().start());
135 let range2 = self.source_range(); 134 let range2 = self.source_range();
136 let edit1 = AtomTextEdit::replace(range1, String::new()); 135 let edit1 = Indel::replace(range1, String::new());
137 let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone()); 136 let edit2 = Indel::replace(range2, indel.insert.clone());
138 additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1))); 137 additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1)));
139 edit2.conv_with((ctx.0, ctx.1)) 138 edit2.conv_with((ctx.0, ctx.1))
140 }) 139 })
141 } else { 140 } else {
142 assert!(self.source_range().intersect(atom_edit.delete).is_none()); 141 assert!(self.source_range().intersect(indel.delete).is_none());
143 additional_text_edits.push(atom_edit.conv_with((ctx.0, ctx.1))); 142 additional_text_edits.push(indel.conv_with((ctx.0, ctx.1)));
144 } 143 }
145 } 144 }
146 let text_edit = text_edit.unwrap(); 145 let text_edit = text_edit.unwrap();
@@ -257,11 +256,11 @@ impl ConvWith<(&LineIndex, LineEndings)> for TextEdit {
257 type Output = Vec<lsp_types::TextEdit>; 256 type Output = Vec<lsp_types::TextEdit>;
258 257
259 fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec<lsp_types::TextEdit> { 258 fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec<lsp_types::TextEdit> {
260 self.as_atoms().iter().map_conv_with(ctx).collect() 259 self.as_indels().iter().map_conv_with(ctx).collect()
261 } 260 }
262} 261}
263 262
264impl ConvWith<(&LineIndex, LineEndings)> for &AtomTextEdit { 263impl ConvWith<(&LineIndex, LineEndings)> for &Indel {
265 type Output = lsp_types::TextEdit; 264 type Output = lsp_types::TextEdit;
266 265
267 fn conv_with( 266 fn conv_with(
@@ -522,7 +521,7 @@ impl TryConvWith<&WorldSnapshot> for SourceFileEdit {
522 let line_index = world.analysis().file_line_index(self.file_id)?; 521 let line_index = world.analysis().file_line_index(self.file_id)?;
523 let line_endings = world.file_line_endings(self.file_id); 522 let line_endings = world.file_line_endings(self.file_id);
524 let edits = 523 let edits =
525 self.edit.as_atoms().iter().map_conv_with((&line_index, line_endings)).collect(); 524 self.edit.as_indels().iter().map_conv_with((&line_index, line_endings)).collect();
526 Ok(TextDocumentEdit { text_document, edits }) 525 Ok(TextDocumentEdit { text_document, edits })
527 } 526 }
528} 527}