diff --git a/src/editor.rs b/src/editor.rs index e245b46..e326274 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -278,8 +278,15 @@ impl Editor { // Input // ------------------------------------------------------------------------ - fn prompt(&mut self, prompt: &str) -> String { + fn prompt(&mut self, prompt: &str, cb: Option<&mut dyn Fn(&mut Self, &str, EditorKey)>) -> String { 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 { self.set_status_message(&format!("{} {}", prompt, buffer)); @@ -297,26 +304,32 @@ impl Editor { }, Escape => { self.set_status_message(""); + cb(self, &String::from(""), char); return String::from(""); } Enter => { if buffer.len() != 0 { self.set_status_message(""); + cb(self, &buffer, char); return buffer; } } OtherKey(ch) => { if (!ch.is_ascii_control()) && (ch as u8) < 128 { buffer.push(ch); - continue; + // continue; } } _ => (), }; + + cb(self, &buffer, char); } } } + fn _noop_prompt_cb(&mut self, _: &str, _: EditorKey) {} + fn move_cursor(&mut self, key: &EditorKey) { let row = self.rows.get(self.cursor_y); match key { @@ -394,6 +407,10 @@ impl Editor { Function(_) => (), OtherKey(c) => { if c.is_ascii_control() { + if c == ctrl_key('f') { + self.find(); + } + if c == ctrl_key('q') { 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)); @@ -482,7 +499,7 @@ impl Editor { fn scroll(&mut self) { self.render_x = 0; 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 @@ -643,20 +660,50 @@ impl Editor { // 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 i: usize = 0; let row = &mut self.rows[index]; for char in row.chars.chars() { if char == '\t' { rx += (KILO_TAB_STOP - 1) - (rx % KILO_TAB_STOP); + } else { + rx += 1; } - rx += 1; + + if i > cx { + return rx; + } + + i += 1; } 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 fn update_row(&mut self, index: usize) { let row = &mut self.rows[index]; @@ -820,7 +867,7 @@ impl Editor { fn save(&mut self) -> io::Result<()> { 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 { self.set_status_message("Save aborted"); return Ok(()) @@ -845,4 +892,47 @@ impl Editor { Ok(()) } + + // ------------------------------------------------------------------------ + // Find + // ------------------------------------------------------------------------ + + fn find_callback(&mut self, query: &str, key: EditorKey) { + 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; + } + } } diff --git a/src/main.rs b/src/main.rs index 14a52df..7d342e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ fn main() -> Result<(), Error> { 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. // `None` is returned on a quit action, in other cases, `Some(())` is returned,