From fbf662505997cc6af2e7f8ddb7e8893da622652c Mon Sep 17 00:00:00 2001 From: Greg V Date: Mon, 1 Jan 2018 02:23:37 +0300 Subject: [PATCH] Add RPC/GTK clipboard provider --- runtime/plugin/nvim_gui_shim.vim | 11 ++++++- src/nvim/handler.rs | 54 ++++++++++++++++++++++++++++++-- src/nvim/redraw_handler.rs | 40 +++++++++++++++++++++++ src/shell.rs | 6 ++++ 4 files changed, 107 insertions(+), 4 deletions(-) diff --git a/runtime/plugin/nvim_gui_shim.vim b/runtime/plugin/nvim_gui_shim.vim index 8e48dbe..5cec3cb 100644 --- a/runtime/plugin/nvim_gui_shim.vim +++ b/runtime/plugin/nvim_gui_shim.vim @@ -4,6 +4,16 @@ if !has('nvim') || exists('g:GuiLoaded') endif let g:GuiLoaded = 1 +if !exists('g:GuiExternalClipboard') + function! provider#clipboard#Call(method, args) abort + if a:method == 'get' + return [rpcrequest(1, 'Gui', 'Clipboard', 'Get'), 'v'] + elseif a:method == 'set' + call rpcnotify(1, 'Gui', 'Clipboard', 'Set', join(a:args[0], ' ')) + endif + endfunction +endif + " Set GUI font function! GuiFont(fname, ...) abort call rpcnotify(1, 'Gui', 'Font', s:NvimQtToPangoFont(a:fname)) @@ -42,4 +52,3 @@ function s:GuiFontCommand(fname, bang) abort endfunction command! -nargs=? -bang Guifont call s:GuiFontCommand("", "") command! -nargs=? -bang GuiFont call s:GuiFontCommand("", "") - diff --git a/src/nvim/handler.rs b/src/nvim/handler.rs index 4128617..fe25085 100644 --- a/src/nvim/handler.rs +++ b/src/nvim/handler.rs @@ -1,5 +1,5 @@ use std::result; -use std::sync::Arc; +use std::sync::{Arc, mpsc}; use neovim_lib::{Handler, Value}; @@ -24,6 +24,7 @@ impl NvimHandler { match method { "redraw" => { self.safe_call(move |ui| { + let ui = &mut ui.borrow_mut(); let mut repaint_mode = RepaintMode::Nothing; for ev in params { @@ -63,6 +64,7 @@ impl NvimHandler { if let Value::String(ev_name) = ev_name { let args = params_iter.collect(); self.safe_call(move |ui| { + let ui = &mut ui.borrow_mut(); redraw_handler::call_gui_event( ui, ev_name.as_str().ok_or_else(|| "Event name does not exists")?, @@ -87,14 +89,56 @@ impl NvimHandler { } } + fn nvim_cb_req (&self, method: &str, params: Vec) -> result::Result { + match method { + "Gui" => { + if !params.is_empty() { + let mut params_iter = params.into_iter(); + if let Some(req_name) = params_iter.next() { + if let Value::String(req_name) = req_name { + let args = params_iter.collect(); + let (sender, receiver) = mpsc::channel(); + self.safe_call(move |ui| { + sender.send(redraw_handler::call_gui_request( + &ui.clone(), + req_name.as_str().ok_or_else(|| "Event name does not exists")?, + &args, + )).unwrap(); + { + let ui = &mut ui.borrow_mut(); + ui.on_redraw(&RepaintMode::All); + } + Ok(()) + }); + Ok(receiver.recv().unwrap()?) + } else { + error!("Unsupported request"); + Err(Value::Nil) + } + } else { + error!("Request name does not exist"); + Err(Value::Nil) + } + } else { + error!("Unsupported request {:?}", params); + Err(Value::Nil) + } + }, + _ => { + error!("Request {}({:?})", method, params); + Err(Value::Nil) + } + } + } + fn safe_call(&self, cb: F) where - F: FnOnce(&mut shell::State) -> result::Result<(), String> + 'static + Send, + F: FnOnce(&Arc>) -> result::Result<(), String> + 'static + Send, { let mut cb = Some(cb); let shell = self.shell.clone(); glib::idle_add(move || { - if let Err(msg) = cb.take().unwrap()(&mut shell.borrow_mut()) { + if let Err(msg) = cb.take().unwrap()(&shell) { error!("Error call function: {}", msg); } glib::Continue(false) @@ -106,4 +150,8 @@ impl Handler for NvimHandler { fn handle_notify(&mut self, name: &str, args: Vec) { self.nvim_cb(name, args); } + + fn handle_request(&mut self, name: &str, args: Vec) -> result::Result { + self.nvim_cb_req(name, args) + } } diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index 095fbea..cc452da 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -1,9 +1,12 @@ use std::result; +use std::sync::Arc; use neovim_lib::{Value, UiOption}; use neovim_lib::neovim_api::Tabpage; +use ui::UiMutex; use shell; +use gtk::ClipboardExt; use value::ValueMapExt; @@ -111,6 +114,14 @@ pub fn call_gui_event( ) -> result::Result<(), String> { match method { "Font" => ui.set_font(try_str!(args[0])), + "Clipboard" => { + match try_str!(args[0]) { + "Set" => { + ui.clipboard_set(try_str!(args[1])); + }, + opt => error!("Unknown option {}", opt), + } + }, "Option" => { match try_str!(args[0]) { "Popupmenu" => { @@ -137,6 +148,35 @@ pub fn call_gui_event( Ok(()) } +pub fn call_gui_request( + ui: &Arc>, + method: &str, + args: &Vec, +) -> result::Result { + match method { + "Clipboard" => { + match try_str!(args[0]) { + "Get" => { + // NOTE: wait_for_text waits on the main loop. We can't have the ui borrowed + // while it runs, otherwise ui callbacks will get called and try to borrow + // mutably twice! + let clipboard = { + let ui = &mut ui.borrow_mut(); + ui.clipboard.clone() + }; + let t = clipboard.wait_for_text().unwrap_or_else(|| String::new()); + Ok(Value::Array(t.split("\n").map(|s| s.into()).collect::>())) + }, + opt => { + error!("Unknown option {}", opt); + Err(Value::Nil) + }, + } + }, + _ => Err(Value::String(format!("Unsupported request {}({:?})", method, args).into())), + } +} + pub fn call( ui: &mut shell::State, method: &str, diff --git a/src/shell.rs b/src/shell.rs index 57c7f5a..78aeb11 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -80,6 +80,7 @@ pub struct State { cursor: Option, popup_menu: RefCell, settings: Rc>, + pub clipboard: gtk::Clipboard, pub mode: mode::Mode, @@ -113,6 +114,7 @@ impl State { cursor: None, popup_menu, settings, + clipboard: gtk::Clipboard::get(&gdk::Atom::intern("CLIPBOARD")), mode: mode::Mode::new(), @@ -201,6 +203,10 @@ impl State { } } + pub fn clipboard_set(&self, text: &str) { + self.clipboard.set_text(text); + } + fn close_popup_menu(&self) { if self.popup_menu.borrow().is_open() { if let Some(mut nvim) = self.nvim() {