diff --git a/src/editor.rs b/src/editor.rs index b4a3da8..f8e9b1c 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -13,11 +13,19 @@ use self::EditorKey::*; const KILO_TAB_STOP: usize = 4; const KILO_QUIT_TIMES: u8 = 3; +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Highlight { + Normal, + Number, + SearchMatch, +} + /// A representation of a line in the editor #[derive(Debug, Default)] pub struct EditorRow { chars: String, render: String, + highlight: Vec, } impl EditorRow { @@ -137,7 +145,14 @@ impl Editor { let mut br = BufReader::with_capacity(5, stdin); let mut first_read = [0; 1]; - br.read_exact(&mut first_read); + match br.read_exact(&mut first_read) { + Ok(_) => (), + Err(e) => { + if e.kind() != io::ErrorKind::UnexpectedEof { + panic!(e); + } + } + } let first_str = String::from_utf8(first_read.to_vec()); if first_str.is_err() { return None; @@ -164,7 +179,14 @@ impl Editor { // Match escape sequence // -------------------------------------------------------------------- let mut seq = [0; 4]; - br.read_exact(&mut seq); + match br.read_exact(&mut seq) { + Ok(_) => (), + Err(e) => { + if e.kind() != io::ErrorKind::UnexpectedEof { + panic!(e); + } + } + } let seq_str = String::from_utf8(seq.to_vec()); // On error, just continue the input loop @@ -279,6 +301,31 @@ impl Editor { } } + // ------------------------------------------------------------------------ + // Syntax Highlighting + // ------------------------------------------------------------------------ + + fn update_syntax(&mut self, index: usize) { + let row = &mut self.rows[index]; + row.highlight = vec![Highlight::Normal; row.render.len()]; + + for (x, ch) in row.render.char_indices() { + if ch.is_ascii_digit() { + row.highlight[x] = Highlight::Number; + } + } + } + + fn syntax_to_color(&self, syntax_type: Highlight) -> i32 { + use Highlight::*; + + match syntax_type { + Normal => 37, + Number => 31, // Red + SearchMatch => 34, // Blue + } + } + // ------------------------------------------------------------------------ // Input // ------------------------------------------------------------------------ @@ -505,6 +552,10 @@ impl Editor { self.output_buffer.push_str(str); } + fn append_out_char(&mut self, ch: char) { + self.output_buffer.push(ch); + } + fn scroll(&mut self) { self.render_x = 0; if self.cursor_y < self.rows.len() { @@ -556,15 +607,27 @@ impl Editor { self.append_out("~"); } } else { - let mut len = self.rows[file_row].render.len() - self.col_offset; - if len > self.screen_cols { - len = self.screen_cols; - } - let output = self.rows[file_row].render.clone(); - // let mut output = self.rows[file_row].render.clone(); - // output.truncate(len); - self.append_out(&output[self.col_offset..len]); + let mut current_color: i32 = -1; + + for (x, ch) in output.char_indices() { + if self.rows[file_row].highlight[x] == Highlight::Normal { + if current_color != -1 { + self.append_out("\x1b[39m"); + current_color = -1; + } + self.append_out_char(ch); + } else { + let color = self.syntax_to_color(self.rows[file_row].highlight[x]); + if color != current_color { + current_color = color; + let code = format!("\x1b[{}m", color); + self.append_out(&code); + } + self.append_out_char(ch); + } + } + self.append_out("\x1b[39m"); } self.append_out("\x1b[K"); @@ -672,9 +735,8 @@ impl Editor { 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() { + for char in self.rows[index].chars.chars() { if char == '\t' { rx += (KILO_TAB_STOP - 1) - (rx % KILO_TAB_STOP); } else { @@ -694,9 +756,8 @@ impl Editor { 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() { + for char in self.rows[index].chars.chars() { if char == '\t' { current_rx += (KILO_TAB_STOP - 1) - (current_rx % KILO_TAB_STOP); } else { @@ -721,6 +782,8 @@ impl Editor { // Cheat at rendering tabs as spaces let str = str.replace('\t', " "); row.render = str; + + self.update_syntax(index); } fn insert_row(&mut self, at: usize, row: &str) { @@ -938,8 +1001,7 @@ impl Editor { current = 0; } - let row = &self.rows[current as usize]; - match row.render.find(query) { + match self.rows[current as usize].render.find(query) { None => (), Some(start) => { self.search_last_match = current; @@ -947,6 +1009,12 @@ impl Editor { self.cursor_x = self.row_rx_to_cx(x, start); self.row_offset = self.rows.len(); + // Highlight matching search result + let len = start + query.len(); + for x in start..len { + self.rows[current as usize].highlight[x] = Highlight::SearchMatch; + } + break; } } @@ -959,7 +1027,7 @@ impl Editor { 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)); + let query = self.prompt("Search (Use ESC/Arrows/Enter):", Some(&mut Self::find_callback)); if query.is_empty() { self.cursor_x = saved_cx;