diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-05-21 14:10:00 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-05-21 14:10:00 +0100 |
commit | 3cba0dc26b707bebc1865671fd2c5139c1e1c537 (patch) | |
tree | 6d77f69e3299d4245ebae8987fc030348ae2ebb9 /crates | |
parent | 0c2b548b0b5712dcc2f9a4eead57e028b5461ba7 (diff) | |
parent | ff28c79ebd4c5a9a27542917940def9d4e66eea6 (diff) |
Merge #4552
4552: Transition OnEnter to WorkspaceSnippetEdit r=matklad a=matklad
This also changes our handiling of snippet edits on the client side.
`editor.insertSnippet` unfortunately forces indentation, which we
really don't want to have to deal with. So, let's just implement our
manual hacky way of dealing with a simple subset of snippets we
actually use in rust-analyzer
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/tests.rs | 9 | ||||
-rw-r--r-- | crates/ra_ide/src/diagnostics.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ra_ide/src/references/rename.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide/src/typing/on_enter.rs | 29 | ||||
-rw-r--r-- | crates/ra_ide_db/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ra_ide_db/src/line_index_utils.rs | 302 | ||||
-rw-r--r-- | crates/ra_ide_db/src/source_change.rs | 19 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 63 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/main.rs | 68 |
12 files changed, 56 insertions, 447 deletions
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index 373a7f7cc..62dd3547f 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs | |||
@@ -7,8 +7,7 @@ use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; | |||
7 | use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; | 7 | use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; |
8 | use ra_syntax::TextRange; | 8 | use ra_syntax::TextRange; |
9 | use test_utils::{ | 9 | use test_utils::{ |
10 | add_cursor, assert_eq_text, extract_offset, extract_range, extract_range_or_offset, | 10 | assert_eq_text, extract_offset, extract_range, extract_range_or_offset, RangeOrOffset, |
11 | RangeOrOffset, | ||
12 | }; | 11 | }; |
13 | 12 | ||
14 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; | 13 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; |
@@ -103,12 +102,6 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) { | |||
103 | 102 | ||
104 | let mut actual = db.file_text(change.file_id).as_ref().to_owned(); | 103 | let mut actual = db.file_text(change.file_id).as_ref().to_owned(); |
105 | change.edit.apply(&mut actual); | 104 | change.edit.apply(&mut actual); |
106 | |||
107 | if !source_change.is_snippet { | ||
108 | if let Some(off) = source_change.cursor_position { | ||
109 | actual = add_cursor(&actual, off.offset) | ||
110 | } | ||
111 | } | ||
112 | assert_eq_text!(after, &actual); | 105 | assert_eq_text!(after, &actual); |
113 | } | 106 | } |
114 | (Some(assist), ExpectedResult::Target(target)) => { | 107 | (Some(assist), ExpectedResult::Target(target)) => { |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 54c2bcc09..c2819bbf7 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -628,7 +628,6 @@ mod tests { | |||
628 | path: "foo.rs", | 628 | path: "foo.rs", |
629 | }, | 629 | }, |
630 | ], | 630 | ], |
631 | cursor_position: None, | ||
632 | is_snippet: false, | 631 | is_snippet: false, |
633 | }, | 632 | }, |
634 | ), | 633 | ), |
@@ -685,7 +684,6 @@ mod tests { | |||
685 | }, | 684 | }, |
686 | ], | 685 | ], |
687 | file_system_edits: [], | 686 | file_system_edits: [], |
688 | cursor_position: None, | ||
689 | is_snippet: false, | 687 | is_snippet: false, |
690 | }, | 688 | }, |
691 | ), | 689 | ), |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 83cb498f7..1d7bacbf6 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -87,7 +87,6 @@ pub use ra_db::{ | |||
87 | pub use ra_ide_db::{ | 87 | pub use ra_ide_db::{ |
88 | change::{AnalysisChange, LibraryData}, | 88 | change::{AnalysisChange, LibraryData}, |
89 | line_index::{LineCol, LineIndex}, | 89 | line_index::{LineCol, LineIndex}, |
90 | line_index_utils::translate_offset_with_edit, | ||
91 | search::SearchScope, | 90 | search::SearchScope, |
92 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, | 91 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, |
93 | symbol_index::Query, | 92 | symbol_index::Query, |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 62ec8d85d..55c3319cb 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -669,7 +669,6 @@ mod tests { | |||
669 | dst_path: "bar/foo2.rs", | 669 | dst_path: "bar/foo2.rs", |
670 | }, | 670 | }, |
671 | ], | 671 | ], |
672 | cursor_position: None, | ||
673 | is_snippet: false, | 672 | is_snippet: false, |
674 | }, | 673 | }, |
675 | }, | 674 | }, |
@@ -722,7 +721,6 @@ mod tests { | |||
722 | dst_path: "foo2/mod.rs", | 721 | dst_path: "foo2/mod.rs", |
723 | }, | 722 | }, |
724 | ], | 723 | ], |
725 | cursor_position: None, | ||
726 | is_snippet: false, | 724 | is_snippet: false, |
727 | }, | 725 | }, |
728 | }, | 726 | }, |
@@ -819,7 +817,6 @@ mod tests { | |||
819 | dst_path: "bar/foo2.rs", | 817 | dst_path: "bar/foo2.rs", |
820 | }, | 818 | }, |
821 | ], | 819 | ], |
822 | cursor_position: None, | ||
823 | is_snippet: false, | 820 | is_snippet: false, |
824 | }, | 821 | }, |
825 | }, | 822 | }, |
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs index 78a40cc94..85be14ad3 100644 --- a/crates/ra_ide/src/typing/on_enter.rs +++ b/crates/ra_ide/src/typing/on_enter.rs | |||
@@ -38,17 +38,15 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour | |||
38 | } | 38 | } |
39 | 39 | ||
40 | let indent = node_indent(&file, comment.syntax())?; | 40 | let indent = node_indent(&file, comment.syntax())?; |
41 | let inserted = format!("\n{}{} ", indent, prefix); | 41 | let inserted = format!("\n{}{} $0", indent, prefix); |
42 | let cursor_position = position.offset + TextSize::of(&inserted); | ||
43 | let edit = TextEdit::insert(position.offset, inserted); | 42 | let edit = TextEdit::insert(position.offset, inserted); |
44 | 43 | ||
45 | Some( | 44 | let mut res = SourceChange::source_file_edit( |
46 | SourceChange::source_file_edit( | 45 | "On enter", |
47 | "On enter", | 46 | SourceFileEdit { edit, file_id: position.file_id }, |
48 | SourceFileEdit { edit, file_id: position.file_id }, | 47 | ); |
49 | ) | 48 | res.is_snippet = true; |
50 | .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }), | 49 | Some(res) |
51 | ) | ||
52 | } | 50 | } |
53 | 51 | ||
54 | fn followed_by_comment(comment: &ast::Comment) -> bool { | 52 | fn followed_by_comment(comment: &ast::Comment) -> bool { |
@@ -84,7 +82,7 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> { | |||
84 | 82 | ||
85 | #[cfg(test)] | 83 | #[cfg(test)] |
86 | mod tests { | 84 | mod tests { |
87 | use test_utils::{add_cursor, assert_eq_text, extract_offset}; | 85 | use test_utils::{assert_eq_text, extract_offset}; |
88 | 86 | ||
89 | use crate::mock_analysis::single_file; | 87 | use crate::mock_analysis::single_file; |
90 | 88 | ||
@@ -98,7 +96,6 @@ mod tests { | |||
98 | assert_eq!(result.source_file_edits.len(), 1); | 96 | assert_eq!(result.source_file_edits.len(), 1); |
99 | let mut actual = before.to_string(); | 97 | let mut actual = before.to_string(); |
100 | result.source_file_edits[0].edit.apply(&mut actual); | 98 | result.source_file_edits[0].edit.apply(&mut actual); |
101 | let actual = add_cursor(&actual, result.cursor_position.unwrap().offset); | ||
102 | Some(actual) | 99 | Some(actual) |
103 | } | 100 | } |
104 | 101 | ||
@@ -121,7 +118,7 @@ fn foo() { | |||
121 | ", | 118 | ", |
122 | r" | 119 | r" |
123 | /// Some docs | 120 | /// Some docs |
124 | /// <|> | 121 | /// $0 |
125 | fn foo() { | 122 | fn foo() { |
126 | } | 123 | } |
127 | ", | 124 | ", |
@@ -137,7 +134,7 @@ impl S { | |||
137 | r" | 134 | r" |
138 | impl S { | 135 | impl S { |
139 | /// Some | 136 | /// Some |
140 | /// <|> docs. | 137 | /// $0 docs. |
141 | fn foo() {} | 138 | fn foo() {} |
142 | } | 139 | } |
143 | ", | 140 | ", |
@@ -151,7 +148,7 @@ fn foo() { | |||
151 | ", | 148 | ", |
152 | r" | 149 | r" |
153 | /// | 150 | /// |
154 | /// <|> Some docs | 151 | /// $0 Some docs |
155 | fn foo() { | 152 | fn foo() { |
156 | } | 153 | } |
157 | ", | 154 | ", |
@@ -175,7 +172,7 @@ fn main() { | |||
175 | r" | 172 | r" |
176 | fn main() { | 173 | fn main() { |
177 | // Fix | 174 | // Fix |
178 | // <|> me | 175 | // $0 me |
179 | let x = 1 + 1; | 176 | let x = 1 + 1; |
180 | } | 177 | } |
181 | ", | 178 | ", |
@@ -195,7 +192,7 @@ fn main() { | |||
195 | r" | 192 | r" |
196 | fn main() { | 193 | fn main() { |
197 | // Fix | 194 | // Fix |
198 | // <|> | 195 | // $0 |
199 | // me | 196 | // me |
200 | let x = 1 + 1; | 197 | let x = 1 + 1; |
201 | } | 198 | } |
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 4f37954bf..1b74e6558 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. | 3 | //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. |
4 | 4 | ||
5 | pub mod line_index; | 5 | pub mod line_index; |
6 | pub mod line_index_utils; | ||
7 | pub mod symbol_index; | 6 | pub mod symbol_index; |
8 | pub mod change; | 7 | pub mod change; |
9 | pub mod defs; | 8 | pub mod defs; |
diff --git a/crates/ra_ide_db/src/line_index_utils.rs b/crates/ra_ide_db/src/line_index_utils.rs deleted file mode 100644 index 7fa6fc448..000000000 --- a/crates/ra_ide_db/src/line_index_utils.rs +++ /dev/null | |||
@@ -1,302 +0,0 @@ | |||
1 | //! Code actions can specify desirable final position of the cursor. | ||
2 | //! | ||
3 | //! The position is specified as a `TextSize` in the final file. We need to send | ||
4 | //! it in `(Line, Column)` coordinate though. However, we only have a LineIndex | ||
5 | //! for a file pre-edit! | ||
6 | //! | ||
7 | //! Code in this module applies this "to (Line, Column) after edit" | ||
8 | //! transformation. | ||
9 | |||
10 | use std::convert::TryInto; | ||
11 | |||
12 | use ra_syntax::{TextRange, TextSize}; | ||
13 | use ra_text_edit::{Indel, TextEdit}; | ||
14 | |||
15 | use crate::line_index::{LineCol, LineIndex, Utf16Char}; | ||
16 | |||
17 | pub fn translate_offset_with_edit( | ||
18 | line_index: &LineIndex, | ||
19 | offset: TextSize, | ||
20 | text_edit: &TextEdit, | ||
21 | ) -> LineCol { | ||
22 | let mut state = Edits::from_text_edit(&text_edit); | ||
23 | |||
24 | let mut res = RunningLineCol::new(); | ||
25 | |||
26 | macro_rules! test_step { | ||
27 | ($x:ident) => { | ||
28 | match &$x { | ||
29 | Step::Newline(n) => { | ||
30 | if offset < *n { | ||
31 | return res.to_line_col(offset); | ||
32 | } else { | ||
33 | res.add_line(*n); | ||
34 | } | ||
35 | } | ||
36 | Step::Utf16Char(x) => { | ||
37 | if offset < x.end() { | ||
38 | // if the offset is inside a multibyte char it's invalid | ||
39 | // clamp it to the start of the char | ||
40 | let clamp = offset.min(x.start()); | ||
41 | return res.to_line_col(clamp); | ||
42 | } else { | ||
43 | res.adjust_col(*x); | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | }; | ||
48 | } | ||
49 | |||
50 | for orig_step in LineIndexStepIter::from(line_index) { | ||
51 | loop { | ||
52 | let translated_step = state.translate_step(&orig_step); | ||
53 | match state.next_steps(&translated_step) { | ||
54 | NextSteps::Use => { | ||
55 | test_step!(translated_step); | ||
56 | break; | ||
57 | } | ||
58 | NextSteps::ReplaceMany(ns) => { | ||
59 | for n in ns { | ||
60 | test_step!(n); | ||
61 | } | ||
62 | break; | ||
63 | } | ||
64 | NextSteps::AddMany(ns) => { | ||
65 | for n in ns { | ||
66 | test_step!(n); | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | loop { | ||
74 | match state.next_inserted_steps() { | ||
75 | None => break, | ||
76 | Some(ns) => { | ||
77 | for n in ns { | ||
78 | test_step!(n); | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | |||
84 | res.to_line_col(offset) | ||
85 | } | ||
86 | |||
87 | #[derive(Debug, Clone)] | ||
88 | enum Step { | ||
89 | Newline(TextSize), | ||
90 | Utf16Char(TextRange), | ||
91 | } | ||
92 | |||
93 | #[derive(Debug)] | ||
94 | struct LineIndexStepIter<'a> { | ||
95 | line_index: &'a LineIndex, | ||
96 | next_newline_idx: usize, | ||
97 | utf16_chars: Option<(TextSize, std::slice::Iter<'a, Utf16Char>)>, | ||
98 | } | ||
99 | |||
100 | impl LineIndexStepIter<'_> { | ||
101 | fn from(line_index: &LineIndex) -> LineIndexStepIter { | ||
102 | let mut x = LineIndexStepIter { line_index, next_newline_idx: 0, utf16_chars: None }; | ||
103 | // skip first newline since it's not real | ||
104 | x.next(); | ||
105 | x | ||
106 | } | ||
107 | } | ||
108 | |||
109 | impl Iterator for LineIndexStepIter<'_> { | ||
110 | type Item = Step; | ||
111 | fn next(&mut self) -> Option<Step> { | ||
112 | self.utf16_chars | ||
113 | .as_mut() | ||
114 | .and_then(|(newline, x)| { | ||
115 | let x = x.next()?; | ||
116 | Some(Step::Utf16Char(TextRange::new(*newline + x.start, *newline + x.end))) | ||
117 | }) | ||
118 | .or_else(|| { | ||
119 | let next_newline = *self.line_index.newlines.get(self.next_newline_idx)?; | ||
120 | self.utf16_chars = self | ||
121 | .line_index | ||
122 | .utf16_lines | ||
123 | .get(&(self.next_newline_idx as u32)) | ||
124 | .map(|x| (next_newline, x.iter())); | ||
125 | self.next_newline_idx += 1; | ||
126 | Some(Step::Newline(next_newline)) | ||
127 | }) | ||
128 | } | ||
129 | } | ||
130 | |||
131 | #[derive(Debug)] | ||
132 | struct OffsetStepIter<'a> { | ||
133 | text: &'a str, | ||
134 | offset: TextSize, | ||
135 | } | ||
136 | |||
137 | impl Iterator for OffsetStepIter<'_> { | ||
138 | type Item = Step; | ||
139 | fn next(&mut self) -> Option<Step> { | ||
140 | let (next, next_offset) = self | ||
141 | .text | ||
142 | .char_indices() | ||
143 | .filter_map(|(i, c)| { | ||
144 | let i: TextSize = i.try_into().unwrap(); | ||
145 | let char_len = TextSize::of(c); | ||
146 | if c == '\n' { | ||
147 | let next_offset = self.offset + i + char_len; | ||
148 | let next = Step::Newline(next_offset); | ||
149 | Some((next, next_offset)) | ||
150 | } else { | ||
151 | if !c.is_ascii() { | ||
152 | let start = self.offset + i; | ||
153 | let end = start + char_len; | ||
154 | let next = Step::Utf16Char(TextRange::new(start, end)); | ||
155 | let next_offset = end; | ||
156 | Some((next, next_offset)) | ||
157 | } else { | ||
158 | None | ||
159 | } | ||
160 | } | ||
161 | }) | ||
162 | .next()?; | ||
163 | let next_idx: usize = (next_offset - self.offset).into(); | ||
164 | self.text = &self.text[next_idx..]; | ||
165 | self.offset = next_offset; | ||
166 | Some(next) | ||
167 | } | ||
168 | } | ||
169 | |||
170 | #[derive(Debug)] | ||
171 | enum NextSteps<'a> { | ||
172 | Use, | ||
173 | ReplaceMany(OffsetStepIter<'a>), | ||
174 | AddMany(OffsetStepIter<'a>), | ||
175 | } | ||
176 | |||
177 | #[derive(Debug)] | ||
178 | struct TranslatedEdit<'a> { | ||
179 | delete: TextRange, | ||
180 | insert: &'a str, | ||
181 | diff: i64, | ||
182 | } | ||
183 | |||
184 | struct Edits<'a> { | ||
185 | edits: &'a [Indel], | ||
186 | current: Option<TranslatedEdit<'a>>, | ||
187 | acc_diff: i64, | ||
188 | } | ||
189 | |||
190 | impl<'a> Edits<'a> { | ||
191 | fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> { | ||
192 | let mut x = Edits { edits: text_edit.as_indels(), current: None, acc_diff: 0 }; | ||
193 | x.advance_edit(); | ||
194 | x | ||
195 | } | ||
196 | fn advance_edit(&mut self) { | ||
197 | self.acc_diff += self.current.as_ref().map_or(0, |x| x.diff); | ||
198 | match self.edits.split_first() { | ||
199 | Some((next, rest)) => { | ||
200 | let delete = self.translate_range(next.delete); | ||
201 | let diff = next.insert.len() as i64 - usize::from(next.delete.len()) as i64; | ||
202 | self.current = Some(TranslatedEdit { delete, insert: &next.insert, diff }); | ||
203 | self.edits = rest; | ||
204 | } | ||
205 | None => { | ||
206 | self.current = None; | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | fn next_inserted_steps(&mut self) -> Option<OffsetStepIter<'a>> { | ||
212 | let cur = self.current.as_ref()?; | ||
213 | let res = Some(OffsetStepIter { offset: cur.delete.start(), text: &cur.insert }); | ||
214 | self.advance_edit(); | ||
215 | res | ||
216 | } | ||
217 | |||
218 | fn next_steps(&mut self, step: &Step) -> NextSteps { | ||
219 | let step_pos = match *step { | ||
220 | Step::Newline(n) => n, | ||
221 | Step::Utf16Char(r) => r.end(), | ||
222 | }; | ||
223 | match &mut self.current { | ||
224 | Some(edit) => { | ||
225 | if step_pos <= edit.delete.start() { | ||
226 | NextSteps::Use | ||
227 | } else if step_pos <= edit.delete.end() { | ||
228 | let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert }; | ||
229 | // empty slice to avoid returning steps again | ||
230 | edit.insert = &edit.insert[edit.insert.len()..]; | ||
231 | NextSteps::ReplaceMany(iter) | ||
232 | } else { | ||
233 | let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert }; | ||
234 | // empty slice to avoid returning steps again | ||
235 | edit.insert = &edit.insert[edit.insert.len()..]; | ||
236 | self.advance_edit(); | ||
237 | NextSteps::AddMany(iter) | ||
238 | } | ||
239 | } | ||
240 | None => NextSteps::Use, | ||
241 | } | ||
242 | } | ||
243 | |||
244 | fn translate_range(&self, range: TextRange) -> TextRange { | ||
245 | if self.acc_diff == 0 { | ||
246 | range | ||
247 | } else { | ||
248 | let start = self.translate(range.start()); | ||
249 | let end = self.translate(range.end()); | ||
250 | TextRange::new(start, end) | ||
251 | } | ||
252 | } | ||
253 | |||
254 | fn translate(&self, x: TextSize) -> TextSize { | ||
255 | if self.acc_diff == 0 { | ||
256 | x | ||
257 | } else { | ||
258 | TextSize::from((usize::from(x) as i64 + self.acc_diff) as u32) | ||
259 | } | ||
260 | } | ||
261 | |||
262 | fn translate_step(&self, x: &Step) -> Step { | ||
263 | if self.acc_diff == 0 { | ||
264 | x.clone() | ||
265 | } else { | ||
266 | match *x { | ||
267 | Step::Newline(n) => Step::Newline(self.translate(n)), | ||
268 | Step::Utf16Char(r) => Step::Utf16Char(self.translate_range(r)), | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | |||
274 | #[derive(Debug)] | ||
275 | struct RunningLineCol { | ||
276 | line: u32, | ||
277 | last_newline: TextSize, | ||
278 | col_adjust: TextSize, | ||
279 | } | ||
280 | |||
281 | impl RunningLineCol { | ||
282 | fn new() -> RunningLineCol { | ||
283 | RunningLineCol { line: 0, last_newline: TextSize::from(0), col_adjust: TextSize::from(0) } | ||
284 | } | ||
285 | |||
286 | fn to_line_col(&self, offset: TextSize) -> LineCol { | ||
287 | LineCol { | ||
288 | line: self.line, | ||
289 | col_utf16: ((offset - self.last_newline) - self.col_adjust).into(), | ||
290 | } | ||
291 | } | ||
292 | |||
293 | fn add_line(&mut self, newline: TextSize) { | ||
294 | self.line += 1; | ||
295 | self.last_newline = newline; | ||
296 | self.col_adjust = TextSize::from(0); | ||
297 | } | ||
298 | |||
299 | fn adjust_col(&mut self, range: TextRange) { | ||
300 | self.col_adjust += range.len() - TextSize::from(1); | ||
301 | } | ||
302 | } | ||
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs index 94e118dd8..3484f5588 100644 --- a/crates/ra_ide_db/src/source_change.rs +++ b/crates/ra_ide_db/src/source_change.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | //! | 3 | //! |
4 | //! It can be viewed as a dual for `AnalysisChange`. | 4 | //! It can be viewed as a dual for `AnalysisChange`. |
5 | 5 | ||
6 | use ra_db::{FileId, FilePosition, RelativePathBuf, SourceRootId}; | 6 | use ra_db::{FileId, RelativePathBuf, SourceRootId}; |
7 | use ra_text_edit::TextEdit; | 7 | use ra_text_edit::TextEdit; |
8 | 8 | ||
9 | #[derive(Debug, Clone)] | 9 | #[derive(Debug, Clone)] |
@@ -12,7 +12,6 @@ pub struct SourceChange { | |||
12 | pub label: String, | 12 | pub label: String, |
13 | pub source_file_edits: Vec<SourceFileEdit>, | 13 | pub source_file_edits: Vec<SourceFileEdit>, |
14 | pub file_system_edits: Vec<FileSystemEdit>, | 14 | pub file_system_edits: Vec<FileSystemEdit>, |
15 | pub cursor_position: Option<FilePosition>, | ||
16 | pub is_snippet: bool, | 15 | pub is_snippet: bool, |
17 | } | 16 | } |
18 | 17 | ||
@@ -28,7 +27,6 @@ impl SourceChange { | |||
28 | label: label.into(), | 27 | label: label.into(), |
29 | source_file_edits, | 28 | source_file_edits, |
30 | file_system_edits, | 29 | file_system_edits, |
31 | cursor_position: None, | ||
32 | is_snippet: false, | 30 | is_snippet: false, |
33 | } | 31 | } |
34 | } | 32 | } |
@@ -42,7 +40,6 @@ impl SourceChange { | |||
42 | label: label, | 40 | label: label, |
43 | source_file_edits: edits, | 41 | source_file_edits: edits, |
44 | file_system_edits: vec![], | 42 | file_system_edits: vec![], |
45 | cursor_position: None, | ||
46 | is_snippet: false, | 43 | is_snippet: false, |
47 | } | 44 | } |
48 | } | 45 | } |
@@ -54,7 +51,6 @@ impl SourceChange { | |||
54 | label: label.into(), | 51 | label: label.into(), |
55 | source_file_edits: vec![], | 52 | source_file_edits: vec![], |
56 | file_system_edits: edits, | 53 | file_system_edits: edits, |
57 | cursor_position: None, | ||
58 | is_snippet: false, | 54 | is_snippet: false, |
59 | } | 55 | } |
60 | } | 56 | } |
@@ -80,18 +76,6 @@ impl SourceChange { | |||
80 | pub fn file_system_edit<L: Into<String>>(label: L, edit: FileSystemEdit) -> Self { | 76 | pub fn file_system_edit<L: Into<String>>(label: L, edit: FileSystemEdit) -> Self { |
81 | SourceChange::file_system_edits(label, vec![edit]) | 77 | SourceChange::file_system_edits(label, vec![edit]) |
82 | } | 78 | } |
83 | |||
84 | /// Sets the cursor position to the given `FilePosition` | ||
85 | pub fn with_cursor(mut self, cursor_position: FilePosition) -> Self { | ||
86 | self.cursor_position = Some(cursor_position); | ||
87 | self | ||
88 | } | ||
89 | |||
90 | /// Sets the cursor position to the given `FilePosition` | ||
91 | pub fn with_cursor_opt(mut self, cursor_position: Option<FilePosition>) -> Self { | ||
92 | self.cursor_position = cursor_position; | ||
93 | self | ||
94 | } | ||
95 | } | 79 | } |
96 | 80 | ||
97 | #[derive(Debug, Clone)] | 81 | #[derive(Debug, Clone)] |
@@ -117,7 +101,6 @@ impl SingleFileChange { | |||
117 | label: self.label, | 101 | label: self.label, |
118 | source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], | 102 | source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], |
119 | file_system_edits: Vec::new(), | 103 | file_system_edits: Vec::new(), |
120 | cursor_position: None, | ||
121 | is_snippet: false, | 104 | is_snippet: false, |
122 | } | 105 | } |
123 | } | 106 | } |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index f75a26eb7..3c7bd609d 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -102,7 +102,7 @@ pub enum OnEnter {} | |||
102 | 102 | ||
103 | impl Request for OnEnter { | 103 | impl Request for OnEnter { |
104 | type Params = lsp_types::TextDocumentPositionParams; | 104 | type Params = lsp_types::TextDocumentPositionParams; |
105 | type Result = Option<SourceChange>; | 105 | type Result = Option<SnippetWorkspaceEdit>; |
106 | const METHOD: &'static str = "rust-analyzer/onEnter"; | 106 | const METHOD: &'static str = "rust-analyzer/onEnter"; |
107 | } | 107 | } |
108 | 108 | ||
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index be6a0aece..fcf08cd79 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -159,12 +159,12 @@ pub fn handle_join_lines( | |||
159 | pub fn handle_on_enter( | 159 | pub fn handle_on_enter( |
160 | world: WorldSnapshot, | 160 | world: WorldSnapshot, |
161 | params: lsp_types::TextDocumentPositionParams, | 161 | params: lsp_types::TextDocumentPositionParams, |
162 | ) -> Result<Option<lsp_ext::SourceChange>> { | 162 | ) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { |
163 | let _p = profile("handle_on_enter"); | 163 | let _p = profile("handle_on_enter"); |
164 | let position = from_proto::file_position(&world, params)?; | 164 | let position = from_proto::file_position(&world, params)?; |
165 | match world.analysis().on_enter(position)? { | 165 | match world.analysis().on_enter(position)? { |
166 | None => Ok(None), | 166 | None => Ok(None), |
167 | Some(source_change) => to_proto::source_change(&world, source_change).map(Some), | 167 | Some(source_change) => to_proto::snippet_workspace_edit(&world, source_change).map(Some), |
168 | } | 168 | } |
169 | } | 169 | } |
170 | 170 | ||
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index af54f81b7..9a8e9e174 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. | 1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. |
2 | use ra_db::{FileId, FileRange}; | 2 | use ra_db::{FileId, FileRange}; |
3 | use ra_ide::{ | 3 | use ra_ide::{ |
4 | translate_offset_with_edit, Assist, CompletionItem, CompletionItemKind, Documentation, | 4 | Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, |
5 | FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, | 5 | FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, InlayHint, |
6 | HighlightedRange, InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, | 6 | InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, |
7 | ReferenceAccess, Severity, SourceChange, SourceFileEdit, | 7 | SourceChange, SourceFileEdit, |
8 | }; | 8 | }; |
9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | 9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; |
10 | use ra_text_edit::{Indel, TextEdit}; | 10 | use ra_text_edit::{Indel, TextEdit}; |
@@ -375,14 +375,6 @@ pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::U | |||
375 | world.file_id_to_uri(file_id) | 375 | world.file_id_to_uri(file_id) |
376 | } | 376 | } |
377 | 377 | ||
378 | pub(crate) fn text_document_identifier( | ||
379 | world: &WorldSnapshot, | ||
380 | file_id: FileId, | ||
381 | ) -> Result<lsp_types::TextDocumentIdentifier> { | ||
382 | let res = lsp_types::TextDocumentIdentifier { uri: url(world, file_id)? }; | ||
383 | Ok(res) | ||
384 | } | ||
385 | |||
386 | pub(crate) fn versioned_text_document_identifier( | 378 | pub(crate) fn versioned_text_document_identifier( |
387 | world: &WorldSnapshot, | 379 | world: &WorldSnapshot, |
388 | file_id: FileId, | 380 | file_id: FileId, |
@@ -496,30 +488,9 @@ pub(crate) fn source_change( | |||
496 | world: &WorldSnapshot, | 488 | world: &WorldSnapshot, |
497 | source_change: SourceChange, | 489 | source_change: SourceChange, |
498 | ) -> Result<lsp_ext::SourceChange> { | 490 | ) -> Result<lsp_ext::SourceChange> { |
499 | let cursor_position = match source_change.cursor_position { | ||
500 | None => None, | ||
501 | Some(pos) => { | ||
502 | let line_index = world.analysis().file_line_index(pos.file_id)?; | ||
503 | let edit = source_change | ||
504 | .source_file_edits | ||
505 | .iter() | ||
506 | .find(|it| it.file_id == pos.file_id) | ||
507 | .map(|it| &it.edit); | ||
508 | let line_col = match edit { | ||
509 | Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit), | ||
510 | None => line_index.line_col(pos.offset), | ||
511 | }; | ||
512 | let position = | ||
513 | lsp_types::Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); | ||
514 | Some(lsp_types::TextDocumentPositionParams { | ||
515 | text_document: text_document_identifier(world, pos.file_id)?, | ||
516 | position, | ||
517 | }) | ||
518 | } | ||
519 | }; | ||
520 | let label = source_change.label.clone(); | 491 | let label = source_change.label.clone(); |
521 | let workspace_edit = self::snippet_workspace_edit(world, source_change)?; | 492 | let workspace_edit = self::snippet_workspace_edit(world, source_change)?; |
522 | Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position }) | 493 | Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position: None }) |
523 | } | 494 | } |
524 | 495 | ||
525 | pub(crate) fn snippet_workspace_edit( | 496 | pub(crate) fn snippet_workspace_edit( |
@@ -639,25 +610,11 @@ fn main() <fold>{ | |||
639 | } | 610 | } |
640 | 611 | ||
641 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { | 612 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { |
642 | let res = if assist.source_change.cursor_position.is_none() { | 613 | let res = lsp_ext::CodeAction { |
643 | lsp_ext::CodeAction { | 614 | title: assist.label, |
644 | title: assist.label, | 615 | kind: Some(String::new()), |
645 | kind: Some(String::new()), | 616 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), |
646 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), | 617 | command: None, |
647 | command: None, | ||
648 | } | ||
649 | } else { | ||
650 | assert!(!assist.source_change.is_snippet); | ||
651 | let source_change = source_change(&world, assist.source_change)?; | ||
652 | let arg = serde_json::to_value(source_change)?; | ||
653 | let title = assist.label; | ||
654 | let command = lsp_types::Command { | ||
655 | title: title.clone(), | ||
656 | command: "rust-analyzer.applySourceChange".to_string(), | ||
657 | arguments: Some(vec![arg]), | ||
658 | }; | ||
659 | |||
660 | lsp_ext::CodeAction { title, kind: Some(String::new()), edit: None, command: Some(command) } | ||
661 | }; | 618 | }; |
662 | Ok(res) | 619 | Ok(res) |
663 | } | 620 | } |
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 74676b3ee..4e94c37e1 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -474,27 +474,21 @@ fn main() {{}} | |||
474 | position: Position { line: 0, character: 5 }, | 474 | position: Position { line: 0, character: 5 }, |
475 | }, | 475 | }, |
476 | json!({ | 476 | json!({ |
477 | "cursorPosition": { | 477 | "documentChanges": [ |
478 | "position": { "character": 4, "line": 1 }, | 478 | { |
479 | "textDocument": { "uri": "file:///[..]src/m0.rs" } | 479 | "edits": [ |
480 | }, | 480 | { |
481 | "label": "On enter", | 481 | "insertTextFormat": 2, |
482 | "workspaceEdit": { | 482 | "newText": "\n/// $0", |
483 | "documentChanges": [ | 483 | "range": { |
484 | { | 484 | "end": { "character": 5, "line": 0 }, |
485 | "edits": [ | 485 | "start": { "character": 5, "line": 0 } |
486 | { | ||
487 | "newText": "\n/// ", | ||
488 | "range": { | ||
489 | "end": { "character": 5, "line": 0 }, | ||
490 | "start": { "character": 5, "line": 0 } | ||
491 | } | ||
492 | } | 486 | } |
493 | ], | 487 | } |
494 | "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null } | 488 | ], |
495 | } | 489 | "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null } |
496 | ] | 490 | } |
497 | } | 491 | ] |
498 | }), | 492 | }), |
499 | ); | 493 | ); |
500 | let elapsed = start.elapsed(); | 494 | let elapsed = start.elapsed(); |
@@ -526,27 +520,21 @@ version = \"0.0.0\" | |||
526 | position: Position { line: 0, character: 8 }, | 520 | position: Position { line: 0, character: 8 }, |
527 | }, | 521 | }, |
528 | json!({ | 522 | json!({ |
529 | "cursorPosition": { | 523 | "documentChanges": [ |
530 | "position": { "line": 1, "character": 4 }, | 524 | { |
531 | "textDocument": { "uri": "file:///[..]src/main.rs" } | 525 | "edits": [ |
532 | }, | 526 | { |
533 | "label": "On enter", | 527 | "insertTextFormat": 2, |
534 | "workspaceEdit": { | 528 | "newText": "\r\n/// $0", |
535 | "documentChanges": [ | 529 | "range": { |
536 | { | 530 | "end": { "line": 0, "character": 8 }, |
537 | "edits": [ | 531 | "start": { "line": 0, "character": 8 } |
538 | { | ||
539 | "newText": "\r\n/// ", | ||
540 | "range": { | ||
541 | "end": { "line": 0, "character": 8 }, | ||
542 | "start": { "line": 0, "character": 8 } | ||
543 | } | ||
544 | } | 532 | } |
545 | ], | 533 | } |
546 | "textDocument": { "uri": "file:///[..]src/main.rs", "version": null } | 534 | ], |
547 | } | 535 | "textDocument": { "uri": "file:///[..]src/main.rs", "version": null } |
548 | ] | 536 | } |
549 | } | 537 | ] |
550 | }), | 538 | }), |
551 | ); | 539 | ); |
552 | } | 540 | } |