aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libeditor/src')
-rw-r--r--crates/libeditor/src/code_actions.rs33
-rw-r--r--crates/libeditor/src/edit.rs93
-rw-r--r--crates/libeditor/src/lib.rs7
3 files changed, 132 insertions, 1 deletions
diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs
new file mode 100644
index 000000000..7c9874588
--- /dev/null
+++ b/crates/libeditor/src/code_actions.rs
@@ -0,0 +1,33 @@
1use {TextUnit, File, EditBuilder, Edit};
2use libsyntax2::{
3 ast::AstNode,
4 SyntaxKind::COMMA,
5 SyntaxNodeRef,
6 algo::{
7 Direction, siblings,
8 find_leaf_at_offset,
9 },
10};
11
12pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> Edit + 'a> {
13 let syntax = file.syntax();
14 let syntax = syntax.as_ref();
15
16 let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?;
17 let left = non_trivia_sibling(comma, Direction::Backward)?;
18 let right = non_trivia_sibling(comma, Direction::Forward)?;
19 Some(move || {
20 let mut edit = EditBuilder::new();
21 edit.replace(left.range(), right.text());
22 edit.replace(right.range(), left.text());
23 edit.finish()
24 })
25}
26
27fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> {
28 siblings(node, direction)
29 .skip(1)
30 .find(|node| !node.kind().is_trivia())
31}
32
33
diff --git a/crates/libeditor/src/edit.rs b/crates/libeditor/src/edit.rs
new file mode 100644
index 000000000..163ecf6de
--- /dev/null
+++ b/crates/libeditor/src/edit.rs
@@ -0,0 +1,93 @@
1use {TextRange, TextUnit};
2
3#[derive(Debug)]
4pub struct Edit {
5 pub atoms: Vec<AtomEdit>,
6}
7
8#[derive(Debug)]
9pub struct AtomEdit {
10 pub delete: TextRange,
11 pub insert: String,
12}
13
14#[derive(Debug)]
15pub struct EditBuilder {
16 atoms: Vec<AtomEdit>
17}
18
19impl EditBuilder {
20 pub fn new() -> EditBuilder {
21 EditBuilder { atoms: Vec::new() }
22 }
23
24 pub fn replace(&mut self, range: TextRange, replacement: String) {
25 let range = self.translate(range);
26 self.atoms.push(AtomEdit { delete: range, insert: replacement })
27 }
28
29 pub fn delete(&mut self, range: TextRange) {
30 self.replace(range, String::new());
31 }
32
33 pub fn insert(&mut self, offset: TextUnit, text: String) {
34 self.replace(TextRange::offset_len(offset, 0.into()), text)
35 }
36
37 pub fn finish(self) -> Edit {
38 Edit { atoms: self.atoms }
39 }
40
41 fn translate(&self, range: TextRange) -> TextRange {
42 let mut range = range;
43 for atom in self.atoms.iter() {
44 range = atom.apply_to_range(range)
45 .expect("conflicting edits");
46 }
47 range
48 }
49}
50
51impl Edit {
52 pub fn apply(&self, text: &str) -> String {
53 let mut text = text.to_owned();
54 for atom in self.atoms.iter() {
55 text = atom.apply(&text);
56 }
57 text
58 }
59}
60
61impl AtomEdit {
62 fn apply(&self, text: &str) -> String {
63 let prefix = &text[
64 TextRange::from_to(0.into(), self.delete.start())
65 ];
66 let suffix = &text[
67 TextRange::from_to(self.delete.end(), TextUnit::of_str(text))
68 ];
69 let mut res = String::with_capacity(prefix.len() + self.insert.len() + suffix.len());
70 res.push_str(prefix);
71 res.push_str(&self.insert);
72 res.push_str(suffix);
73 res
74 }
75
76 fn apply_to_position(&self, pos: TextUnit) -> Option<TextUnit> {
77 if pos <= self.delete.start() {
78 return Some(pos);
79 }
80 if pos < self.delete.end() {
81 return None;
82 }
83 Some(pos - self.delete.len() + TextUnit::of_str(&self.insert))
84 }
85
86 fn apply_to_range(&self, range: TextRange) -> Option<TextRange> {
87 Some(TextRange::from_to(
88 self.apply_to_position(range.start())?,
89 self.apply_to_position(range.end())?,
90 ))
91 }
92}
93
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs
index 013d27450..103f32190 100644
--- a/crates/libeditor/src/lib.rs
+++ b/crates/libeditor/src/lib.rs
@@ -1,9 +1,12 @@
1extern crate libsyntax2; 1extern crate libsyntax2;
2extern crate superslice; 2extern crate superslice;
3extern crate itertools;
3 4
4mod extend_selection; 5mod extend_selection;
5mod symbols; 6mod symbols;
6mod line_index; 7mod line_index;
8mod edit;
9mod code_actions;
7 10
8use libsyntax2::{ 11use libsyntax2::{
9 ast::{self, NameOwner}, 12 ast::{self, NameOwner},
@@ -15,7 +18,9 @@ pub use libsyntax2::{File, TextRange, TextUnit};
15pub use self::{ 18pub use self::{
16 line_index::{LineIndex, LineCol}, 19 line_index::{LineIndex, LineCol},
17 extend_selection::extend_selection, 20 extend_selection::extend_selection,
18 symbols::{FileSymbol, file_symbols} 21 symbols::{FileSymbol, file_symbols},
22 edit::{EditBuilder, Edit},
23 code_actions::{flip_comma},
19}; 24};
20 25
21#[derive(Debug)] 26#[derive(Debug)]