Find with callback

This commit is contained in:
Timothy Warren 2019-09-03 16:19:19 -04:00
parent b461804354
commit dcd0c3bec1
2 changed files with 97 additions and 7 deletions

View File

@ -278,8 +278,15 @@ impl Editor {
// Input // Input
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
fn prompt(&mut self, prompt: &str) -> String { fn prompt(&mut self, prompt: &str, cb: Option<&mut dyn Fn(&mut Self, &str, EditorKey<char>)>) -> String {
let mut buffer = String::new(); let mut buffer = String::new();
let default_cb = &mut Self::_noop_prompt_cb;
let cb = if cb.is_some() {
cb.unwrap()
} else {
default_cb
};
loop { loop {
self.set_status_message(&format!("{} {}", prompt, buffer)); self.set_status_message(&format!("{} {}", prompt, buffer));
@ -297,26 +304,32 @@ impl Editor {
}, },
Escape => { Escape => {
self.set_status_message(""); self.set_status_message("");
cb(self, &String::from(""), char);
return String::from(""); return String::from("");
} }
Enter => { Enter => {
if buffer.len() != 0 { if buffer.len() != 0 {
self.set_status_message(""); self.set_status_message("");
cb(self, &buffer, char);
return buffer; return buffer;
} }
} }
OtherKey(ch) => { OtherKey(ch) => {
if (!ch.is_ascii_control()) && (ch as u8) < 128 { if (!ch.is_ascii_control()) && (ch as u8) < 128 {
buffer.push(ch); buffer.push(ch);
continue; // continue;
} }
} }
_ => (), _ => (),
}; };
cb(self, &buffer, char);
} }
} }
} }
fn _noop_prompt_cb(&mut self, _: &str, _: EditorKey<char>) {}
fn move_cursor(&mut self, key: &EditorKey<char>) { fn move_cursor(&mut self, key: &EditorKey<char>) {
let row = self.rows.get(self.cursor_y); let row = self.rows.get(self.cursor_y);
match key { match key {
@ -394,6 +407,10 @@ impl Editor {
Function(_) => (), Function(_) => (),
OtherKey(c) => { OtherKey(c) => {
if c.is_ascii_control() { if c.is_ascii_control() {
if c == ctrl_key('f') {
self.find();
}
if c == ctrl_key('q') { if c == ctrl_key('q') {
if self.dirty > 0 && self.quit_times > 0 { if self.dirty > 0 && self.quit_times > 0 {
self.set_status_message(&format!("WARNING!!! File has unsaved changes. Press Ctrl-Q {} more times to quit.", self.quit_times)); self.set_status_message(&format!("WARNING!!! File has unsaved changes. Press Ctrl-Q {} more times to quit.", self.quit_times));
@ -482,7 +499,7 @@ impl Editor {
fn scroll(&mut self) { fn scroll(&mut self) {
self.render_x = 0; self.render_x = 0;
if self.cursor_y < self.rows.len() { if self.cursor_y < self.rows.len() {
self.render_x = self.row_cx_to_rx(self.cursor_y); self.render_x = self.row_cx_to_rx(self.cursor_y, self.cursor_x);
} }
// Vertical scrolling // Vertical scrolling
@ -643,20 +660,50 @@ impl Editor {
// Row Operations // Row Operations
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
fn row_cx_to_rx(&mut self, index: usize) -> usize { fn row_cx_to_rx(&mut self, index: usize, cx: usize) -> usize {
let mut rx: usize = 0; let mut rx: usize = 0;
let mut i: usize = 0;
let row = &mut self.rows[index]; let row = &mut self.rows[index];
for char in row.chars.chars() { for char in row.chars.chars() {
if char == '\t' { if char == '\t' {
rx += (KILO_TAB_STOP - 1) - (rx % KILO_TAB_STOP); rx += (KILO_TAB_STOP - 1) - (rx % KILO_TAB_STOP);
} else {
rx += 1;
} }
rx += 1;
if i > cx {
return rx;
}
i += 1;
} }
rx rx
} }
fn row_rx_to_cx(&mut self, index: usize, rx: usize) -> usize {
let mut current_rx:usize = 0;
let mut cx: usize = 0;
let row = &mut self.rows[index];
for char in row.chars.chars() {
if char == '\t' {
current_rx += (KILO_TAB_STOP - 1) - (current_rx % KILO_TAB_STOP);
} else {
current_rx += 1;
}
if current_rx > rx {
return cx;
}
cx += 1;
}
cx
}
/// Convert tab characters to spaces for display /// Convert tab characters to spaces for display
fn update_row(&mut self, index: usize) { fn update_row(&mut self, index: usize) {
let row = &mut self.rows[index]; let row = &mut self.rows[index];
@ -820,7 +867,7 @@ impl Editor {
fn save(&mut self) -> io::Result<()> { fn save(&mut self) -> io::Result<()> {
if self.filename.len() == 0 { if self.filename.len() == 0 {
self.filename = self.prompt("Save as (ESC to cancel):"); self.filename = self.prompt("Save as (ESC to cancel):", None);
if self.filename.len() == 0 { if self.filename.len() == 0 {
self.set_status_message("Save aborted"); self.set_status_message("Save aborted");
return Ok(()) return Ok(())
@ -845,4 +892,47 @@ impl Editor {
Ok(()) Ok(())
} }
// ------------------------------------------------------------------------
// Find
// ------------------------------------------------------------------------
fn find_callback(&mut self, query: &str, key: EditorKey<char>) {
if key == Enter || key == Escape {
return;
}
if query.is_empty() {
return;
}
for x in 0..self.rows.len() {
match self.rows[x].render.find(query) {
None => (),
Some(start) => {
self.cursor_y = x;
self.cursor_x = self.row_rx_to_cx(x, start);
self.row_offset = self.rows.len();
break;
}
}
}
}
fn find(&mut self) {
let saved_cx = self.cursor_x;
let saved_cy = self.cursor_y;
let saved_coloff = self.col_offset;
let saved_rowoff = self.row_offset;
let query = self.prompt("Search (ESC to cancel):", Some(&mut Self::find_callback));
if query.is_empty() {
self.cursor_x = saved_cx;
self.cursor_y = saved_cy;
self.col_offset = saved_coloff;
self.row_offset = saved_rowoff;
}
}
} }

View File

@ -25,7 +25,7 @@ fn main() -> Result<(), Error> {
editor.open(&args[1])?; editor.open(&args[1])?;
} }
editor.set_status_message("HELP: Ctrl-S = save | Ctrl-Q = quit"); editor.set_status_message("HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find");
// Main input loop. Editor::process_keypress uses an Option Enum as a sentinel. // Main input loop. Editor::process_keypress uses an Option Enum as a sentinel.
// `None` is returned on a quit action, in other cases, `Some(())` is returned, // `None` is returned on a quit action, in other cases, `Some(())` is returned,