Skip to content

Commit

Permalink
Move over to using nvim-rs instead of neovim-lib
Browse files Browse the repository at this point in the history
While neovim-lib was originally started for neovim-gtk, it hasn't really
seen any new developments in quite a while now - while the fork of it,
nvim-rs, seems to still be relatively active. Additionally, nvim-rs also
just has a much cleaner API in general and is much more capable when it
comes to cleanly handling errors and shutting our neovim instance down.

So, let's do the initial work of moving over to using nvim-rs so that we
can implement these aforementioned features. Note that we also get rid of
neovim-gtk's concept of having both non-thread safe and thread-safe
references to our Neovim<W> instance - something that's not really needed
as nvim-rs ensures Neovim<W> itself is thread safe.

And finally, we also add the NvimWriter wrapper - which simply wraps around
ChildStdin as provided by tokio so that we can eventually support
neovim instances connected via other means, such as TCP sockets.
  • Loading branch information
Lyude committed Aug 31, 2021
1 parent c036492 commit ca5d183
Show file tree
Hide file tree
Showing 21 changed files with 486 additions and 421 deletions.
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ gobject-sys = "0.9"
#pango-sys = { git = 'https://github.com/gtk-rs/sys' }
#gio = { git = 'https://github.com/gtk-rs/gio' }
#pangocairo = { git = 'https://github.com/RazrFalcon/pangocairo-rs' }
neovim-lib = "0.6"
async-trait = "^0"
futures = { version = "0.3", features = ["io-compat", "thread-pool"] }
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "^0", features = ["full"] }
nvim-rs = { version = "^0.3", features = ["use_tokio"] }
phf = "0.8"
log = "0.4"
env_logger = "0.7"
htmlescape = "0.3"
rmpv = "0.4"
rmpv = { version = "^1", features = ["with-serde"] }
percent-encoding = "1.0"
regex = "1.3"
lazy_static = "1.4"
Expand Down Expand Up @@ -73,4 +77,3 @@ features = ["v3_22"]
version = "0.9"
features = ["v3_22"]
#git = 'https://github.com/gtk-rs/sys'

2 changes: 1 addition & 1 deletion src/cmd_line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ impl CmdLine {
let state = state.borrow();
let nvim = state.nvim.as_ref().unwrap().nvim();
if let Some(mut nvim) = nvim {
popup_menu::tree_button_press(tree, ev, &mut *nvim, "");
popup_menu::tree_button_press(tree, ev, &mut nvim, "");
}
Inhibit(false)
}));
Expand Down
36 changes: 16 additions & 20 deletions src/file_browser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ use gio::prelude::*;
use gtk;
use gtk::prelude::*;

use neovim_lib::{NeovimApi, NeovimApiAsync};

use crate::misc::escape_filename;
use crate::nvim::{ErrorReport, NeovimClient, NeovimRef};
use crate::nvim::{NeovimClient, NvimSession};
use crate::spawn_timeout;
use crate::shell;
use crate::subscriptions::SubscriptionKey;

Expand Down Expand Up @@ -102,7 +101,7 @@ impl FileBrowserWidget {
file_browser
}

fn nvim(&self) -> Option<NeovimRef> {
fn nvim(&self) -> Option<NvimSession> {
self.nvim.as_ref().unwrap().nvim()
}

Expand Down Expand Up @@ -178,11 +177,10 @@ impl FileBrowserWidget {

let cd_action = &self.comps.cd_action;
cd_action.connect_activate(clone!(state_ref, nvim_ref => move |_, _| {
let mut nvim = nvim_ref.nvim().unwrap();
if let Some(ref path) = state_ref.borrow().selected_path {
nvim.set_current_dir_async(&path)
.cb(|r| r.report_err())
.call();
let nvim = nvim_ref.nvim().unwrap();
if let Some(ref path) = &state_ref.borrow().selected_path {
let path = path.clone();
spawn_timeout!(nvim.set_current_dir(&path));
}
}));
actions.add_action(cd_action);
Expand Down Expand Up @@ -269,10 +267,10 @@ impl FileBrowserWidget {
} else {
&file_path
};
let file_path = escape_filename(file_path);
nvim_ref.nvim().unwrap().command_async(&format!(":e {}", file_path))
.cb(|r| r.report_err())
.call();
let file_path = escape_filename(file_path).to_string();
let nvim = nvim_ref.nvim().unwrap();

spawn_timeout!(nvim.command(&format!(":e {}", file_path)));
}
}));

Expand All @@ -281,12 +279,10 @@ impl FileBrowserWidget {
self.comps.dir_list.connect_changed(clone!(nvim_ref, state_ref => move |dir_list| {
if let Some(iter) = dir_list.get_active_iter() {
let model = dir_list.get_model().unwrap();
if let Some(dir) = model.get_value(&iter, 2).get::<&str>() {
if let Some(dir) = model.get_value(&iter, 2).get::<String>() {
if dir != state_ref.borrow().current_dir {
let mut nvim = nvim_ref.nvim().unwrap();
nvim.set_current_dir_async(dir)
.cb(|r| r.report_err())
.call();
let nvim = nvim_ref.nvim().unwrap();
spawn_timeout!(nvim.set_current_dir(&dir));
}
}
}
Expand Down Expand Up @@ -517,8 +513,8 @@ fn populate_tree_nodes(
}
}

fn get_current_dir(nvim: &mut NeovimRef) -> Option<String> {
match nvim.eval("getcwd()") {
fn get_current_dir(nvim: &NvimSession) -> Option<String> {
match nvim.block_timeout(nvim.eval("getcwd()")) {
Ok(cwd) => cwd.as_str().map(|s| s.to_owned()),
Err(err) => {
error!("Couldn't get cwd: {}", err);
Expand Down
2 changes: 1 addition & 1 deletion src/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::rc::Rc;

use fnv::FnvHashMap;

use neovim_lib::Value;
use nvim_rs::Value;

use crate::highlight::{Highlight, HighlightMap};
use crate::ui_model::{ModelRect, ModelRectVec, UiModel};
Expand Down
2 changes: 1 addition & 1 deletion src/highlight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use fnv::FnvHashMap;

use crate::color::*;
use crate::ui_model::Cell;
use neovim_lib::Value;
use nvim_rs::Value;

pub struct HighlightMap {
highlights: FnvHashMap<u64, Rc<Highlight>>,
Expand Down
17 changes: 12 additions & 5 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use gtk::prelude::*;
use gdk;
use gdk::EventKey;
use phf;
use neovim_lib::{Neovim, NeovimApi};
use crate::nvim::{NvimSession, ErrorReport};

include!(concat!(env!("OUT_DIR"), "/key_map_table.rs"));

Expand Down Expand Up @@ -76,7 +76,7 @@ pub fn convert_key(ev: &EventKey) -> Option<String> {
}
}

pub fn im_input(nvim: &mut Neovim, input: &str) {
pub fn im_input(nvim: &NvimSession, input: &str) {
debug!("nvim_input -> {}", input);

let input: String = input
Expand All @@ -85,13 +85,20 @@ pub fn im_input(nvim: &mut Neovim, input: &str) {
keyval_to_input_string(&ch.to_string(), gdk::ModifierType::empty())
})
.collect();
nvim.input(&input).expect("Error run input command to nvim");
nvim
.block_timeout(nvim.input(&input))
.ok_and_report()
.expect("Failed to send input command to nvim");
}

pub fn gtk_key_press(nvim: &mut Neovim, ev: &EventKey) -> Inhibit {
pub fn gtk_key_press(nvim: &NvimSession, ev: &EventKey)
-> Inhibit {
if let Some(input) = convert_key(ev) {
debug!("nvim_input -> {}", input);
nvim.input(&input).expect("Error run input command to nvim");
nvim
.block_timeout(nvim.input(&input))
.ok_and_report()
.expect("Failed to send input command to nvim");
Inhibit(true)
} else {
Inhibit(false)
Expand Down
2 changes: 1 addition & 1 deletion src/mode.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::collections::HashMap;
use neovim_lib::Value;
use nvim_rs::Value;

#[derive(Clone, PartialEq)]
pub enum NvimMode {
Expand Down
148 changes: 12 additions & 136 deletions src/nvim/client.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::cell::{Cell, RefCell, RefMut};
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex, MutexGuard};
use std::{
cell::Cell,
sync::RwLock,
};

use super::ErrorReport;
use neovim_lib::{Neovim, NeovimApi};
use crate::nvim::NvimSession;

#[derive(Clone, Copy, PartialEq)]
enum NeovimClientState {
Expand All @@ -13,129 +13,25 @@ enum NeovimClientState {
Error,
}

pub enum NeovimRef<'a> {
SingleThreaded(RefMut<'a, Neovim>),
MultiThreaded(MutexGuard<'a, Option<Neovim>>),
}

impl<'a> NeovimRef<'a> {
fn from_nvim(nvim: RefMut<'a, Neovim>) -> Self {
NeovimRef::SingleThreaded(nvim)
}

fn try_nvim_async(nvim_async: &'a NeovimClientAsync) -> Option<NeovimRef<'a>> {
let guard = nvim_async.nvim.try_lock();

if let Ok(guard) = guard {
if guard.is_some() {
return Some(NeovimRef::MultiThreaded(guard));
}
}

None
}

fn from_nvim_async(nvim_async: &'a NeovimClientAsync) -> Option<NeovimRef<'a>> {
let guard = nvim_async.nvim.lock().unwrap();

if guard.is_some() {
Some(NeovimRef::MultiThreaded(guard))
} else {
None
}
}

pub fn non_blocked(mut self) -> Option<Self> {
self.get_mode().ok_and_report().and_then(|mode| {
mode.iter()
.find(|kv| kv.0.as_str().map(|key| key == "blocking").unwrap_or(false))
.map(|kv| kv.1.as_bool().unwrap_or(false))
.and_then(|block| if block { None } else { Some(self) })
})
}
}

impl<'a> Deref for NeovimRef<'a> {
type Target = Neovim;

fn deref(&self) -> &Neovim {
match *self {
NeovimRef::SingleThreaded(ref nvim) => &*nvim,
NeovimRef::MultiThreaded(ref nvim) => (&*nvim).as_ref().unwrap(),
}
}
}

impl<'a> DerefMut for NeovimRef<'a> {
fn deref_mut(&mut self) -> &mut Neovim {
match *self {
NeovimRef::SingleThreaded(ref mut nvim) => &mut *nvim,
NeovimRef::MultiThreaded(ref mut nvim) => (&mut *nvim).as_mut().unwrap(),
}
}
}

pub struct NeovimClientAsync {
nvim: Arc<Mutex<Option<Neovim>>>,
}

impl NeovimClientAsync {
fn new() -> Self {
NeovimClientAsync {
nvim: Arc::new(Mutex::new(None)),
}
}

pub fn borrow(&self) -> Option<NeovimRef> {
NeovimRef::from_nvim_async(self)
}

pub fn try_borrow(&self) -> Option<NeovimRef> {
NeovimRef::try_nvim_async(self)
}
}

impl Clone for NeovimClientAsync {
fn clone(&self) -> Self {
NeovimClientAsync {
nvim: self.nvim.clone(),
}
}
}

pub struct NeovimClient {
state: Cell<NeovimClientState>,
nvim: RefCell<Option<Neovim>>,
nvim_async: NeovimClientAsync,
nvim: RwLock<Option<NvimSession>>,
}

impl NeovimClient {
pub fn new() -> Self {
NeovimClient {
state: Cell::new(NeovimClientState::Uninitialized),
nvim: RefCell::new(None),
nvim_async: NeovimClientAsync::new(),
nvim: RwLock::new(None),
}
}

pub fn clear(&self) {
let mut nvim = self.nvim.borrow_mut();
if nvim.is_some() {
nvim.take();
} else {
self.nvim_async.nvim.lock().unwrap().take();
}
*self.nvim.write().unwrap() = None
}

pub fn async_to_sync(&self) {
let mut lock = self.nvim_async.nvim.lock().unwrap();
let nvim = lock.take().unwrap();
*self.nvim.borrow_mut() = Some(nvim);
}

pub fn set_nvim_async(&self, nvim: Neovim) -> NeovimClientAsync {
*self.nvim_async.nvim.lock().unwrap() = Some(nvim);
self.nvim_async.clone()
pub fn set(&self, nvim: NvimSession) {
self.nvim.write().unwrap().replace(nvim);
}

pub fn set_initialized(&self) {
Expand All @@ -162,27 +58,7 @@ impl NeovimClient {
self.state.get() == NeovimClientState::InitInProgress
}

/// In case neovimref locked in another thread
/// this method can return None
pub fn try_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 {
self.nvim_async.try_borrow()
}
}

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 {
self.nvim_async.borrow()
}
pub fn nvim(&self) -> Option<NvimSession> {
self.nvim.read().unwrap().clone()
}
}
Loading

0 comments on commit ca5d183

Please sign in to comment.