From a22594ac808df671396215f7b795162f2543a5fc Mon Sep 17 00:00:00 2001 From: daa84 Date: Thu, 6 Jul 2017 17:50:55 +0300 Subject: [PATCH 1/5] Starting point for mode_info_set processing --- src/nvim.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/shell.rs | 8 +++++ 2 files changed, 93 insertions(+), 7 deletions(-) diff --git a/src/nvim.rs b/src/nvim.rs index f2af5c4..eb3febb 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -5,6 +5,7 @@ use std::process::{Stdio, Command}; use std::result; use std::sync::Arc; use std::ops::{Deref, DerefMut}; +use std::collections::HashMap; use neovim_lib::{Handler, Neovim, NeovimApi, Session, Value, UiAttachOptions, CallError, UiOption}; use neovim_lib::neovim_api::Tabpage; @@ -60,6 +61,11 @@ pub trait RedrawEvents { selected: Tabpage, tabs: Vec<(Tabpage, Option<&str>)>) -> RepaintMode; + + fn mode_info_set(&mut self, + cursor_style_enabled: bool, + mode_info: Vec) + -> RepaintMode; } pub trait GuiApi { @@ -78,6 +84,61 @@ macro_rules! try_uint { ($exp:expr) => ($exp.as_u64().ok_or("Can't convert argument to u64".to_owned())?) } +macro_rules! try_bool { + ($exp:expr) => ($exp.as_bool().ok_or("Can't convert argument to bool".to_owned())?) +} + +pub enum CursorShape { + Block, + Horizontal, + Vertical, + Unknown, +} + +impl CursorShape { + fn new(shape_code: &Value) -> Result { + let str_code = shape_code + .as_str() + .ok_or("Can't convert cursor shape to string".to_owned())?; + + Ok(match str_code { + "block" => CursorShape::Block, + "horizontal" => CursorShape::Horizontal, + "vertical" => CursorShape::Vertical, + _ => { + error!("Unknown cursor_shape {}", str_code); + CursorShape::Unknown + } + }) + } +} + +pub struct ModeInfo { + cursor_shape: Option, +} + +impl ModeInfo { + pub fn new(mode_info_arr: &Vec<(Value, Value)>) -> Result { + let mode_info_map = mode_info_arr + .iter() + .map(|p| { + p.0 + .as_str() + .ok_or("mode_info key not string".to_owned()) + .map(|key| (key, p.1.clone())) + }) + .collect::, String>>()?; + + let cursor_shape = if let Some(shape) = mode_info_map.get("cursor_shape") { + Some(CursorShape::new(shape)?) + } else { + None + }; + + Ok(ModeInfo { cursor_shape }) + } +} + #[derive(Debug)] pub struct NvimInitError { source: Box, @@ -104,7 +165,7 @@ impl NvimInitError { } impl fmt::Display for NvimInitError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.source) } } @@ -352,6 +413,21 @@ fn call(ui: &mut shell::State, } ui.tabline_update(Tabpage::new(args[0].clone()), tabs_out) } + "mode_info_set" => { + let mode_info_array = args[1] + .as_array() + .ok_or("Erro get array key value for mode_info")?; + + let mode_info = mode_info_array + .iter() + .map(|mi| { + mi.as_map() + .ok_or("Erro get map for mode_info".to_owned()) + .and_then(|mi_map| ModeInfo::new(mi_map)) + }) + .collect::, String>>()?; + ui.mode_info_set(try_bool!(args[0]), mode_info) + } _ => { println!("Event {}({:?})", method, args); RepaintMode::Nothing @@ -438,7 +514,9 @@ impl NeovimClientWrapper { match *self { NeovimClientWrapper::Initialized(ref nvim) => nvim, NeovimClientWrapper::Uninitialized => panic!("Access to uninitialized neovim client"), - NeovimClientWrapper::Error => panic!("Access to neovim client that is not started due to some error"), + NeovimClientWrapper::Error => { + panic!("Access to neovim client that is not started due to some error") + } } } @@ -446,20 +524,20 @@ impl NeovimClientWrapper { match *self { NeovimClientWrapper::Initialized(ref mut nvim) => nvim, NeovimClientWrapper::Uninitialized => panic!("Access to uninitialized neovim client"), - NeovimClientWrapper::Error => panic!("Access to neovim client that is not started due to some error"), + NeovimClientWrapper::Error => { + panic!("Access to neovim client that is not started due to some error") + } } } } pub struct NeovimClient { - nvim: NeovimClientWrapper + nvim: NeovimClientWrapper, } impl NeovimClient { pub fn new() -> Self { - NeovimClient { - nvim: NeovimClientWrapper::Uninitialized, - } + NeovimClient { nvim: NeovimClientWrapper::Uninitialized } } pub fn set_nvim(&mut self, nvim: Neovim) { diff --git a/src/shell.rs b/src/shell.rs index c39caee..9048ec0 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1013,6 +1013,14 @@ impl RedrawEvents for State { RepaintMode::Nothing } + + + fn mode_info_set(&mut self, + cursor_style_enabled: bool, + mode_info: Vec) -> RepaintMode { + // TODO: mode info handle + RepaintMode::Nothing + } } impl GuiApi for State { From 27b498a36d1edd2416183a3a989c9ffa0c2dc7f0 Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 8 Jul 2017 14:02:56 +0300 Subject: [PATCH 2/5] Neovim message process cleanup --- src/main.rs | 1 + src/nvim.rs | 100 ++++++++++++++++++++++++------------------------- src/shell.rs | 2 +- src/tabline.rs | 6 +-- src/value.rs | 20 ++++++++++ 5 files changed, 73 insertions(+), 56 deletions(-) create mode 100644 src/value.rs diff --git a/src/main.rs b/src/main.rs index 85fc87f..064e91c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ extern crate serde_derive; extern crate serde; extern crate toml; +mod value; mod ui_model; #[macro_use] mod ui; diff --git a/src/nvim.rs b/src/nvim.rs index eb3febb..f83f569 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -5,7 +5,6 @@ use std::process::{Stdio, Command}; use std::result; use std::sync::Arc; use std::ops::{Deref, DerefMut}; -use std::collections::HashMap; use neovim_lib::{Handler, Neovim, NeovimApi, Session, Value, UiAttachOptions, CallError, UiOption}; use neovim_lib::neovim_api::Tabpage; @@ -15,6 +14,8 @@ use ui_model::{ModelRect, ModelRectVec}; use shell; use glib; +use value::ValueMapExt; + pub trait RedrawEvents { fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode; @@ -59,7 +60,7 @@ pub trait RedrawEvents { fn tabline_update(&mut self, selected: Tabpage, - tabs: Vec<(Tabpage, Option<&str>)>) + tabs: Vec<(Tabpage, Option)>) -> RepaintMode; fn mode_info_set(&mut self, @@ -88,6 +89,23 @@ macro_rules! try_bool { ($exp:expr) => ($exp.as_bool().ok_or("Can't convert argument to bool".to_owned())?) } +macro_rules! map_array { + ($arg:expr, $err:expr, |$item:ident| $exp:expr) => ( + $arg.as_array() + .ok_or($err) + .and_then(|items| items.iter().map(|$item| { + $exp + }).collect::, _>>()) + ); + ($arg:expr, $err:expr, |$item:ident| {$exp:expr}) => ( + $arg.as_array() + .ok_or($err) + .and_then(|items| items.iter().map(|$item| { + $exp + }).collect::, _>>()) + ); +} + pub enum CursorShape { Block, Horizontal, @@ -119,15 +137,7 @@ pub struct ModeInfo { impl ModeInfo { pub fn new(mode_info_arr: &Vec<(Value, Value)>) -> Result { - let mode_info_map = mode_info_arr - .iter() - .map(|p| { - p.0 - .as_str() - .ok_or("mode_info key not string".to_owned()) - .map(|key| (key, p.1.clone())) - }) - .collect::, String>>()?; + let mode_info_map = mode_info_arr.to_attrs_map()?; let cursor_shape = if let Some(shape) = mode_info_map.get("cursor_shape") { Some(CursorShape::new(shape)?) @@ -372,17 +382,11 @@ fn call(ui: &mut shell::State, "busy_start" => ui.on_busy(true), "busy_stop" => ui.on_busy(false), "popupmenu_show" => { - let mut menu_items = Vec::new(); - - let items = args[0].as_array().ok_or("Error get menu list array")?; - for item in items { - let item_line: result::Result, &str> = item.as_array() - .ok_or("Error get menu item array")? - .iter() - .map(|col| col.as_str().ok_or("Error get menu column")) - .collect(); - menu_items.push(item_line?); - } + let menu_items = map_array!(args[0], "Error get menu list array", |item| { + map_array!(item, + "Error get menu item array", + |col| col.as_str().ok_or("Error get menu column")) + })?; ui.popupmenu_show(&menu_items, try_int!(args[1]), @@ -392,40 +396,32 @@ fn call(ui: &mut shell::State, "popupmenu_hide" => ui.popupmenu_hide(), "popupmenu_select" => ui.popupmenu_select(try_int!(args[0])), "tabline_update" => { - let tabs_in = args[1].as_array().ok_or("Error get tabline list")?; + let tabs_out = map_array!(args[1], "Error get tabline list".to_owned(), |tab| { + tab.as_map() + .ok_or("Error get map for tab".to_owned()) + .and_then(|tab_map| tab_map.to_attrs_map()) + .map(|tab_attrs| { + let name_attr = tab_attrs + .get("name") + .and_then(|n| n.as_str().map(|s| s.to_owned())); + let tab_attr = tab_attrs + .get("tab") + .map(|tab_id| Tabpage::new(tab_id.clone())) + .unwrap(); - let mut tabs_out = Vec::new(); - for tab in tabs_in { - let tab_attrs = tab.as_map().ok_or("Error get map for tab")?; - - let mut tab_attr = None; - let mut name_attr = None; - - for attr in tab_attrs { - let key = attr.0.as_str().ok_or("Error get key value")?; - if key == "tab" { - tab_attr = Some(Tabpage::new(attr.1.clone())); - } else if key == "name" { - name_attr = attr.1.as_str(); - } - } - tabs_out.push((tab_attr.unwrap(), name_attr)); - } + (tab_attr, name_attr) + }) + })?; ui.tabline_update(Tabpage::new(args[0].clone()), tabs_out) } "mode_info_set" => { - let mode_info_array = args[1] - .as_array() - .ok_or("Erro get array key value for mode_info")?; - - let mode_info = mode_info_array - .iter() - .map(|mi| { - mi.as_map() - .ok_or("Erro get map for mode_info".to_owned()) - .and_then(|mi_map| ModeInfo::new(mi_map)) - }) - .collect::, String>>()?; + let mode_info = map_array!(args[1], + "Error get array key value for mode_info".to_owned(), + |mi| { + mi.as_map() + .ok_or("Erro get map for mode_info".to_owned()) + .and_then(|mi_map| ModeInfo::new(mi_map)) + })?; ui.mode_info_set(try_bool!(args[0]), mode_info) } _ => { diff --git a/src/shell.rs b/src/shell.rs index 9048ec0..a18a7f8 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1007,7 +1007,7 @@ impl RedrawEvents for State { fn tabline_update(&mut self, selected: Tabpage, - tabs: Vec<(Tabpage, Option<&str>)>) + tabs: Vec<(Tabpage, Option)>) -> RepaintMode { self.tabs.update_tabs(&self.nvim, &selected, &tabs); diff --git a/src/tabline.rs b/src/tabline.rs index 69efe3f..cea0304 100644 --- a/src/tabline.rs +++ b/src/tabline.rs @@ -69,7 +69,7 @@ impl Tabline { fn update_state(&self, nvim: &Rc>, selected: &Tabpage, - tabs: &Vec<(Tabpage, Option<&str>)>) { + tabs: &Vec<(Tabpage, Option)>) { let mut state = self.state.borrow_mut(); if state.nvim.is_none() { @@ -84,7 +84,7 @@ impl Tabline { pub fn update_tabs(&self, nvim: &Rc>, selected: &Tabpage, - tabs: &Vec<(Tabpage, Option<&str>)>) { + tabs: &Vec<(Tabpage, Option)>) { if tabs.len() <= 1 { self.tabs.hide(); return; @@ -113,7 +113,7 @@ impl Tabline { for (idx, tab) in tabs.iter().enumerate() { let tab_child = self.tabs.get_nth_page(Some(idx as u32)); self.tabs - .set_tab_label_text(&tab_child.unwrap(), &tab.1.unwrap_or("??")); + .set_tab_label_text(&tab_child.unwrap(), &tab.1.as_ref().unwrap_or(&"??".to_owned())); if *selected == tab.0 { self.tabs.set_current_page(Some(idx as u32)); diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..f74e7a1 --- /dev/null +++ b/src/value.rs @@ -0,0 +1,20 @@ +use std::collections::HashMap; +use neovim_lib::Value; + +pub trait ValueMapExt { + fn to_attrs_map(&self) -> Result, String>; +} + +impl ValueMapExt for Vec<(Value, Value)> { + fn to_attrs_map(&self) -> Result, String> { + self.iter() + .map(|p| { + p.0 + .as_str() + .ok_or("Can't convert map key to string".to_owned()) + .map(|key| (key, p.1.clone())) + }) + .collect::, String>>() + + } +} From 5a6f7d7581143bb181c53937c773681b90b50eb5 Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 8 Jul 2017 23:45:55 +0300 Subject: [PATCH 3/5] Extract mode struct --- src/cursor.rs | 4 ++-- src/main.rs | 1 + src/mode.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/nvim.rs | 18 +++++++++++++++--- src/shell.rs | 29 ++++++++++------------------- 5 files changed, 74 insertions(+), 24 deletions(-) create mode 100644 src/mode.rs diff --git a/src/cursor.rs b/src/cursor.rs index 96a8000..6a5d1bb 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -2,7 +2,7 @@ use cairo; use ui_model::Color; use ui::UiMutex; use shell; -use shell::NvimMode; +use mode; use nvim::{RepaintMode, RedrawEvents}; use std::sync::{Arc, Weak}; @@ -123,7 +123,7 @@ impl Cursor { let current_point = ctx.get_current_point(); ctx.set_source_rgba(1.0 - bg.0, 1.0 - bg.1, 1.0 - bg.2, 0.6 * state.alpha.0); - let cursor_width = if shell.mode == NvimMode::Insert { + let cursor_width = if shell.mode.is(&mode::NvimMode::Insert) { char_width / 5.0 } else { if double_width { diff --git a/src/main.rs b/src/main.rs index 064e91c..a9cab44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,7 @@ extern crate serde; extern crate toml; mod value; +mod mode; mod ui_model; #[macro_use] mod ui; diff --git a/src/mode.rs b/src/mode.rs new file mode 100644 index 0000000..b1defb4 --- /dev/null +++ b/src/mode.rs @@ -0,0 +1,46 @@ +use nvim; + +#[derive(PartialEq)] +pub enum NvimMode { + Normal, + Insert, + Other, +} + +pub struct Mode { + mode: NvimMode, + idx: usize, + info: Option>, +} + +impl Mode { + pub fn new() -> Self { + Mode { + mode: NvimMode::Normal, + idx: 0, + info: None, + } + } + + pub fn is(&self, mode: &NvimMode) -> bool { + self.mode == *mode + } + + pub fn update(&mut self, mode: &str, idx: usize) { + match mode { + "normal" => self.mode = NvimMode::Normal, + "insert" => self.mode = NvimMode::Insert, + _ => self.mode = NvimMode::Other, + } + + self.idx = idx; + } + + pub fn set_info(&mut self, cursor_style_enabled: bool, info: Vec) { + self.info = if cursor_style_enabled { + Some(info) + } else { + None + }; + } +} diff --git a/src/nvim.rs b/src/nvim.rs index f83f569..60ed3ef 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -41,7 +41,7 @@ pub trait RedrawEvents { fn on_update_sp(&mut self, sp: i64) -> RepaintMode; - fn on_mode_change(&mut self, mode: &str) -> RepaintMode; + fn on_mode_change(&mut self, mode: &str, idx: u64) -> RepaintMode; fn on_mouse(&mut self, on: bool) -> RepaintMode; @@ -106,6 +106,7 @@ macro_rules! map_array { ); } +#[derive(Debug)] pub enum CursorShape { Block, Horizontal, @@ -131,8 +132,10 @@ impl CursorShape { } } +#[derive(Debug)] pub struct ModeInfo { cursor_shape: Option, + cell_percentage: Option, } impl ModeInfo { @@ -145,7 +148,16 @@ impl ModeInfo { None }; - Ok(ModeInfo { cursor_shape }) + let cell_percentage = if let Some(cell_percentage) = mode_info_map.get("cell_percentage") { + cell_percentage.as_u64() + } else { + None + }; + + Ok(ModeInfo { + cursor_shape, + cell_percentage, + }) } } @@ -376,7 +388,7 @@ fn call(ui: &mut shell::State, "update_bg" => ui.on_update_bg(try_int!(args[0])), "update_fg" => ui.on_update_fg(try_int!(args[0])), "update_sp" => ui.on_update_sp(try_int!(args[0])), - "mode_change" => ui.on_mode_change(try_str!(args[0])), + "mode_change" => ui.on_mode_change(try_str!(args[0]), try_uint!(args[1])), "mouse_on" => ui.on_mouse(true), "mouse_off" => ui.on_mouse(false), "busy_start" => ui.on_busy(true), diff --git a/src/shell.rs b/src/shell.rs index a18a7f8..f1a7deb 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -28,6 +28,7 @@ use ui::UiMutex; use popup_menu::PopupMenu; use tabline::Tabline; use error; +use mode; const DEFAULT_FONT_NAME: &str = "DejaVu Sans Mono 12"; pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.2"; @@ -44,21 +45,12 @@ macro_rules! idle_cb_call { ) } - -#[derive(PartialEq)] -pub enum NvimMode { - Normal, - Insert, - Other, -} - pub struct State { pub model: UiModel, bg_color: Color, fg_color: Color, sp_color: Color, cur_attrs: Option, - pub mode: NvimMode, mouse_enabled: bool, nvim: Rc>, font_desc: FontDescription, @@ -66,6 +58,8 @@ pub struct State { popup_menu: RefCell, settings: Rc>, + pub mode: mode::Mode, + stack: gtk::Stack, drawing_area: gtk::DrawingArea, tabs: Tabline, @@ -93,13 +87,15 @@ impl State { bg_color: COLOR_BLACK, fg_color: COLOR_WHITE, sp_color: COLOR_RED, - mode: NvimMode::Normal, mouse_enabled: true, font_desc: FontDescription::from_string(DEFAULT_FONT_NAME), cursor: None, popup_menu, settings: settings, + mode: mode::Mode::new(), + + // UI stack: gtk::Stack::new(), drawing_area, tabs: Tabline::new(), @@ -414,7 +410,7 @@ impl Shell { pub fn edit_paste(&self) { let state = self.state.borrow(); - let paste_command = if state.mode == NvimMode::Normal { + let paste_command = if state.mode.is(&mode::NvimMode::Normal) { "\"*p" } else { "\"*pa" @@ -949,13 +945,8 @@ impl RedrawEvents for State { RepaintMode::Nothing } - 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, - } - + fn on_mode_change(&mut self, mode: &str, idx: u64) -> RepaintMode { + self.mode.update(mode, idx as usize); RepaintMode::Area(self.model.cur_point()) } @@ -1018,7 +1009,7 @@ impl RedrawEvents for State { fn mode_info_set(&mut self, cursor_style_enabled: bool, mode_info: Vec) -> RepaintMode { - // TODO: mode info handle + self.mode.set_info(cursor_style_enabled, mode_info); RepaintMode::Nothing } } From 90dc2d4a530e5c3b8066cb6ac0a82e6f4720869f Mon Sep 17 00:00:00 2001 From: daa Date: Sun, 9 Jul 2017 01:29:52 +0300 Subject: [PATCH 4/5] Basic functions --- src/mode.rs | 7 +++++++ src/nvim.rs | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/mode.rs b/src/mode.rs index b1defb4..fab111f 100644 --- a/src/mode.rs +++ b/src/mode.rs @@ -26,6 +26,13 @@ impl Mode { self.mode == *mode } + pub fn mode_info(&self) -> nvim::ModeInfo { + self.info + .as_ref() + .and_then(|i| i.get(self.idx).cloned()) + .unwrap_or_else(nvim::ModeInfo::default) + } + pub fn update(&mut self, mode: &str, idx: usize) { match mode { "normal" => self.mode = NvimMode::Normal, diff --git a/src/nvim.rs b/src/nvim.rs index 60ed3ef..b0409dd 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -106,7 +106,7 @@ macro_rules! map_array { ); } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum CursorShape { Block, Horizontal, @@ -132,13 +132,20 @@ impl CursorShape { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ModeInfo { cursor_shape: Option, cell_percentage: Option, } impl ModeInfo { + pub fn default() -> Self { + ModeInfo { + cursor_shape: Some(CursorShape::Block), + cell_percentage: None, + } + } + pub fn new(mode_info_arr: &Vec<(Value, Value)>) -> Result { let mode_info_map = mode_info_arr.to_attrs_map()?; @@ -159,6 +166,14 @@ impl ModeInfo { cell_percentage, }) } + + pub fn cursor_shape(&self) -> CursorShape { + self.cursor_shape.as_ref().cloned().unwrap_or(CursorShape::Block) + } + + pub fn cell_percentage(&self) -> u64 { + self.cell_percentage.unwrap_or(100) + } } #[derive(Debug)] From 609d593db57d287a11713f525189f99ca497f056 Mon Sep 17 00:00:00 2001 From: daa Date: Sun, 9 Jul 2017 12:40:31 +0300 Subject: [PATCH 5/5] Draw gui cursor shape --- src/cursor.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++++---- src/mode.rs | 5 +- src/nvim.rs | 13 ++---- 3 files changed, 123 insertions(+), 21 deletions(-) diff --git a/src/cursor.rs b/src/cursor.rs index 6a5d1bb..5522474 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -3,6 +3,7 @@ use ui_model::Color; use ui::UiMutex; use shell; use mode; +use nvim; use nvim::{RepaintMode, RedrawEvents}; use std::sync::{Arc, Weak}; @@ -123,7 +124,63 @@ impl Cursor { let current_point = ctx.get_current_point(); ctx.set_source_rgba(1.0 - bg.0, 1.0 - bg.1, 1.0 - bg.2, 0.6 * state.alpha.0); - let cursor_width = if shell.mode.is(&mode::NvimMode::Insert) { + let (y, width, height) = + cursor_rect(&shell.mode, char_width, line_height, line_y, double_width); + + ctx.rectangle(current_point.0, y, width, height); + if state.anim_phase == AnimPhase::NoFocus { + ctx.stroke(); + } else { + ctx.fill(); + } + } +} + +fn cursor_rect(mode: &mode::Mode, + char_width: f64, + line_height: f64, + line_y: f64, + double_width: bool) + -> (f64, f64, f64) { + if let Some(mode_info) = mode.mode_info() { + match mode_info.cursor_shape() { + None | + Some(&nvim::CursorShape::Unknown) | + Some(&nvim::CursorShape::Block) => { + let cursor_width = if double_width { + char_width * 2.0 + } else { + char_width + }; + (line_y, cursor_width, line_height) + } + Some(&nvim::CursorShape::Vertical) => { + let cell_percentage = mode_info.cell_percentage(); + let cursor_width = if cell_percentage > 0 { + (char_width * cell_percentage as f64) / 100.0 + } else { + char_width + }; + (line_y, cursor_width, line_height) + } + Some(&nvim::CursorShape::Horizontal) => { + let cell_percentage = mode_info.cell_percentage(); + let cursor_width = if double_width { + char_width * 2.0 + } else { + char_width + }; + + if cell_percentage > 0 { + let height = (line_height * cell_percentage as f64) / 100.0; + (line_y + line_height - height, cursor_width, height) + } else { + (line_y, cursor_width, line_height) + } + } + } + } else { + let cursor_width = if mode.is(&mode::NvimMode::Insert) { char_width / 5.0 } else { if double_width { @@ -133,15 +190,9 @@ impl Cursor { } }; - ctx.rectangle(current_point.0, line_y, cursor_width, line_height); - if state.anim_phase == AnimPhase::NoFocus { - ctx.stroke(); - } else { - ctx.fill(); - } + (line_y, cursor_width, line_height) } } - fn anim_step(state: &Arc>) -> glib::Continue { let mut mut_state = state.borrow_mut(); @@ -201,3 +252,62 @@ impl Drop for Cursor { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cursor_rect_horizontal() { + let mut mode = mode::Mode::new(); + let mode_info = nvim::ModeInfo::new(&vec![(From::from("cursor_shape"), + From::from("horizontal")), + (From::from("cell_percentage"), From::from(25))]); + mode.update("insert", 0); + mode.set_info(true, vec![mode_info.unwrap()]); + let char_width = 50.0; + let line_height = 30.0; + let line_y = 0.0; + + let (y, width, height) = cursor_rect(&mode, char_width, line_height, line_y, false); + assert_eq!(line_y + line_height - line_height / 4.0, y); + assert_eq!(char_width, width); + assert_eq!(line_height / 4.0, height); + } + + #[test] + fn test_cursor_rect_horizontal_doublewidth() { + let mut mode = mode::Mode::new(); + let mode_info = nvim::ModeInfo::new(&vec![(From::from("cursor_shape"), + From::from("horizontal")), + (From::from("cell_percentage"), From::from(25))]); + mode.update("insert", 0); + mode.set_info(true, vec![mode_info.unwrap()]); + let char_width = 50.0; + let line_height = 30.0; + let line_y = 0.0; + + let (y, width, height) = cursor_rect(&mode, char_width, line_height, line_y, true); + assert_eq!(line_y + line_height - line_height / 4.0, y); + assert_eq!(char_width * 2.0, width); + assert_eq!(line_height / 4.0, height); + } + + #[test] + fn test_cursor_rect_vertical() { + let mut mode = mode::Mode::new(); + let mode_info = nvim::ModeInfo::new(&vec![(From::from("cursor_shape"), + From::from("vertical")), + (From::from("cell_percentage"), From::from(25))]); + mode.update("insert", 0); + mode.set_info(true, vec![mode_info.unwrap()]); + let char_width = 50.0; + let line_height = 30.0; + let line_y = 0.0; + + let (y, width, height) = cursor_rect(&mode, char_width, line_height, line_y, false); + assert_eq!(line_y, y); + assert_eq!(char_width / 4.0, width); + assert_eq!(line_height, height); + } +} diff --git a/src/mode.rs b/src/mode.rs index fab111f..e8fdc60 100644 --- a/src/mode.rs +++ b/src/mode.rs @@ -26,11 +26,10 @@ impl Mode { self.mode == *mode } - pub fn mode_info(&self) -> nvim::ModeInfo { + pub fn mode_info(&self) -> Option<&nvim::ModeInfo> { self.info .as_ref() - .and_then(|i| i.get(self.idx).cloned()) - .unwrap_or_else(nvim::ModeInfo::default) + .and_then(|i| i.get(self.idx)) } pub fn update(&mut self, mode: &str, idx: usize) { diff --git a/src/nvim.rs b/src/nvim.rs index b0409dd..eabcae5 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -139,13 +139,6 @@ pub struct ModeInfo { } impl ModeInfo { - pub fn default() -> Self { - ModeInfo { - cursor_shape: Some(CursorShape::Block), - cell_percentage: None, - } - } - pub fn new(mode_info_arr: &Vec<(Value, Value)>) -> Result { let mode_info_map = mode_info_arr.to_attrs_map()?; @@ -167,12 +160,12 @@ impl ModeInfo { }) } - pub fn cursor_shape(&self) -> CursorShape { - self.cursor_shape.as_ref().cloned().unwrap_or(CursorShape::Block) + pub fn cursor_shape(&self) -> Option<&CursorShape> { + self.cursor_shape.as_ref() } pub fn cell_percentage(&self) -> u64 { - self.cell_percentage.unwrap_or(100) + self.cell_percentage.unwrap_or(0) } }