Merge branch 'master' into imcontext

This commit is contained in:
daa 2017-07-22 23:32:13 +03:00
commit 808b40d8a8
4 changed files with 235 additions and 83 deletions

View File

@ -28,6 +28,19 @@ impl ErrorArea {
ErrorArea { base, label } ErrorArea { base, label }
} }
pub fn show_nvim_init_error(&self, err: &str) {
error!("Can't initialize nvim: {}", err);
self.label.set_markup(&format!("<big>Can't initialize nvim:</big>\n\
<span foreground=\"red\"><i>{}</i></span>\n\n\
<big>Possible error reasons:</big>\n\
&#9679; Not supported nvim version (minimum supported version is <b>{}</b>)\n\
&#9679; Error in configuration file (init.vim or ginit.vim)\n\
&#9679; Wrong nvim binary path \
(right path can be passed with <i>--nvim-bin-path=path_here</i>)",
encode_minimal(err), shell::MINIMUM_SUPPORTED_NVIM_VERSION));
self.base.show_all();
}
pub fn show_nvim_start_error(&self, err: &str, cmd: &str) { pub fn show_nvim_start_error(&self, err: &str, cmd: &str) {
error!("Can't start nvim: {}\nCommand line: {}", err, cmd); error!("Can't start nvim: {}\nCommand line: {}", err, cmd);
self.label.set_markup(&format!("<big>Can't start nvim instance:</big>\n\ self.label.set_markup(&format!("<big>Can't start nvim instance:</big>\n\

View File

@ -172,15 +172,24 @@ impl ModeInfo {
#[derive(Debug)] #[derive(Debug)]
pub struct NvimInitError { pub struct NvimInitError {
source: Box<error::Error>, source: Box<error::Error>,
cmd: String, cmd: Option<String>,
} }
impl NvimInitError { impl NvimInitError {
pub fn new_post_init<E>(error: E) -> NvimInitError
where E: Into<Box<error::Error>>
{
NvimInitError {
cmd: None,
source: error.into(),
}
}
pub fn new<E>(cmd: &Command, error: E) -> NvimInitError pub fn new<E>(cmd: &Command, error: E) -> NvimInitError
where E: Into<Box<error::Error>> where E: Into<Box<error::Error>>
{ {
NvimInitError { NvimInitError {
cmd: format!("{:?}", cmd), cmd: Some(format!("{:?}", cmd)),
source: error.into(), source: error.into(),
} }
} }
@ -189,8 +198,8 @@ impl NvimInitError {
format!("{}", self.source) format!("{}", self.source)
} }
pub fn cmd(&self) -> &str { pub fn cmd(&self) -> Option<&String> {
&self.cmd self.cmd.as_ref()
} }
} }
@ -210,11 +219,9 @@ impl error::Error for NvimInitError {
} }
} }
pub fn initialize(shell: Arc<UiMutex<shell::State>>, pub fn start(shell: Arc<UiMutex<shell::State>>,
nvim_bin_path: Option<&String>, nvim_bin_path: Option<&String>)
cols: u64, -> result::Result<Neovim, NvimInitError> {
rows: u64)
-> result::Result<Neovim, NvimInitError> {
let mut cmd = if let Some(path) = nvim_bin_path { let mut cmd = if let Some(path) = nvim_bin_path {
Command::new(path) Command::new(path)
} else { } else {
@ -250,15 +257,29 @@ pub fn initialize(shell: Arc<UiMutex<shell::State>>,
nvim.session nvim.session
.start_event_loop_handler(NvimHandler::new(shell)); .start_event_loop_handler(NvimHandler::new(shell));
Ok(nvim)
}
pub fn post_start_init(nvim: &mut Neovim,
open_path: Option<&String>,
cols: u64,
rows: u64)
-> result::Result<(), NvimInitError> {
let mut opts = UiAttachOptions::new(); let mut opts = UiAttachOptions::new();
opts.set_popupmenu_external(false); opts.set_popupmenu_external(false);
opts.set_tabline_external(true); opts.set_tabline_external(true);
nvim.ui_attach(cols, rows, opts) nvim.ui_attach(cols, rows, opts)
.map_err(|e| NvimInitError::new(&cmd, e))?; .map_err(|e| NvimInitError::new_post_init(e))?;
nvim.command("runtime! ginit.vim") nvim.command("runtime! ginit.vim")
.map_err(|e| NvimInitError::new(&cmd, e))?; .map_err(|e| NvimInitError::new_post_init(e))?;
Ok(nvim) if let Some(path) = open_path {
nvim.command(&format!("e {}", path))
.map_err(|e| NvimInitError::new_post_init(e))?;
}
Ok(())
} }
pub struct NvimHandler { pub struct NvimHandler {
@ -505,32 +526,41 @@ impl RepaintMode {
} }
enum NeovimClientWrapper { enum NeovimClientState {
Uninitialized, Uninitialized,
InitInProgress,
Initialized(Neovim), Initialized(Neovim),
Error, Error,
} }
impl NeovimClientWrapper { impl NeovimClientState {
pub fn is_initialized(&self) -> bool { pub fn is_initializing(&self) -> bool {
match *self { match *self {
NeovimClientWrapper::Initialized(_) => true, NeovimClientState::InitInProgress => true,
_ => false, _ => false,
} }
} }
pub fn is_error(&self) -> bool { pub fn is_uninitialized(&self) -> bool {
match *self { match *self {
NeovimClientWrapper::Error => true, NeovimClientState::Uninitialized => true,
_ => false,
}
}
pub fn is_initialized(&self) -> bool {
match *self {
NeovimClientState::Initialized(_) => true,
_ => false, _ => false,
} }
} }
pub fn nvim(&self) -> &Neovim { pub fn nvim(&self) -> &Neovim {
match *self { match *self {
NeovimClientWrapper::Initialized(ref nvim) => nvim, NeovimClientState::Initialized(ref nvim) => nvim,
NeovimClientWrapper::Uninitialized => panic!("Access to uninitialized neovim client"), NeovimClientState::InitInProgress |
NeovimClientWrapper::Error => { NeovimClientState::Uninitialized => panic!("Access to uninitialized neovim client"),
NeovimClientState::Error => {
panic!("Access to neovim client that is not started due to some error") panic!("Access to neovim client that is not started due to some error")
} }
} }
@ -538,9 +568,10 @@ impl NeovimClientWrapper {
pub fn nvim_mut(&mut self) -> &mut Neovim { pub fn nvim_mut(&mut self) -> &mut Neovim {
match *self { match *self {
NeovimClientWrapper::Initialized(ref mut nvim) => nvim, NeovimClientState::Initialized(ref mut nvim) => nvim,
NeovimClientWrapper::Uninitialized => panic!("Access to uninitialized neovim client"), NeovimClientState::InitInProgress |
NeovimClientWrapper::Error => { NeovimClientState::Uninitialized => panic!("Access to uninitialized neovim client"),
NeovimClientState::Error => {
panic!("Access to neovim client that is not started due to some error") panic!("Access to neovim client that is not started due to some error")
} }
} }
@ -548,36 +579,44 @@ impl NeovimClientWrapper {
} }
pub struct NeovimClient { pub struct NeovimClient {
nvim: NeovimClientWrapper, state: NeovimClientState,
} }
impl NeovimClient { impl NeovimClient {
pub fn new() -> Self { pub fn new() -> Self {
NeovimClient { nvim: NeovimClientWrapper::Uninitialized } NeovimClient { state: NeovimClientState::Uninitialized }
} }
pub fn set_nvim(&mut self, nvim: Neovim) { pub fn set_initialized(&mut self, nvim: Neovim) {
self.nvim = NeovimClientWrapper::Initialized(nvim); self.state = NeovimClientState::Initialized(nvim);
} }
pub fn set_error(&mut self) { pub fn set_error(&mut self) {
self.nvim = NeovimClientWrapper::Error; self.state = NeovimClientState::Error;
}
pub fn set_in_progress(&mut self) {
self.state = NeovimClientState::InitInProgress;
} }
pub fn is_initialized(&self) -> bool { pub fn is_initialized(&self) -> bool {
self.nvim.is_initialized() self.state.is_initialized()
} }
pub fn is_error(&self) -> bool { pub fn is_uninitialized(&self) -> bool {
self.nvim.is_error() self.state.is_uninitialized()
}
pub fn is_initializing(&self) -> bool {
self.state.is_initializing()
} }
pub fn nvim(&self) -> &Neovim { pub fn nvim(&self) -> &Neovim {
self.nvim.nvim() self.state.nvim()
} }
pub fn nvim_mut(&mut self) -> &mut Neovim { pub fn nvim_mut(&mut self) -> &mut Neovim {
self.nvim.nvim_mut() self.state.nvim_mut()
} }
} }

View File

@ -7,6 +7,8 @@ use nvim::RepaintMode;
use shell::Shell; use shell::Shell;
#[cfg(unix)] #[cfg(unix)]
use gio; use gio;
#[cfg(unix)]
use gio::SettingsExt;
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum FontSource { pub enum FontSource {

View File

@ -9,7 +9,7 @@ use pangocairo::CairoContextExt;
use pango; use pango;
use pango::{LayoutExt, FontDescription}; use pango::{LayoutExt, FontDescription};
use gdk; use gdk;
use gdk::{ModifierType, EventConfigure, EventButton, EventMotion, EventType, EventScroll}; use gdk::{ModifierType, EventButton, EventMotion, EventType, EventScroll};
use gdk_sys; use gdk_sys;
use glib; use glib;
use gtk; use gtk;
@ -70,6 +70,7 @@ pub struct State {
line_height: Option<f64>, line_height: Option<f64>,
char_width: Option<f64>, char_width: Option<f64>,
request_resize: bool, request_resize: bool,
request_nvim_resize: bool,
resize_timer: Option<glib::SourceId>, resize_timer: Option<glib::SourceId>,
options: ShellOptions, options: ShellOptions,
@ -108,6 +109,7 @@ impl State {
char_width: None, char_width: None,
resize_timer: None, resize_timer: None,
request_resize: false, request_resize: false,
request_nvim_resize: false,
options, options,
@ -188,6 +190,10 @@ impl State {
self.request_resize = true; self.request_resize = true;
} }
fn request_nvim_resize(&mut self) {
self.request_nvim_resize = true;
}
fn close_popup_menu(&self) { fn close_popup_menu(&self) {
if self.popup_menu.borrow().is_open() { if self.popup_menu.borrow().is_open() {
let mut nvim = self.nvim(); let mut nvim = self.nvim();
@ -245,6 +251,14 @@ impl State {
None None
} }
fn show_error_area(&self) {
let stack = self.stack.clone();
gtk::idle_add(move || {
stack.set_visible_child_name("Error");
Continue(false)
});
}
} }
pub struct UiState { pub struct UiState {
@ -257,6 +271,7 @@ impl UiState {
} }
} }
#[derive(Clone)]
pub struct ShellOptions { pub struct ShellOptions {
nvim_bin_path: Option<String>, nvim_bin_path: Option<String>,
open_path: Option<String>, open_path: Option<String>,
@ -300,7 +315,7 @@ impl Shell {
} }
pub fn init(&mut self) { pub fn init(&mut self) {
let mut state = self.state.borrow_mut(); let state = self.state.borrow();
state.drawing_area.set_hexpand(true); state.drawing_area.set_hexpand(true);
state.drawing_area.set_vexpand(true); state.drawing_area.set_vexpand(true);
state.drawing_area.set_can_focus(true); state.drawing_area.set_can_focus(true);
@ -421,9 +436,10 @@ impl Shell {
let ref_state = self.state.clone(); let ref_state = self.state.clone();
state state
.drawing_area .drawing_area
.connect_configure_event(move |_, ev| gtk_configure_event(&ref_state, ev)); .connect_configure_event(move |_, _| {
try_nvim_resize(&ref_state);
state.cursor.as_mut().unwrap().start(); false
});
} }
#[cfg(unix)] #[cfg(unix)]
@ -580,60 +596,104 @@ fn update_line_metrics(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) {
fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit { fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit {
update_line_metrics(state_arc, ctx); update_line_metrics(state_arc, ctx);
if state_arc.borrow_mut().request_nvim_resize {
try_nvim_resize(state_arc);
}
init_nvim(state_arc); init_nvim(state_arc);
let mut state = state_arc.borrow_mut(); let mut state = state_arc.borrow_mut();
// in case nvim not initialized if state.nvim.borrow().is_initialized() {
if !state.nvim.borrow().is_error() {
draw(&*state, ctx); draw(&*state, ctx);
request_window_resize(&mut *state); request_window_resize(&mut *state);
} else if state.nvim.borrow().is_initializing() {
draw_initializing(&*state, ctx);
} }
Inhibit(false) Inhibit(false)
} }
fn show_nvim_start_error(err: nvim::NvimInitError, state_arc: Arc<UiMutex<State>>) {
let source = err.source();
let cmd = err.cmd().unwrap().to_owned();
glib::idle_add(move || {
let state = state_arc.borrow();
state.nvim.borrow_mut().set_error();
state.error_area.show_nvim_start_error(&source, &cmd);
state.show_error_area();
Continue(false)
});
}
fn show_nvim_init_error(err: nvim::NvimInitError, state_arc: Arc<UiMutex<State>>) {
let source = err.source();
glib::idle_add(move || {
let state = state_arc.borrow();
state.nvim.borrow_mut().set_error();
state.error_area.show_nvim_init_error(&source);
state.show_error_area();
Continue(false)
});
}
fn init_nvim_async(state_arc: Arc<UiMutex<State>>,
options: ShellOptions,
cols: usize,
rows: usize) {
// execute nvim
let mut 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);
return;
}
};
// add callback on session end
let guard = nvim.session.take_dispatch_guard();
let state_ref = state_arc.clone();
thread::spawn(move || {
guard.join().expect("Can't join dispatch thread");
idle_cb_call!(state_ref.detach_cb());
});
// attach ui
let mut nvim = Some(nvim);
glib::idle_add(move || {
let mut nvim = nvim.take().unwrap();
if let Err(err) = nvim::post_start_init(&mut nvim,
options.open_path.as_ref(),
cols as u64,
rows as u64) {
show_nvim_init_error(err, state_arc.clone());
} else {
let mut state = state_arc.borrow_mut();
state.nvim.borrow_mut().set_initialized(nvim);
state.cursor.as_mut().unwrap().start();
}
Continue(false)
});
}
fn init_nvim(state_arc: &Arc<UiMutex<State>>) { fn init_nvim(state_arc: &Arc<UiMutex<State>>) {
let state = state_arc.borrow(); let state = state_arc.borrow();
let mut nvim_client = state.nvim.borrow_mut(); let mut nvim = state.nvim.borrow_mut();
if !nvim_client.is_initialized() && !nvim_client.is_error() { if nvim.is_uninitialized() {
nvim.set_in_progress();
let (cols, rows) = state.calc_nvim_size().unwrap(); let (cols, rows) = state.calc_nvim_size().unwrap();
let mut nvim = match nvim::initialize(state_arc.clone(),
state.options.nvim_bin_path.as_ref(),
cols as u64,
rows as u64) {
Ok(nvim) => nvim,
Err(err) => {
nvim_client.set_error();
state
.error_area
.show_nvim_start_error(&err.source(), err.cmd());
let stack = state.stack.clone(); let state_arc = state_arc.clone();
gtk::idle_add(move || { let options = state.options.clone();
stack.set_visible_child_name("Error"); thread::spawn(move || init_nvim_async(state_arc, options, cols, rows));
Continue(false)
});
return;
}
};
if let Some(ref path) = state.options.open_path {
nvim.command(&format!("e {}", path)).report_err(&mut nvim);
}
let guard = nvim.session.take_dispatch_guard();
let state_ref = state_arc.clone();
thread::spawn(move || {
guard.join().expect("Can't join dispatch thread");
idle_cb_call!(state_ref.detach_cb());
});
nvim_client.set_nvim(nvim);
} }
} }
@ -688,6 +748,43 @@ fn draw_backgound(state: &State,
} }
} }
fn draw_initializing(state: &State, ctx: &cairo::Context) {
let layout = ctx.create_pango_layout();
let desc = state.create_pango_font();
let alloc = state.drawing_area.get_allocation();
let line_height = state.line_height.unwrap();
let char_width = state.char_width.unwrap();
ctx.set_source_rgb(state.bg_color.0, state.bg_color.1, state.bg_color.2);
ctx.paint();
layout.set_font_description(&desc);
layout.set_text("Loading..", -1);
let (width, height) = layout.get_pixel_size();
let x = alloc.width as f64 / 2.0 - width as f64 / 2.0;
let y = alloc.height as f64 / 2.0 - height as f64 / 2.0;
ctx.move_to(x, y);
ctx.set_source_rgb(state.fg_color.0, state.fg_color.1, state.fg_color.2);
ctx.update_pango_layout(&layout);
ctx.show_pango_layout(&layout);
ctx.move_to(x + width as f64, y);
state
.cursor
.as_ref()
.unwrap()
.draw(ctx,
state,
char_width,
line_height,
y,
false,
&state.bg_color);
}
fn draw(state: &State, ctx: &cairo::Context) { fn draw(state: &State, ctx: &cairo::Context) {
let layout = ctx.create_pango_layout(); let layout = ctx.create_pango_layout();
let mut desc = state.create_pango_font(); let mut desc = state.create_pango_font();
@ -754,7 +851,7 @@ fn draw(state: &State, ctx: &cairo::Context) {
if !cell.ch.is_whitespace() { if !cell.ch.is_whitespace() {
update_font_description(&mut desc, &cell.attrs); update_font_description(&mut desc, &cell.attrs);
layout.set_font_description(Some(&desc)); layout.set_font_description(&desc);
buf.clear(); buf.clear();
buf.push(cell.ch); buf.push(cell.ch);
layout.set_text(&buf, -1); layout.set_text(&buf, -1);
@ -859,15 +956,17 @@ fn split_color(indexed_color: u64) -> Color {
Color(r / 255.0, g / 255.0, b / 255.0) Color(r / 255.0, g / 255.0, b / 255.0)
} }
fn gtk_configure_event(state: &Arc<UiMutex<State>>, _: &EventConfigure) -> bool { fn try_nvim_resize(state: &Arc<UiMutex<State>>) {
let mut state_ref = state.borrow_mut(); let mut state_ref = state.borrow_mut();
state_ref.request_nvim_resize = false;
if let Some(timer) = state_ref.resize_timer { if let Some(timer) = state_ref.resize_timer {
glib::source_remove(timer); glib::source_remove(timer);
} }
if !state_ref.nvim.borrow().is_initialized() { if !state_ref.nvim.borrow().is_initialized() {
return false; return;
} }
if let Some((columns, rows)) = state_ref.calc_nvim_size() { if let Some((columns, rows)) = state_ref.calc_nvim_size() {
@ -879,13 +978,12 @@ fn gtk_configure_event(state: &Arc<UiMutex<State>>, _: &EventConfigure) -> bool
if state_ref.model.rows != rows || state_ref.model.columns != columns { if state_ref.model.rows != rows || state_ref.model.columns != columns {
if let Err(err) = state_ref.nvim().ui_try_resize(columns as u64, rows as u64) { if let Err(err) = state_ref.nvim().ui_try_resize(columns as u64, rows as u64) {
println!("Error trying resize nvim {}", err); error!("Error trying resize nvim {}", err);
} }
} }
Continue(false) Continue(false)
})); }));
} }
false
} }
impl RedrawEvents for State { impl RedrawEvents for State {
@ -1066,7 +1164,7 @@ impl RedrawEvents for State {
impl GuiApi for State { impl GuiApi for State {
fn set_font(&mut self, font_desc: &str) { fn set_font(&mut self, font_desc: &str) {
self.set_font_desc(font_desc); self.set_font_desc(font_desc);
self.request_resize(); self.request_nvim_resize();
let mut settings = self.settings.borrow_mut(); let mut settings = self.settings.borrow_mut();
settings.set_font_source(FontSource::Rpc); settings.set_font_source(FontSource::Rpc);