From 66be735aa98c32fb062d1c756fa9303ff2d13002 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 12 Aug 2018 18:50:16 +0300 Subject: flip comma --- crates/libeditor/src/code_actions.rs | 33 +++++++++++++ crates/libeditor/src/edit.rs | 93 ++++++++++++++++++++++++++++++++++++ crates/libeditor/src/lib.rs | 7 ++- 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 crates/libeditor/src/code_actions.rs create mode 100644 crates/libeditor/src/edit.rs (limited to 'crates/libeditor/src') 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 @@ +use {TextUnit, File, EditBuilder, Edit}; +use libsyntax2::{ + ast::AstNode, + SyntaxKind::COMMA, + SyntaxNodeRef, + algo::{ + Direction, siblings, + find_leaf_at_offset, + }, +}; + +pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option Edit + 'a> { + let syntax = file.syntax(); + let syntax = syntax.as_ref(); + + let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?; + let left = non_trivia_sibling(comma, Direction::Backward)?; + let right = non_trivia_sibling(comma, Direction::Forward)?; + Some(move || { + let mut edit = EditBuilder::new(); + edit.replace(left.range(), right.text()); + edit.replace(right.range(), left.text()); + edit.finish() + }) +} + +fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option { + siblings(node, direction) + .skip(1) + .find(|node| !node.kind().is_trivia()) +} + + 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 @@ +use {TextRange, TextUnit}; + +#[derive(Debug)] +pub struct Edit { + pub atoms: Vec, +} + +#[derive(Debug)] +pub struct AtomEdit { + pub delete: TextRange, + pub insert: String, +} + +#[derive(Debug)] +pub struct EditBuilder { + atoms: Vec +} + +impl EditBuilder { + pub fn new() -> EditBuilder { + EditBuilder { atoms: Vec::new() } + } + + pub fn replace(&mut self, range: TextRange, replacement: String) { + let range = self.translate(range); + self.atoms.push(AtomEdit { delete: range, insert: replacement }) + } + + pub fn delete(&mut self, range: TextRange) { + self.replace(range, String::new()); + } + + pub fn insert(&mut self, offset: TextUnit, text: String) { + self.replace(TextRange::offset_len(offset, 0.into()), text) + } + + pub fn finish(self) -> Edit { + Edit { atoms: self.atoms } + } + + fn translate(&self, range: TextRange) -> TextRange { + let mut range = range; + for atom in self.atoms.iter() { + range = atom.apply_to_range(range) + .expect("conflicting edits"); + } + range + } +} + +impl Edit { + pub fn apply(&self, text: &str) -> String { + let mut text = text.to_owned(); + for atom in self.atoms.iter() { + text = atom.apply(&text); + } + text + } +} + +impl AtomEdit { + fn apply(&self, text: &str) -> String { + let prefix = &text[ + TextRange::from_to(0.into(), self.delete.start()) + ]; + let suffix = &text[ + TextRange::from_to(self.delete.end(), TextUnit::of_str(text)) + ]; + let mut res = String::with_capacity(prefix.len() + self.insert.len() + suffix.len()); + res.push_str(prefix); + res.push_str(&self.insert); + res.push_str(suffix); + res + } + + fn apply_to_position(&self, pos: TextUnit) -> Option { + if pos <= self.delete.start() { + return Some(pos); + } + if pos < self.delete.end() { + return None; + } + Some(pos - self.delete.len() + TextUnit::of_str(&self.insert)) + } + + fn apply_to_range(&self, range: TextRange) -> Option { + Some(TextRange::from_to( + self.apply_to_position(range.start())?, + self.apply_to_position(range.end())?, + )) + } +} + 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 @@ extern crate libsyntax2; extern crate superslice; +extern crate itertools; mod extend_selection; mod symbols; mod line_index; +mod edit; +mod code_actions; use libsyntax2::{ ast::{self, NameOwner}, @@ -15,7 +18,9 @@ pub use libsyntax2::{File, TextRange, TextUnit}; pub use self::{ line_index::{LineIndex, LineCol}, extend_selection::extend_selection, - symbols::{FileSymbol, file_symbols} + symbols::{FileSymbol, file_symbols}, + edit::{EditBuilder, Edit}, + code_actions::{flip_comma}, }; #[derive(Debug)] -- cgit v1.2.3