Find with callback
This commit is contained in:
parent
b461804354
commit
dcd0c3bec1
102
src/editor.rs
102
src/editor.rs
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user