diff options
-rw-r--r-- | crates/libeditor/src/code_actions.rs | 29 | ||||
-rw-r--r-- | crates/libeditor/src/edit.rs | 15 | ||||
-rw-r--r-- | crates/libeditor/src/lib.rs | 9 | ||||
-rw-r--r-- | crates/libeditor/tests/test.rs | 59 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 10 |
5 files changed, 85 insertions, 37 deletions
diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs index 6e2c73f29..88c348436 100644 --- a/crates/libeditor/src/code_actions.rs +++ b/crates/libeditor/src/code_actions.rs | |||
@@ -10,7 +10,17 @@ use libsyntax2::{ | |||
10 | }, | 10 | }, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> Edit + 'a> { | 13 | pub struct ActionResult { |
14 | pub edit: Edit, | ||
15 | pub cursor_position: CursorPosition, | ||
16 | } | ||
17 | |||
18 | pub enum CursorPosition { | ||
19 | Same, | ||
20 | Offset(TextUnit), | ||
21 | } | ||
22 | |||
23 | pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> { | ||
14 | let syntax = file.syntax(); | 24 | let syntax = file.syntax(); |
15 | let syntax = syntax.as_ref(); | 25 | let syntax = syntax.as_ref(); |
16 | 26 | ||
@@ -21,18 +31,27 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() | |||
21 | let mut edit = EditBuilder::new(); | 31 | let mut edit = EditBuilder::new(); |
22 | edit.replace(left.range(), right.text()); | 32 | edit.replace(left.range(), right.text()); |
23 | edit.replace(right.range(), left.text()); | 33 | edit.replace(right.range(), left.text()); |
24 | edit.finish() | 34 | ActionResult { |
35 | edit: edit.finish(), | ||
36 | cursor_position: CursorPosition::Same, | ||
37 | } | ||
25 | }) | 38 | }) |
26 | } | 39 | } |
27 | 40 | ||
28 | pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> Edit + 'a> { | 41 | pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> { |
29 | let syntax = file.syntax(); | 42 | let syntax = file.syntax(); |
30 | let syntax = syntax.as_ref(); | 43 | let syntax = syntax.as_ref(); |
31 | let nominal = find_node::<ast::NominalDef<_>>(syntax, offset)?; | 44 | let nominal = find_node::<ast::NominalDef<_>>(syntax, offset)?; |
32 | Some(move || { | 45 | Some(move || { |
33 | let mut edit = EditBuilder::new(); | 46 | let mut edit = EditBuilder::new(); |
34 | edit.insert(nominal.syntax().range().start(), "#[derive()]\n".to_string()); | 47 | let node_start = nominal.syntax().range().start(); |
35 | edit.finish() | 48 | edit.insert(node_start, "#[derive()]\n".to_string()); |
49 | ActionResult { | ||
50 | edit: edit.finish(), | ||
51 | cursor_position: CursorPosition::Offset( | ||
52 | node_start + TextUnit::of_str("#[derive(") | ||
53 | ), | ||
54 | } | ||
36 | }) | 55 | }) |
37 | } | 56 | } |
38 | 57 | ||
diff --git a/crates/libeditor/src/edit.rs b/crates/libeditor/src/edit.rs index 15a2a904f..3edd0809d 100644 --- a/crates/libeditor/src/edit.rs +++ b/crates/libeditor/src/edit.rs | |||
@@ -67,6 +67,21 @@ impl Edit { | |||
67 | assert_eq!(buf.len(), total_len); | 67 | assert_eq!(buf.len(), total_len); |
68 | buf | 68 | buf |
69 | } | 69 | } |
70 | |||
71 | pub fn apply_to_offset(&self, offset: TextUnit) -> Option<TextUnit> { | ||
72 | let mut res = offset; | ||
73 | for atom in self.atoms.iter() { | ||
74 | if atom.delete.start() >= offset { | ||
75 | break; | ||
76 | } | ||
77 | if offset < atom.delete.end() { | ||
78 | return None | ||
79 | } | ||
80 | res += TextUnit::of_str(&atom.insert); | ||
81 | res -= atom.delete.len(); | ||
82 | } | ||
83 | Some(res) | ||
84 | } | ||
70 | } | 85 | } |
71 | 86 | ||
72 | impl AtomEdit { | 87 | impl AtomEdit { |
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index 2c46ca45f..9e44f5d92 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs | |||
@@ -21,7 +21,7 @@ pub use self::{ | |||
21 | extend_selection::extend_selection, | 21 | extend_selection::extend_selection, |
22 | symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, | 22 | symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, |
23 | edit::{EditBuilder, Edit, AtomEdit}, | 23 | edit::{EditBuilder, Edit, AtomEdit}, |
24 | code_actions::{flip_comma, add_derive}, | 24 | code_actions::{flip_comma, add_derive, ActionResult, CursorPosition}, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | #[derive(Debug)] | 27 | #[derive(Debug)] |
@@ -37,13 +37,6 @@ pub struct Diagnostic { | |||
37 | } | 37 | } |
38 | 38 | ||
39 | #[derive(Debug)] | 39 | #[derive(Debug)] |
40 | pub struct Symbol { | ||
41 | // pub parent: ???, | ||
42 | pub name: String, | ||
43 | pub range: TextRange, | ||
44 | } | ||
45 | |||
46 | #[derive(Debug)] | ||
47 | pub struct Runnable { | 40 | pub struct Runnable { |
48 | pub range: TextRange, | 41 | pub range: TextRange, |
49 | pub kind: RunnableKind, | 42 | pub kind: RunnableKind, |
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index dd2c99c3f..7063425ce 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs | |||
@@ -6,10 +6,10 @@ extern crate assert_eq_text; | |||
6 | 6 | ||
7 | use std::fmt; | 7 | use std::fmt; |
8 | use itertools::Itertools; | 8 | use itertools::Itertools; |
9 | use libsyntax2::AstNode; | ||
10 | use libeditor::{ | 9 | use libeditor::{ |
11 | File, TextUnit, TextRange, | 10 | File, TextUnit, TextRange, ActionResult, CursorPosition, |
12 | highlight, runnables, extend_selection, file_structure, flip_comma, | 11 | highlight, runnables, extend_selection, file_structure, |
12 | flip_comma, add_derive, | ||
13 | }; | 13 | }; |
14 | 14 | ||
15 | #[test] | 15 | #[test] |
@@ -103,13 +103,19 @@ impl fmt::Debug for E {} | |||
103 | 103 | ||
104 | #[test] | 104 | #[test] |
105 | fn test_swap_comma() { | 105 | fn test_swap_comma() { |
106 | check_modification( | 106 | check_action( |
107 | "fn foo(x: i32,<|> y: Result<(), ()>) {}", | 107 | "fn foo(x: i32,<|> y: Result<(), ()>) {}", |
108 | "fn foo(y: Result<(), ()>, x: i32) {}", | 108 | "fn foo(y: Result<(), ()>,<|> x: i32) {}", |
109 | &|file, offset| { | 109 | |file, off| flip_comma(file, off).map(|f| f()), |
110 | let edit = flip_comma(file, offset).unwrap()(); | 110 | ) |
111 | edit.apply(&file.syntax().text()) | 111 | } |
112 | }, | 112 | |
113 | #[test] | ||
114 | fn test_add_derive() { | ||
115 | check_action( | ||
116 | "struct Foo { a: i32, <|>}", | ||
117 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | ||
118 | |file, off| add_derive(file, off).map(|f| f()), | ||
113 | ) | 119 | ) |
114 | } | 120 | } |
115 | 121 | ||
@@ -123,21 +129,36 @@ fn dbg_eq(expected: &str, actual: &impl fmt::Debug) { | |||
123 | assert_eq!(expected, actual); | 129 | assert_eq!(expected, actual); |
124 | } | 130 | } |
125 | 131 | ||
126 | fn check_modification( | 132 | fn check_action<F: Fn(&File, TextUnit) -> Option<ActionResult>>( |
127 | before: &str, | 133 | before: &str, |
128 | after: &str, | 134 | after: &str, |
129 | f: &impl Fn(&File, TextUnit) -> String, | 135 | f: F, |
130 | ) { | 136 | ) { |
137 | let (before_cursor_pos, before) = extract_cursor(before); | ||
138 | let file = file(&before); | ||
139 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); | ||
140 | let actual = result.edit.apply(&before); | ||
141 | let actual_cursor_pos: u32 = match result.cursor_position { | ||
142 | CursorPosition::Same => result.edit.apply_to_offset(before_cursor_pos).unwrap(), | ||
143 | CursorPosition::Offset(off) => off, | ||
144 | }.into(); | ||
145 | let actual_cursor_pos = actual_cursor_pos as usize; | ||
146 | let mut actual_with_cursor = String::new(); | ||
147 | actual_with_cursor.push_str(&actual[..actual_cursor_pos]); | ||
148 | actual_with_cursor.push_str("<|>"); | ||
149 | actual_with_cursor.push_str(&actual[actual_cursor_pos..]); | ||
150 | assert_eq_text!(after, &actual_with_cursor); | ||
151 | } | ||
152 | |||
153 | fn extract_cursor(text: &str) -> (TextUnit, String) { | ||
131 | let cursor = "<|>"; | 154 | let cursor = "<|>"; |
132 | let cursor_pos = match before.find(cursor) { | 155 | let cursor_pos = match text.find(cursor) { |
133 | None => panic!("before text should contain cursor marker"), | 156 | None => panic!("text should contain cursor marker"), |
134 | Some(pos) => pos, | 157 | Some(pos) => pos, |
135 | }; | 158 | }; |
136 | let mut text = String::with_capacity(before.len() - cursor.len()); | 159 | let mut new_text = String::with_capacity(text.len() - cursor.len()); |
137 | text.push_str(&before[..cursor_pos]); | 160 | new_text.push_str(&text[..cursor_pos]); |
138 | text.push_str(&before[cursor_pos + cursor.len()..]); | 161 | new_text.push_str(&text[cursor_pos + cursor.len()..]); |
139 | let cursor_pos = TextUnit::from(cursor_pos as u32); | 162 | let cursor_pos = TextUnit::from(cursor_pos as u32); |
140 | let file = file(&text); | 163 | (cursor_pos, new_text) |
141 | let actual = f(&file, cursor_pos); | ||
142 | assert_eq_text!(after, &actual); | ||
143 | } | 164 | } |
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 637bf1b24..078abfbfa 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
@@ -187,12 +187,12 @@ pub fn handle_execute_command( | |||
187 | let arg: ActionRequest = from_value(arg)?; | 187 | let arg: ActionRequest = from_value(arg)?; |
188 | let file_id = arg.text_document.try_conv_with(&path_map)?; | 188 | let file_id = arg.text_document.try_conv_with(&path_map)?; |
189 | let file = world.file_syntax(file_id)?; | 189 | let file = world.file_syntax(file_id)?; |
190 | let edit = match arg.id { | 190 | let action_result = match arg.id { |
191 | ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|edit| edit()), | 191 | ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|f| f()), |
192 | ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|edit| edit()), | 192 | ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|f| f()), |
193 | }; | 193 | }; |
194 | let edit = match edit { | 194 | let edit = match action_result { |
195 | Some(edit) => edit, | 195 | Some(action_result) => action_result.edit, |
196 | None => bail!("command not applicable"), | 196 | None => bail!("command not applicable"), |
197 | }; | 197 | }; |
198 | let line_index = world.file_line_index(file_id)?; | 198 | let line_index = world.file_line_index(file_id)?; |