diff --git a/src/error.rs b/src/error.rs
index ccda4bb..de58f88 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -28,6 +28,19 @@ impl ErrorArea {
ErrorArea { base, label }
}
+ pub fn show_nvim_init_error(&self, err: &str) {
+ error!("Can't initialize nvim: {}", err);
+ self.label.set_markup(&format!("Can't initialize nvim:\n\
+ {}\n\n\
+ Possible error reasons:\n\
+ ● Not supported nvim version (minimum supported version is {})\n\
+ ● Error in configuration file (init.vim or ginit.vim)\n\
+ ● Wrong nvim binary path \
+ (right path can be passed with --nvim-bin-path=path_here)",
+ encode_minimal(err), shell::MINIMUM_SUPPORTED_NVIM_VERSION));
+ self.base.show_all();
+ }
+
pub fn show_nvim_start_error(&self, err: &str, cmd: &str) {
error!("Can't start nvim: {}\nCommand line: {}", err, cmd);
self.label.set_markup(&format!("Can't start nvim instance:\n\
diff --git a/src/nvim.rs b/src/nvim.rs
index eabcae5..93e5470 100644
--- a/src/nvim.rs
+++ b/src/nvim.rs
@@ -172,15 +172,24 @@ impl ModeInfo {
#[derive(Debug)]
pub struct NvimInitError {
source: Box,
- cmd: String,
+ cmd: Option,
}
impl NvimInitError {
+ pub fn new_post_init(error: E) -> NvimInitError
+ where E: Into>
+ {
+ NvimInitError {
+ cmd: None,
+ source: error.into(),
+ }
+ }
+
pub fn new(cmd: &Command, error: E) -> NvimInitError
where E: Into>
{
NvimInitError {
- cmd: format!("{:?}", cmd),
+ cmd: Some(format!("{:?}", cmd)),
source: error.into(),
}
}
@@ -189,8 +198,8 @@ impl NvimInitError {
format!("{}", self.source)
}
- pub fn cmd(&self) -> &str {
- &self.cmd
+ pub fn cmd(&self) -> Option<&String> {
+ self.cmd.as_ref()
}
}
@@ -210,11 +219,9 @@ impl error::Error for NvimInitError {
}
}
-pub fn initialize(shell: Arc>,
- nvim_bin_path: Option<&String>,
- cols: u64,
- rows: u64)
- -> result::Result {
+pub fn start(shell: Arc>,
+ nvim_bin_path: Option<&String>)
+ -> result::Result {
let mut cmd = if let Some(path) = nvim_bin_path {
Command::new(path)
} else {
@@ -250,15 +257,29 @@ pub fn initialize(shell: Arc>,
nvim.session
.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();
opts.set_popupmenu_external(false);
opts.set_tabline_external(true);
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")
- .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 {
@@ -505,32 +526,41 @@ impl RepaintMode {
}
-enum NeovimClientWrapper {
+enum NeovimClientState {
Uninitialized,
+ InitInProgress,
Initialized(Neovim),
Error,
}
-impl NeovimClientWrapper {
- pub fn is_initialized(&self) -> bool {
+impl NeovimClientState {
+ pub fn is_initializing(&self) -> bool {
match *self {
- NeovimClientWrapper::Initialized(_) => true,
+ NeovimClientState::InitInProgress => true,
_ => false,
}
}
- pub fn is_error(&self) -> bool {
+ pub fn is_uninitialized(&self) -> bool {
match *self {
- NeovimClientWrapper::Error => true,
+ NeovimClientState::Uninitialized => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_initialized(&self) -> bool {
+ match *self {
+ NeovimClientState::Initialized(_) => true,
_ => false,
}
}
pub fn nvim(&self) -> &Neovim {
match *self {
- NeovimClientWrapper::Initialized(ref nvim) => nvim,
- NeovimClientWrapper::Uninitialized => panic!("Access to uninitialized neovim client"),
- NeovimClientWrapper::Error => {
+ 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")
}
}
@@ -538,9 +568,10 @@ impl NeovimClientWrapper {
pub fn nvim_mut(&mut self) -> &mut Neovim {
match *self {
- NeovimClientWrapper::Initialized(ref mut nvim) => nvim,
- NeovimClientWrapper::Uninitialized => panic!("Access to uninitialized neovim client"),
- NeovimClientWrapper::Error => {
+ 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")
}
}
@@ -548,36 +579,44 @@ impl NeovimClientWrapper {
}
pub struct NeovimClient {
- nvim: NeovimClientWrapper,
+ state: NeovimClientState,
}
impl NeovimClient {
pub fn new() -> Self {
- NeovimClient { nvim: NeovimClientWrapper::Uninitialized }
+ NeovimClient { state: NeovimClientState::Uninitialized }
}
- pub fn set_nvim(&mut self, nvim: Neovim) {
- self.nvim = NeovimClientWrapper::Initialized(nvim);
+ pub fn set_initialized(&mut self, nvim: Neovim) {
+ self.state = NeovimClientState::Initialized(nvim);
}
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 {
- self.nvim.is_initialized()
+ self.state.is_initialized()
}
- pub fn is_error(&self) -> bool {
- self.nvim.is_error()
+ pub fn is_uninitialized(&self) -> bool {
+ self.state.is_uninitialized()
+ }
+
+ pub fn is_initializing(&self) -> bool {
+ self.state.is_initializing()
}
pub fn nvim(&self) -> &Neovim {
- self.nvim.nvim()
+ self.state.nvim()
}
pub fn nvim_mut(&mut self) -> &mut Neovim {
- self.nvim.nvim_mut()
+ self.state.nvim_mut()
}
}
diff --git a/src/settings.rs b/src/settings.rs
index 1e64cfa..679f9fb 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -7,6 +7,8 @@ use nvim::RepaintMode;
use shell::Shell;
#[cfg(unix)]
use gio;
+#[cfg(unix)]
+use gio::SettingsExt;
#[derive(PartialEq)]
pub enum FontSource {
diff --git a/src/shell.rs b/src/shell.rs
index 837948e..2d55e25 100644
--- a/src/shell.rs
+++ b/src/shell.rs
@@ -9,7 +9,7 @@ use pangocairo::CairoContextExt;
use pango;
use pango::{LayoutExt, FontDescription};
use gdk;
-use gdk::{ModifierType, EventConfigure, EventButton, EventMotion, EventType, EventScroll};
+use gdk::{ModifierType, EventButton, EventMotion, EventType, EventScroll};
use gdk_sys;
use glib;
use gtk;
@@ -70,6 +70,7 @@ pub struct State {
line_height: Option,
char_width: Option,
request_resize: bool,
+ request_nvim_resize: bool,
resize_timer: Option,
options: ShellOptions,
@@ -108,6 +109,7 @@ impl State {
char_width: None,
resize_timer: None,
request_resize: false,
+ request_nvim_resize: false,
options,
@@ -188,6 +190,10 @@ impl State {
self.request_resize = true;
}
+ fn request_nvim_resize(&mut self) {
+ self.request_nvim_resize = true;
+ }
+
fn close_popup_menu(&self) {
if self.popup_menu.borrow().is_open() {
let mut nvim = self.nvim();
@@ -245,6 +251,14 @@ impl State {
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 {
@@ -257,6 +271,7 @@ impl UiState {
}
}
+#[derive(Clone)]
pub struct ShellOptions {
nvim_bin_path: Option,
open_path: Option,
@@ -300,7 +315,7 @@ impl Shell {
}
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_vexpand(true);
state.drawing_area.set_can_focus(true);
@@ -421,9 +436,10 @@ impl Shell {
let ref_state = self.state.clone();
state
.drawing_area
- .connect_configure_event(move |_, ev| gtk_configure_event(&ref_state, ev));
-
- state.cursor.as_mut().unwrap().start();
+ .connect_configure_event(move |_, _| {
+ try_nvim_resize(&ref_state);
+ false
+ });
}
#[cfg(unix)]
@@ -580,60 +596,104 @@ fn update_line_metrics(state_arc: &Arc>, ctx: &cairo::Context) {
fn gtk_draw(state_arc: &Arc>, ctx: &cairo::Context) -> Inhibit {
update_line_metrics(state_arc, ctx);
+
+ if state_arc.borrow_mut().request_nvim_resize {
+ try_nvim_resize(state_arc);
+ }
+
init_nvim(state_arc);
let mut state = state_arc.borrow_mut();
- // in case nvim not initialized
- if !state.nvim.borrow().is_error() {
+ if state.nvim.borrow().is_initialized() {
draw(&*state, ctx);
request_window_resize(&mut *state);
+ } else if state.nvim.borrow().is_initializing() {
+ draw_initializing(&*state, ctx);
}
Inhibit(false)
}
+fn show_nvim_start_error(err: nvim::NvimInitError, state_arc: Arc>) {
+ 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>) {
+ 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>,
+ 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>) {
let state = state_arc.borrow();
- let mut nvim_client = state.nvim.borrow_mut();
- if !nvim_client.is_initialized() && !nvim_client.is_error() {
+ let mut nvim = state.nvim.borrow_mut();
+ if nvim.is_uninitialized() {
+ nvim.set_in_progress();
+
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();
- gtk::idle_add(move || {
- stack.set_visible_child_name("Error");
- 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);
+ let state_arc = state_arc.clone();
+ let options = state.options.clone();
+ thread::spawn(move || init_nvim_async(state_arc, options, cols, rows));
}
}
@@ -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) {
let layout = ctx.create_pango_layout();
let mut desc = state.create_pango_font();
@@ -754,7 +851,7 @@ fn draw(state: &State, ctx: &cairo::Context) {
if !cell.ch.is_whitespace() {
update_font_description(&mut desc, &cell.attrs);
- layout.set_font_description(Some(&desc));
+ layout.set_font_description(&desc);
buf.clear();
buf.push(cell.ch);
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)
}
-fn gtk_configure_event(state: &Arc>, _: &EventConfigure) -> bool {
+fn try_nvim_resize(state: &Arc>) {
let mut state_ref = state.borrow_mut();
+ state_ref.request_nvim_resize = false;
+
if let Some(timer) = state_ref.resize_timer {
glib::source_remove(timer);
}
if !state_ref.nvim.borrow().is_initialized() {
- return false;
+ return;
}
if let Some((columns, rows)) = state_ref.calc_nvim_size() {
@@ -879,13 +978,12 @@ fn gtk_configure_event(state: &Arc>, _: &EventConfigure) -> bool
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) {
- println!("Error trying resize nvim {}", err);
+ error!("Error trying resize nvim {}", err);
}
}
Continue(false)
}));
}
- false
}
impl RedrawEvents for State {
@@ -1066,7 +1164,7 @@ impl RedrawEvents for State {
impl GuiApi for State {
fn set_font(&mut self, font_desc: &str) {
self.set_font_desc(font_desc);
- self.request_resize();
+ self.request_nvim_resize();
let mut settings = self.settings.borrow_mut();
settings.set_font_source(FontSource::Rpc);