Refactor nvim client

This commit is contained in:
daa84 2017-11-10 18:36:54 +03:00
parent 74514258eb
commit 2ee2fa31be
7 changed files with 202 additions and 125 deletions

View File

@ -1,112 +1,166 @@
use std::ops::{Deref, DerefMut};
use std::cell::{Cell, RefCell, RefMut};
use std::sync::{Arc, Mutex, MutexGuard};
use neovim_lib::Neovim;
#[derive(Clone, Copy, PartialEq)]
enum NeovimClientState {
Uninitialized,
InitInProgress,
Initialized(Neovim),
Initialized,
Error,
}
impl NeovimClientState {
pub fn is_initializing(&self) -> bool {
pub enum NeovimRef<'a> {
SingleThreaded(RefMut<'a, Neovim>),
MultiThreaded {
guard: MutexGuard<'a, RefCell<Option<Neovim>>>,
nvim: RefMut<'a, Option<Neovim>>,
},
}
impl<'a> NeovimRef<'a> {
fn from_nvim(nvim: RefMut<'a, Neovim>) -> Self {
NeovimRef::SingleThreaded(nvim)
}
fn is_some(&self) -> bool {
match *self {
NeovimClientState::InitInProgress => true,
_ => false,
NeovimRef::MultiThreaded{ref nvim, ..} => nvim.is_some(),
NeovimRef::SingleThreaded(_) => true,
}
}
pub fn is_uninitialized(&self) -> bool {
match *self {
NeovimClientState::Uninitialized => true,
_ => false,
fn from_nvim_async(nvim_async: &'a NeovimClientAsync) -> Option<NeovimRef<'a>> {
let guard = nvim_async.nvim.lock().unwrap();
let nvim = guard.borrow_mut();
let nvim_ref = NeovimRef::MultiThreaded { guard, nvim };
if nvim_ref.is_some() {
Some(nvim_ref)
} else {
None
}
}
}
pub fn is_initialized(&self) -> bool {
impl<'a> Deref for NeovimRef<'a> {
type Target = Neovim;
fn deref(&self) -> &Neovim {
match *self {
NeovimClientState::Initialized(_) => true,
_ => false,
NeovimRef::SingleThreaded(ref nvim) => &*nvim,
NeovimRef::MultiThreaded { ref nvim, .. } => (&*nvim).as_ref().unwrap(),
}
}
}
pub fn nvim(&self) -> &Neovim {
impl<'a> DerefMut for NeovimRef<'a> {
fn deref_mut(&mut self) -> &mut Neovim {
match *self {
NeovimClientState::Initialized(ref nvim) => nvim,
NeovimClientState::InitInProgress |
NeovimClientState::Uninitialized => panic!("Access to uninitialized neovim client"),
NeovimClientState::Error => {
panic!("Access to neovim client that is not started due to some error")
}
NeovimRef::SingleThreaded(ref mut nvim) => &mut *nvim,
NeovimRef::MultiThreaded { ref mut nvim, .. } => (&mut *nvim).as_mut().unwrap(),
}
}
}
pub fn nvim_mut(&mut self) -> &mut Neovim {
match *self {
NeovimClientState::Initialized(ref mut nvim) => nvim,
NeovimClientState::InitInProgress |
NeovimClientState::Uninitialized => panic!("Access to uninitialized neovim client"),
NeovimClientState::Error => {
panic!("Access to neovim client that is not started due to some error")
}
pub struct NeovimClientAsync {
nvim: Arc<Mutex<RefCell<Option<Neovim>>>>,
}
impl NeovimClientAsync {
fn new(nvim: Neovim) -> Self {
NeovimClientAsync { nvim: Arc::new(Mutex::new(RefCell::new(Some(nvim)))) }
}
pub fn borrow(&self) -> NeovimRef {
NeovimRef::from_nvim_async(self).unwrap()
}
}
impl Clone for NeovimClientAsync {
fn clone(&self) -> Self {
NeovimClientAsync {
nvim: self.nvim.clone()
}
}
}
pub struct NeovimClient {
state: NeovimClientState,
state: Cell<NeovimClientState>,
nvim: RefCell<Option<Neovim>>,
nvim_async: RefCell<Option<NeovimClientAsync>>,
}
impl NeovimClient {
pub fn new() -> Self {
NeovimClient { state: NeovimClientState::Uninitialized }
NeovimClient {
state: Cell::new(NeovimClientState::Uninitialized),
nvim: RefCell::new(None),
nvim_async: RefCell::new(None),
}
}
pub fn set_initialized(&mut self, nvim: Neovim) {
self.state = NeovimClientState::Initialized(nvim);
pub fn async_to_sync(&self) {
{
let lock = self.nvim_async
.borrow()
.as_ref()
.expect("Nvim not initialized")
.nvim
.lock()
.unwrap();
let nvim = lock.borrow_mut().take().unwrap();
*self.nvim.borrow_mut() = Some(nvim);
}
*self.nvim_async.borrow_mut() = None;
}
pub fn set_error(&mut self) {
self.state = NeovimClientState::Error;
pub fn set_nvim_async(&self, nvim: Neovim) -> NeovimClientAsync {
let nvim_async = NeovimClientAsync::new(nvim);
*self.nvim_async.borrow_mut() = Some(nvim_async.clone());
nvim_async
}
pub fn set_in_progress(&mut self) {
self.state = NeovimClientState::InitInProgress;
pub fn set_initialized(&self) {
self.state.set(NeovimClientState::Initialized);
}
pub fn set_error(&self) {
self.state.set(NeovimClientState::Error);
}
pub fn set_in_progress(&self) {
self.state.set(NeovimClientState::InitInProgress);
}
pub fn is_initialized(&self) -> bool {
self.state.is_initialized()
self.state.get() == NeovimClientState::Initialized
}
pub fn is_uninitialized(&self) -> bool {
self.state.is_uninitialized()
self.state.get() == NeovimClientState::Uninitialized
}
pub fn is_initializing(&self) -> bool {
self.state.is_initializing()
self.state.get() == NeovimClientState::InitInProgress
}
pub fn nvim(&self) -> &Neovim {
self.state.nvim()
}
pub fn nvim_mut(&mut self) -> &mut Neovim {
self.state.nvim_mut()
pub fn nvim(&self) -> Option<NeovimRef> {
let nvim = self.nvim.borrow_mut();
if nvim.is_some() {
Some(NeovimRef::from_nvim(
RefMut::map(nvim, |n| n.as_mut().unwrap()),
))
} else {
let nvim_async = self.nvim_async.borrow();
if let Some(ref nvim_async) = *nvim_async {
NeovimRef::from_nvim_async(nvim_async)
} else {
None
}
}
}
}
impl Deref for NeovimClient {
type Target = Neovim;
fn deref(&self) -> &Neovim {
self.nvim()
}
}
impl DerefMut for NeovimClient {
fn deref_mut(&mut self) -> &mut Neovim {
self.nvim_mut()
}
}

View File

@ -7,7 +7,7 @@ mod repaint_mode;
pub use self::redraw_handler::{RedrawEvents, GuiApi};
pub use self::repaint_mode::RepaintMode;
pub use self::client::NeovimClient;
pub use self::client::{NeovimClient, NeovimClientAsync, NeovimRef};
pub use self::mode_info::{ModeInfo, CursorShape};
use std::error;
@ -131,7 +131,7 @@ pub fn start(
}
pub fn post_start_init(
nvim: &mut Neovim,
nvim: NeovimClientAsync,
open_path: Option<&String>,
cols: u64,
rows: u64,
@ -139,15 +139,15 @@ pub fn post_start_init(
let mut opts = UiAttachOptions::new();
opts.set_popupmenu_external(false);
opts.set_tabline_external(true);
nvim.ui_attach(cols, rows, &opts).map_err(
nvim.borrow().ui_attach(cols, rows, &opts).map_err(
NvimInitError::new_post_init,
)?;
nvim.command("runtime! ginit.vim").map_err(
nvim.borrow().command("runtime! ginit.vim").map_err(
NvimInitError::new_post_init,
)?;
if let Some(path) = open_path {
nvim.command(&format!("e {}", path)).map_err(
nvim.borrow().command(&format!("e {}", path)).map_err(
NvimInitError::new_post_init,
)?;
}

View File

@ -1,5 +1,4 @@
use std::rc::Rc;
use std::cell::RefCell;
use super::vim_plug;
use super::store::{Store, PlugInfo};
@ -35,7 +34,7 @@ impl Manager {
}
}
pub fn init_nvim_client(&mut self, nvim: Rc<RefCell<NeovimClient>>) {
pub fn init_nvim_client(&mut self, nvim: Rc<NeovimClient>) {
self.vim_plug.initialize(nvim);
}

View File

@ -1,13 +1,12 @@
use std::rc::Rc;
use std::cell::{RefCell, RefMut};
use neovim_lib::{Neovim, NeovimApi};
use neovim_lib::NeovimApi;
use nvim::{NeovimClient, ErrorReport};
use nvim::{NeovimClient, ErrorReport, NeovimRef};
use value::ValueMapExt;
pub struct Manager {
nvim: Option<Rc<RefCell<NeovimClient>>>,
nvim: Option<Rc<NeovimClient>>,
}
impl Manager {
@ -15,17 +14,12 @@ impl Manager {
Manager { nvim: None }
}
pub fn initialize(&mut self, nvim: Rc<RefCell<NeovimClient>>) {
pub fn initialize(&mut self, nvim: Rc<NeovimClient>) {
self.nvim = Some(nvim);
}
fn nvim(&self) -> Option<RefMut<Neovim>> {
let nvim_client = self.nvim.as_ref().unwrap();
if nvim_client.borrow().is_initialized() {
Some(RefMut::map(nvim_client.borrow_mut(), |n| n.nvim_mut()))
} else {
None
}
fn nvim(&self) -> Option<NeovimRef> {
self.nvim.as_ref().unwrap().nvim()
}
pub fn get_plugs(&self) -> Result<Box<[VimPlugInfo]>, String> {

View File

@ -17,7 +17,7 @@ use input;
const MAX_VISIBLE_ROWS: i32 = 10;
struct State {
nvim: Option<Rc<RefCell<nvim::NeovimClient>>>,
nvim: Option<Rc<nvim::NeovimClient>>,
renderer: gtk::CellRendererText,
tree: gtk::TreeView,
scroll: gtk::ScrolledWindow,
@ -139,8 +139,11 @@ impl PopupMenu {
let state_ref = state.clone();
state.borrow().tree.connect_button_press_event(move |tree, ev| {
let state = state_ref.borrow();
let mut nvim = state.nvim.as_ref().unwrap().borrow_mut();
tree_button_press(tree, ev, &mut *nvim)
if let Some(mut nvim) = state.nvim.as_ref().unwrap().nvim() {
tree_button_press(tree, ev, &mut *nvim)
} else {
Inhibit(false)
}
});
let state_ref = state.clone();
@ -149,8 +152,12 @@ impl PopupMenu {
let state_ref = state.clone();
popover.connect_key_press_event(move |_, ev| {
let state = state_ref.borrow();
let mut nvim = state.nvim.as_ref().unwrap().borrow_mut();
input::gtk_key_press(&mut *nvim, ev)
let nvim = state.nvim.as_ref().unwrap();
if let Some(mut nvim) = nvim.nvim() {
input::gtk_key_press(&mut *nvim, ev)
} else {
Inhibit(false)
}
});
PopupMenu {

View File

@ -1,6 +1,6 @@
use std::cell::{RefMut, RefCell, Cell};
use std::cell::{RefCell, Cell};
use std::rc::Rc;
use std::sync::Arc;
use std::sync::{Arc, Condvar, Mutex};
use std::ops::Deref;
use std::thread;
@ -21,7 +21,7 @@ use settings::{Settings, FontSource};
use ui_model::{UiModel, Attrs, ModelRect};
use color::{ColorModel, Color, COLOR_BLACK, COLOR_WHITE, COLOR_RED};
use nvim;
use nvim::{RedrawEvents, GuiApi, RepaintMode, ErrorReport, NeovimClient};
use nvim::{RedrawEvents, GuiApi, RepaintMode, ErrorReport, NeovimClient, NeovimRef, NeovimClientAsync};
use input;
use input::keyval_to_input_string;
use cursor::Cursor;
@ -60,7 +60,7 @@ pub struct State {
color_model: ColorModel,
cur_attrs: Option<Attrs>,
mouse_enabled: bool,
nvim: Rc<RefCell<NeovimClient>>,
nvim: Rc<NeovimClient>,
pub font_ctx: render::Context,
cursor: Option<Cursor>,
popup_menu: RefCell<PopupMenu>,
@ -91,7 +91,7 @@ impl State {
State {
model: UiModel::empty(),
color_model: ColorModel::new(),
nvim: Rc::new(RefCell::new(NeovimClient::new())),
nvim: Rc::new(NeovimClient::new()),
cur_attrs: None,
mouse_enabled: true,
font_ctx,
@ -125,22 +125,17 @@ impl State {
&self.color_model.bg_color
}
pub fn nvim(&self) -> Option<RefMut<Neovim>> {
if self.nvim.borrow().is_initialized() {
Some(RefMut::map(self.nvim.borrow_mut(), |n| n.nvim_mut()))
} else {
None
}
pub fn nvim(&self) -> Option<NeovimRef> {
self.nvim.nvim()
}
pub fn nvim_clone(&self) -> Rc<RefCell<NeovimClient>> {
pub fn nvim_clone(&self) -> Rc<NeovimClient> {
self.nvim.clone()
}
pub fn start_nvim_initialization(&self) -> bool {
let mut nvim = self.nvim.borrow_mut();
if nvim.is_uninitialized() {
nvim.set_in_progress();
if self.nvim.is_uninitialized() {
self.nvim.set_in_progress();
true
} else {
false
@ -229,7 +224,9 @@ impl State {
}
fn im_commit(&self, ch: &str) {
input::im_input(&mut self.nvim.borrow_mut(), ch);
if let Some(mut nvim) = self.nvim() {
input::im_input(&mut nvim, ch);
}
}
fn calc_nvim_size(&self) -> (usize, usize) {
@ -270,7 +267,7 @@ impl State {
}
fn try_nvim_resize(&self) {
if !self.nvim.borrow().is_initialized() {
if !self.nvim.is_initialized() {
return;
}
@ -305,9 +302,10 @@ impl State {
gtk::timeout_add(250, move || {
resize_state.set(ResizeState::NvimResizeRequest(columns, rows));
let mut nvim = nvim.borrow_mut();
if let Err(err) = nvim.ui_try_resize(columns as u64, rows as u64) {
error!("Error trying resize nvim {}", err);
if let Some(mut nvim) = nvim.nvim() {
if let Err(err) = nvim.ui_try_resize(columns as u64, rows as u64) {
error!("Error trying resize nvim {}", err);
}
}
Continue(false)
}),
@ -406,8 +404,7 @@ impl Shell {
pub fn is_nvim_initialized(&self) -> bool {
let state = self.state.borrow();
let nvim = state.nvim.borrow();
nvim.is_initialized()
state.nvim.is_initialized()
}
pub fn init(&mut self) {
@ -488,8 +485,8 @@ impl Shell {
{
Inhibit(true)
} else {
if shell.nvim.borrow().is_initialized() {
input::gtk_key_press(&mut shell.nvim.borrow_mut(), ev)
if let Some(mut nvim) = shell.nvim() {
input::gtk_key_press(&mut nvim, ev)
} else {
Inhibit(false)
}
@ -716,7 +713,7 @@ fn gtk_motion_notify(shell: &mut State, ui_state: &mut UiState, ev: &EventMotion
fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit {
let state = state_arc.borrow();
if state.nvim.borrow().is_initialized() {
if state.nvim.is_initialized() {
render::render(
ctx,
state.cursor.as_ref().unwrap(),
@ -725,7 +722,7 @@ fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit {
&state.color_model,
&state.mode,
);
} else if state.nvim.borrow().is_initializing() {
} else if state.nvim.is_initializing() {
draw_initializing(&*state, ctx);
}
@ -738,7 +735,7 @@ fn show_nvim_start_error(err: &nvim::NvimInitError, state_arc: Arc<UiMutex<State
glib::idle_add(move || {
let state = state_arc.borrow();
state.nvim.borrow_mut().set_error();
state.nvim.set_error();
state.error_area.show_nvim_start_error(&source, &cmd);
state.show_error_area();
@ -751,7 +748,7 @@ fn show_nvim_init_error(err: &nvim::NvimInitError, state_arc: Arc<UiMutex<State>
glib::idle_add(move || {
let state = state_arc.borrow();
state.nvim.borrow_mut().set_error();
state.nvim.set_error();
state.error_area.show_nvim_init_error(&source);
state.show_error_area();
@ -766,7 +763,7 @@ fn init_nvim_async(
rows: usize,
) {
// execute nvim
let mut nvim = match nvim::start(state_arc.clone(), options.nvim_bin_path.as_ref()) {
let nvim = match nvim::start(state_arc.clone(), options.nvim_bin_path.as_ref()) {
Ok(nvim) => nvim,
Err(err) => {
show_nvim_start_error(&err, state_arc);
@ -774,8 +771,10 @@ fn init_nvim_async(
}
};
let nvim = set_nvim_to_state(state_arc.clone(), nvim);
// add callback on session end
let guard = nvim.session.take_dispatch_guard();
let guard = nvim.borrow().session.take_dispatch_guard();
let state_ref = state_arc.clone();
thread::spawn(move || {
guard.join().expect("Can't join dispatch thread");
@ -785,7 +784,7 @@ fn init_nvim_async(
// attach ui
if let Err(err) = nvim::post_start_init(
&mut nvim,
nvim,
options.open_path.as_ref(),
cols as u64,
rows as u64,
@ -793,17 +792,40 @@ fn init_nvim_async(
{
show_nvim_init_error(&err, state_arc.clone());
} else {
set_nvim_initialized(nvim, state_arc);
set_nvim_initialized(state_arc);
}
}
fn set_nvim_initialized(nvim: Neovim, state_arc: Arc<UiMutex<State>>) {
let mut nvim = Some(nvim);
fn set_nvim_to_state(state_arc: Arc<UiMutex<State>>, nvim: Neovim) -> NeovimClientAsync {
let pair = Arc::new((Mutex::new(None), Condvar::new()));
let pair2 = pair.clone();
glib::idle_add(move || {
let nvim_aync = state_arc.borrow().nvim.set_nvim_async(nvim);
let &(ref lock, ref cvar) = &*pair2;
let mut started = lock.lock().unwrap();
*started = Some(nvim_aync);
cvar.notify_one();
Continue(false)
});
// Wait idle set nvim properly
let &(ref lock, ref cvar) = &*pair;
let mut started = lock.lock().unwrap();
while started.is_none() {
started = cvar.wait(started).unwrap();
}
started.take().unwrap()
}
fn set_nvim_initialized(state_arc: Arc<UiMutex<State>>) {
glib::idle_add(clone!(state_arc => move || {
let mut state = state_arc.borrow_mut();
state.nvim.borrow_mut().set_initialized(
nvim.take().unwrap(),
);
state.nvim.async_to_sync();
state.nvim.set_initialized();
state.cursor.as_mut().unwrap().start();
Continue(false)

View File

@ -18,7 +18,7 @@ use nvim::ErrorReport;
struct State {
data: Vec<Tabpage>,
selected: Option<Tabpage>,
nvim: Option<Rc<RefCell<nvim::NeovimClient>>>,
nvim: Option<Rc<nvim::NeovimClient>>,
}
impl State {
@ -33,8 +33,9 @@ impl State {
fn switch_page(&self, idx: u32) {
let target = &self.data[idx as usize];
if Some(target) != self.selected.as_ref() {
let mut nvim = self.nvim.as_ref().unwrap().borrow_mut();
nvim.set_current_tabpage(target).report_err(&mut **nvim);
if let Some(nvim) = self.nvim.as_ref().unwrap().nvim() {
nvim.set_current_tabpage(target).report_err(&mut *nvim);
}
}
}
}
@ -70,7 +71,7 @@ impl Tabline {
fn update_state(
&self,
nvim: &Rc<RefCell<nvim::NeovimClient>>,
nvim: &Rc<nvim::NeovimClient>,
selected: &Tabpage,
tabs: &[(Tabpage, Option<String>)],
) {
@ -87,7 +88,7 @@ impl Tabline {
pub fn update_tabs(
&self,
nvim: &Rc<RefCell<nvim::NeovimClient>>,
nvim: &Rc<nvim::NeovimClient>,
selected: &Tabpage,
tabs: &[(Tabpage, Option<String>)],
) {