diff --git a/src/nvim.rs b/src/nvim.rs index 0305d88..b15e9de 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -7,35 +7,35 @@ use shell::Shell; use glib; pub trait RedrawEvents { - fn on_cursor_goto(&mut self, row: u64, col: u64); + fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode; - fn on_put(&mut self, text: &str); + fn on_put(&mut self, text: &str) -> RepaintMode; - fn on_clear(&mut self); + fn on_clear(&mut self) -> RepaintMode; - fn on_resize(&mut self, columns: u64, rows: u64); + fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode; fn on_redraw(&self, mode: &RepaintMode); - fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>); + fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>) -> RepaintMode; - fn on_eol_clear(&mut self); + fn on_eol_clear(&mut self) -> RepaintMode; - fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64); + fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) -> RepaintMode; - fn on_scroll(&mut self, count: i64); + fn on_scroll(&mut self, count: i64) -> RepaintMode; - fn on_update_bg(&mut self, bg: i64); + fn on_update_bg(&mut self, bg: i64) -> RepaintMode; - fn on_update_fg(&mut self, fg: i64); + fn on_update_fg(&mut self, fg: i64) -> RepaintMode; - fn on_update_sp(&mut self, sp: i64); + fn on_update_sp(&mut self, sp: i64) -> RepaintMode; - fn on_mode_change(&mut self, mode: &str); + fn on_mode_change(&mut self, mode: &str) -> RepaintMode; - fn on_mouse_on(&mut self); + fn on_mouse_on(&mut self) -> RepaintMode; - fn on_mouse_off(&mut self); + fn on_mouse_off(&mut self) -> RepaintMode; } pub trait GuiApi { @@ -96,6 +96,8 @@ fn nvim_cb(method: &str, params: Vec) { match method { "redraw" => { safe_call(move |ui| { + let mut repaint_mode = RepaintMode::Nothing; + for ev in ¶ms { if let &Value::Array(ref ev_args) = ev { if let Value::String(ref ev_name) = ev_args[0] { @@ -104,7 +106,8 @@ fn nvim_cb(method: &str, params: Vec) { &Value::Array(ref ar) => ar.clone(), _ => vec![], }; - call(ui, ev_name, &args)?; + let call_reapint_mode = call(ui, ev_name, &args)?; + repaint_mode = repaint_mode.join(&call_reapint_mode); } } else { println!("Unsupported event {:?}", ev_args); @@ -114,7 +117,7 @@ fn nvim_cb(method: &str, params: Vec) { } } - ui.on_redraw(&RepaintMode::All); + ui.on_redraw(&repaint_mode); Ok(()) }); } @@ -148,8 +151,8 @@ fn call_gui_event(ui: &mut Shell, method: &str, args: &Vec) -> result::Re Ok(()) } -fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result<(), String> { - match method { +fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result { + Ok(match method { "cursor_goto" => ui.on_cursor_goto(try_uint!(args[0]), try_uint!(args[1])), "put" => ui.on_put(try_str!(args[0])), "clear" => ui.on_clear(), @@ -160,6 +163,7 @@ fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result<(), S } else { panic!("Supports only map value as argument"); } + RepaintMode::Nothing } "eol_clear" => ui.on_eol_clear(), "set_scroll_region" => { @@ -167,6 +171,7 @@ fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result<(), S try_uint!(args[1]), try_uint!(args[2]), try_uint!(args[3])); + RepaintMode::Nothing } "scroll" => ui.on_scroll(try_int!(args[0])), "update_bg" => ui.on_update_bg(try_int!(args[0])), @@ -175,10 +180,11 @@ fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result<(), S "mode_change" => ui.on_mode_change(try_str!(args[0])), "mouse_on" => ui.on_mouse_on(), "mouse_off" => ui.on_mouse_off(), - _ => println!("Event {}({:?})", method, args), - }; - - Ok(()) + _ => { + println!("Event {}({:?})", method, args); + RepaintMode::Nothing + }, + }) } fn safe_call(cb: F) @@ -205,8 +211,25 @@ impl ErrorReport for result::Result { } } +#[derive(Clone)] pub enum RepaintMode { + Nothing, All, Area(ModelRect), } +impl RepaintMode { + pub fn join(&self, mode: &RepaintMode) -> RepaintMode { + match (self, mode) { + (&RepaintMode::Nothing, m) => m.clone(), + (m, &RepaintMode::Nothing) => m.clone(), + (&RepaintMode::All, _) => RepaintMode::All, + (_, &RepaintMode::All) => RepaintMode::All, + (&RepaintMode::Area(ref mr1), &RepaintMode::Area(ref mr2)) => { + let mut area = mr1.clone(); + area.join(mr2); + RepaintMode::Area(area) + } + } + } +} diff --git a/src/shell.rs b/src/shell.rs index eea40c3..5fadf04 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -459,24 +459,26 @@ fn gtk_configure_event(_: &DrawingArea, ev: &EventConfigure) -> bool { } impl RedrawEvents for Shell { - fn on_cursor_goto(&mut self, row: u64, col: u64) { - self.model.set_cursor(row, col); + fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode { + RepaintMode::Area(self.model.set_cursor(row, col)) } - fn on_put(&mut self, text: &str) { - self.model.put(text, self.cur_attrs.as_ref()); + fn on_put(&mut self, text: &str) -> RepaintMode { + RepaintMode::Area(self.model.put(text, self.cur_attrs.as_ref())) } - fn on_clear(&mut self) { + fn on_clear(&mut self) -> RepaintMode { self.model.clear(); + RepaintMode::All } - fn on_eol_clear(&mut self) { - self.model.eol_clear(); + fn on_eol_clear(&mut self) -> RepaintMode { + RepaintMode::Area(self.model.eol_clear()) } - fn on_resize(&mut self, columns: u64, rows: u64) { + fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode { self.model = UiModel::new(rows, columns); + RepaintMode::All } fn on_redraw(&self, mode: &RepaintMode) { @@ -492,18 +494,20 @@ impl RedrawEvents for Shell { } }); }, + &RepaintMode::Nothing => (), } } - fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) { + fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) -> RepaintMode { self.model.set_scroll_region(top, bot, left, right); + RepaintMode::Nothing } - fn on_scroll(&mut self, count: i64) { - self.model.scroll(count as usize); + fn on_scroll(&mut self, count: i64) -> RepaintMode { + RepaintMode::Area(self.model.scroll(count as usize)) } - fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>) { + fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>) -> RepaintMode { let mut model_attrs = Attrs::new(); for &(ref key_val, ref val) in attrs { @@ -537,46 +541,54 @@ impl RedrawEvents for Shell { } self.cur_attrs = Some(model_attrs); + RepaintMode::Nothing } - fn on_update_bg(&mut self, bg: i64) { + fn on_update_bg(&mut self, bg: i64) -> RepaintMode { if bg >= 0 { self.bg_color = split_color(bg as u64); } else { self.bg_color = COLOR_BLACK; } + RepaintMode::Nothing } - fn on_update_fg(&mut self, fg: i64) { + fn on_update_fg(&mut self, fg: i64) -> RepaintMode { if fg >= 0 { self.fg_color = split_color(fg as u64); } else { self.fg_color = COLOR_WHITE; } + RepaintMode::Nothing } - fn on_update_sp(&mut self, sp: i64) { + fn on_update_sp(&mut self, sp: i64) -> RepaintMode { if sp >= 0 { self.sp_color = split_color(sp as u64); } else { self.sp_color = COLOR_RED; } + RepaintMode::Nothing } - fn on_mode_change(&mut self, mode: &str) { + fn on_mode_change(&mut self, mode: &str) -> RepaintMode { match mode { "normal" => self.mode = NvimMode::Normal, "insert" => self.mode = NvimMode::Insert, _ => self.mode = NvimMode::Other, } + + RepaintMode::Area(self.model.cur_point()) } - fn on_mouse_on(&mut self) { + fn on_mouse_on(&mut self) -> RepaintMode { self.mouse_enabled = true; + RepaintMode::Nothing } - fn on_mouse_off(&mut self) { + fn on_mouse_off(&mut self) -> RepaintMode { self.mouse_enabled = false; + RepaintMode::Nothing } } diff --git a/src/ui_model.rs b/src/ui_model.rs index d77a53d..b47b5e1 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -120,13 +120,17 @@ impl UiModel { &self.model } + pub fn cur_point(&self) -> ModelRect { + ModelRect::point(self.cur_row, self.cur_col) + } + pub fn set_cursor(&mut self, row: u64, col: u64) -> ModelRect { - let mut changed_region = ModelRect::point(self.cur_row, self.cur_col); + let mut changed_region = self.cur_point(); self.cur_row = row as usize; self.cur_col = col as usize; - changed_region.join(&ModelRect::point(self.cur_row, self.cur_col)); + changed_region.join(&self.cur_point()); changed_region @@ -137,8 +141,8 @@ impl UiModel { } pub fn put(&mut self, text: &str, attrs: Option<&Attrs>) -> ModelRect { + let changed_region = self.cur_point(); let mut cell = &mut self.model[self.cur_row][self.cur_col]; - let changed_region = ModelRect::point(self.cur_row, self.cur_col); cell.ch = text.chars().last().unwrap_or(' '); cell.attrs = attrs.map(Attrs::clone).unwrap_or_else(|| Attrs::new()); @@ -183,21 +187,19 @@ impl UiModel { self.clear_region(top, (top - count - 1), left, right); } - ModelRect::new(top, bot, left ,right) + ModelRect::new(top, bot, left, right) } - pub fn clear(&mut self) -> ModelRect { + pub fn clear(&mut self) { let (rows, columns) = (self.rows, self.columns); self.clear_region(0, rows - 1, 0, columns - 1); - - ModelRect::new(0, 0, rows -1, columns -1) } pub fn eol_clear(&mut self) -> ModelRect { let (cur_row, cur_col, columns) = (self.cur_row, self.cur_col, self.columns); self.clear_region(cur_row, cur_row, cur_col, columns - 1); - ModelRect::new(cur_row, cur_col, cur_col, columns -1) + ModelRect::new(cur_row, cur_col, cur_col, columns - 1) } fn clear_region(&mut self, top: usize, bot: usize, left: usize, right: usize) { @@ -209,6 +211,7 @@ impl UiModel { } } +#[derive(Clone)] pub struct ModelRect { top: usize, bot: usize, @@ -260,8 +263,81 @@ impl ModelRect { } pub fn to_area(&self, line_height: i32, char_width: i32) -> (i32, i32, i32, i32) { - let y = self.top as i32 * line_height; - let x = self.left as i32 * char_width; - (x, y, self.right as i32 * char_width - x, self.bot as i32 * line_height - y) + (self.left as i32 * char_width, + self.top as i32 * line_height, + (self.right - self.left + 1) as i32 * char_width, + (self.bot - self.top + 1) as i32 * line_height) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cursor_area() { + let mut model = UiModel::new(10, 20); + + model.set_cursor(1, 1); + + let rect = model.set_cursor(5, 5); + + assert_eq!(1, rect.top); + assert_eq!(1, rect.left); + assert_eq!(5, rect.bot); + assert_eq!(5, rect.right); + } + + #[test] + fn test_eol_clear_area() { + let mut model = UiModel::new(10, 20); + + model.set_cursor(1, 1); + + let rect = model.eol_clear(); + + assert_eq!(1, rect.top); + assert_eq!(1, rect.left); + assert_eq!(1, rect.bot); + assert_eq!(19, rect.right); + } + + #[test] + fn test_repaint_rect() { + let rect = ModelRect::point(1, 1); + let (x, y, width, height) = rect.to_area(10, 5); + + assert_eq!(5, x); + assert_eq!(10, y); + assert_eq!(5, width); + assert_eq!(10, height); + } + + #[test] + fn test_put_area() { + let mut model = UiModel::new(10, 20); + + model.set_cursor(1, 1); + + let rect = model.put(" ", None); + + assert_eq!(1, rect.top); + assert_eq!(1, rect.left); + assert_eq!(1, rect.bot); + assert_eq!(1, rect.right); + } + + #[test] + fn test_scroll_area() { + let mut model = UiModel::new(10, 20); + + model.set_scroll_region(1, 5, 1, 5); + + let rect = model.scroll(3); + + assert_eq!(1, rect.top); + assert_eq!(1, rect.left); + assert_eq!(5, rect.bot); + assert_eq!(5, rect.right); } }