ViEditor: implement search, capture commands
This commit is contained in:
parent
7526fa9726
commit
c1e40363ab
1 changed files with 112 additions and 17 deletions
129
src/edit/vi.rs
129
src/edit/vi.rs
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
SyntaxEditor,
|
SyntaxEditor,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum ViMode {
|
pub enum ViMode {
|
||||||
/// Passthrough mode, disables vi features
|
/// Passthrough mode, disables vi features
|
||||||
Passthrough,
|
Passthrough,
|
||||||
|
|
@ -16,15 +16,16 @@ pub enum ViMode {
|
||||||
/// Insert mode
|
/// Insert mode
|
||||||
Insert,
|
Insert,
|
||||||
/// Command mode
|
/// Command mode
|
||||||
Command,
|
Command { value: String },
|
||||||
/// Search mode
|
/// Search mode
|
||||||
Search { forwards: bool },
|
Search { value: String, forwards: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ViEditor<'a> {
|
pub struct ViEditor<'a> {
|
||||||
editor: SyntaxEditor<'a>,
|
editor: SyntaxEditor<'a>,
|
||||||
mode: ViMode,
|
mode: ViMode,
|
||||||
|
search_opt: Option<(String, bool)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ViEditor<'a> {
|
impl<'a> ViEditor<'a> {
|
||||||
|
|
@ -32,6 +33,7 @@ impl<'a> ViEditor<'a> {
|
||||||
Self {
|
Self {
|
||||||
editor,
|
editor,
|
||||||
mode: ViMode::Normal,
|
mode: ViMode::Normal,
|
||||||
|
search_opt: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,8 +68,66 @@ impl<'a> ViEditor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current vi editing mode
|
/// Get current vi editing mode
|
||||||
pub fn mode(&self) -> ViMode {
|
pub fn mode(&self) -> &ViMode {
|
||||||
self.mode
|
&self.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search(&mut self, inverted: bool) {
|
||||||
|
let (search, mut forwards) = match &self.search_opt {
|
||||||
|
Some(some) => some,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if inverted {
|
||||||
|
forwards = !forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cursor = self.cursor();
|
||||||
|
let start_line = cursor.line;
|
||||||
|
if forwards {
|
||||||
|
while cursor.line < self.buffer().lines.len() {
|
||||||
|
if let Some(index) = self.buffer().lines[cursor.line]
|
||||||
|
.text()
|
||||||
|
.match_indices(search.as_str())
|
||||||
|
.filter_map(|(i, _)| {
|
||||||
|
if cursor.line != start_line || i > cursor.index {
|
||||||
|
Some(i)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
{
|
||||||
|
cursor.index = index;
|
||||||
|
self.set_cursor(cursor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.line += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cursor.line += 1;
|
||||||
|
while cursor.line > 0 {
|
||||||
|
cursor.line -= 1;
|
||||||
|
|
||||||
|
if let Some(index) = self.buffer().lines[cursor.line]
|
||||||
|
.text()
|
||||||
|
.rmatch_indices(search.as_str())
|
||||||
|
.filter_map(|(i, _)| {
|
||||||
|
if cursor.line != start_line || i < cursor.index {
|
||||||
|
Some(i)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
{
|
||||||
|
cursor.index = index;
|
||||||
|
self.set_cursor(cursor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,7 +173,7 @@ impl<'a> Edit for ViEditor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(&mut self, font_system: &mut FontSystem, action: Action) {
|
fn action(&mut self, font_system: &mut FontSystem, action: Action) {
|
||||||
let old_mode = self.mode;
|
let old_mode = self.mode.clone();
|
||||||
|
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViMode::Passthrough => self.editor.action(font_system, action),
|
ViMode::Passthrough => self.editor.action(font_system, action),
|
||||||
|
|
@ -203,6 +263,10 @@ impl<'a> Edit for ViEditor<'a> {
|
||||||
//TODO: 'L' => self.editor.action(Action::ScreenLow),
|
//TODO: 'L' => self.editor.action(Action::ScreenLow),
|
||||||
// Middle of screen
|
// Middle of screen
|
||||||
//TODO: 'M' => self.editor.action(Action::ScreenMiddle),
|
//TODO: 'M' => self.editor.action(Action::ScreenMiddle),
|
||||||
|
// Next search item
|
||||||
|
'n' => self.search(false),
|
||||||
|
// Previous search item
|
||||||
|
'N' => self.search(true),
|
||||||
// Enter visual mode
|
// Enter visual mode
|
||||||
'v' => {
|
'v' => {
|
||||||
if self.editor.select_opt().is_some() {
|
if self.editor.select_opt().is_some() {
|
||||||
|
|
@ -244,15 +308,23 @@ impl<'a> Edit for ViEditor<'a> {
|
||||||
'^' => self.editor.action(font_system, Action::SoftHome),
|
'^' => self.editor.action(font_system, Action::SoftHome),
|
||||||
// Enter command mode
|
// Enter command mode
|
||||||
':' => {
|
':' => {
|
||||||
self.mode = ViMode::Command;
|
self.mode = ViMode::Command {
|
||||||
|
value: String::new(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// Enter search mode
|
// Enter search mode
|
||||||
'/' => {
|
'/' => {
|
||||||
self.mode = ViMode::Search { forwards: true };
|
self.mode = ViMode::Search {
|
||||||
|
value: String::new(),
|
||||||
|
forwards: true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// Enter search backwards mode
|
// Enter search backwards mode
|
||||||
'?' => {
|
'?' => {
|
||||||
self.mode = ViMode::Search { forwards: false };
|
self.mode = ViMode::Search {
|
||||||
|
value: String::new(),
|
||||||
|
forwards: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
|
|
@ -278,24 +350,47 @@ impl<'a> Edit for ViEditor<'a> {
|
||||||
}
|
}
|
||||||
_ => self.editor.action(font_system, action),
|
_ => self.editor.action(font_system, action),
|
||||||
},
|
},
|
||||||
ViMode::Command => match action {
|
ViMode::Command { ref mut value } => match action {
|
||||||
Action::Escape => {
|
Action::Escape => {
|
||||||
self.mode = ViMode::Normal;
|
self.mode = ViMode::Normal;
|
||||||
}
|
}
|
||||||
Action::Enter => {}
|
|
||||||
Action::Insert(c) => match c {
|
Action::Insert(c) => match c {
|
||||||
_ => {}
|
_ => {
|
||||||
|
value.push(c);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
Action::Enter => {
|
||||||
|
//TODO: run command
|
||||||
|
self.mode = ViMode::Normal;
|
||||||
|
}
|
||||||
|
Action::Backspace => {
|
||||||
|
if value.pop().is_none() {
|
||||||
|
self.mode = ViMode::Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => self.editor.action(font_system, action),
|
_ => self.editor.action(font_system, action),
|
||||||
},
|
},
|
||||||
ViMode::Search { forwards } => match action {
|
ViMode::Search {
|
||||||
|
ref mut value,
|
||||||
|
forwards,
|
||||||
|
} => match action {
|
||||||
Action::Escape => {
|
Action::Escape => {
|
||||||
self.mode = ViMode::Normal;
|
self.mode = ViMode::Normal;
|
||||||
}
|
}
|
||||||
Action::Enter => {}
|
Action::Insert(c) => {
|
||||||
Action::Insert(c) => match c {
|
value.push(c);
|
||||||
_ => {}
|
}
|
||||||
},
|
Action::Enter => {
|
||||||
|
//TODO: do not require clone?
|
||||||
|
self.search_opt = Some((value.clone(), forwards));
|
||||||
|
self.mode = ViMode::Normal;
|
||||||
|
self.search(false);
|
||||||
|
}
|
||||||
|
Action::Backspace => {
|
||||||
|
if value.pop().is_none() {
|
||||||
|
self.mode = ViMode::Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => self.editor.action(font_system, action),
|
_ => self.editor.action(font_system, action),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue