minor refactoring (should startup faster) and handle exit codes properly

macos-click-through
Keith Simmons 3 years ago
parent 1b6878d072
commit 557e988c09

@ -10,6 +10,7 @@ use crate::{
editor::EditorCommand, editor::EditorCommand,
error_handling::ResultPanicExplanation, error_handling::ResultPanicExplanation,
event_aggregator::EVENT_AGGREGATOR, event_aggregator::EVENT_AGGREGATOR,
running_tracker::*,
settings::SETTINGS, settings::SETTINGS,
}; };
@ -48,6 +49,12 @@ impl Handler for NeovimHandler {
"setting_changed" => { "setting_changed" => {
SETTINGS.handle_changed_notification(arguments); SETTINGS.handle_changed_notification(arguments);
} }
"neovide.quit" => {
let error_code = arguments[0]
.as_i64()
.expect("Could not parse error code from neovim");
RUNNING_TRACKER.quit_with_code(error_code as i32, "Quit from neovim");
}
#[cfg(windows)] #[cfg(windows)]
"neovide.register_right_click" => { "neovide.register_right_click" => {
EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::RegisterRightClick)); EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::RegisterRightClick));

@ -1,15 +1,15 @@
pub mod create; pub mod create;
mod events; mod events;
mod handler; mod handler;
mod setup;
mod tx_wrapper; mod tx_wrapper;
mod ui_commands; mod ui_commands;
use std::{path::Path, process::Stdio, sync::Arc}; use std::{path::Path, process::Stdio, sync::Arc, thread};
use log::{error, info, warn}; use log::{error, info, warn};
use nvim_rs::UiAttachOptions; use nvim_rs::UiAttachOptions;
use rmpv::Value; use tokio::process::Command;
use tokio::{process::Command, runtime::Runtime};
use crate::{ use crate::{
cmd_line::CmdLineSettings, error_handling::ResultPanicExplanation, running_tracker::*, cmd_line::CmdLineSettings, error_handling::ResultPanicExplanation, running_tracker::*,
@ -18,17 +18,88 @@ use crate::{
pub use events::*; pub use events::*;
use handler::NeovimHandler; use handler::NeovimHandler;
use setup::setup_neovide_specific_state;
pub use tx_wrapper::{TxWrapper, WrapTx}; pub use tx_wrapper::{TxWrapper, WrapTx};
pub use ui_commands::{start_ui_command_handler, ParallelCommand, SerialCommand, UiCommand}; pub use ui_commands::{start_ui_command_handler, ParallelCommand, SerialCommand, UiCommand};
enum ConnectionMode {
Child,
RemoteTcp(String),
}
fn connection_mode() -> ConnectionMode {
if let Some(arg) = SETTINGS.get::<CmdLineSettings>().remote_tcp {
ConnectionMode::RemoteTcp(arg)
} else {
ConnectionMode::Child
}
}
pub fn start_bridge() {
thread::spawn(|| {
start_neovim_runtime();
});
}
#[tokio::main]
async fn start_neovim_runtime() {
let handler = NeovimHandler::new();
let (nvim, io_handler) = match connection_mode() {
ConnectionMode::Child => create::new_child_cmd(&mut create_nvim_command(), handler).await,
ConnectionMode::RemoteTcp(address) => create::new_tcp(address, handler).await,
}
.unwrap_or_explained_panic("Could not locate or start neovim process");
// Check the neovim version to ensure its high enough
match nvim.command_output("echo has('nvim-0.4')").await.as_deref() {
Ok("1") => {} // This is just a guard
_ => {
error!("Neovide requires nvim version 0.4 or higher. Download the latest version here https://github.com/neovim/neovim/wiki/Installing-Neovim");
std::process::exit(0);
}
}
setup_neovide_specific_state(&nvim).await;
let settings = SETTINGS.get::<CmdLineSettings>();
let geometry = settings.geometry;
let mut options = UiAttachOptions::new();
options.set_linegrid_external(true);
options.set_multigrid_external(settings.multi_grid);
options.set_rgb(true);
// Triggers loading the user's config
nvim.ui_attach(geometry.width as i64, geometry.height as i64, &options)
.await
.unwrap_or_explained_panic("Could not attach ui to neovim process");
info!("Neovim process attached");
let nvim = Arc::new(nvim);
start_ui_command_handler(nvim.clone());
SETTINGS.read_initial_values(&nvim).await;
SETTINGS.setup_changed_listeners(&nvim).await;
match io_handler.await {
Err(join_error) => error!("Error joining IO loop: '{}'", join_error),
Ok(Err(error)) => {
if !error.is_channel_closed() {
error!("Error: '{}'", error);
}
}
Ok(Ok(())) => {}
};
RUNNING_TRACKER.quit("neovim processed failed");
}
#[cfg(windows)] #[cfg(windows)]
fn set_windows_creation_flags(cmd: &mut Command) { fn set_windows_creation_flags(cmd: &mut Command) {
cmd.creation_flags(0x0800_0000); // CREATE_NO_WINDOW cmd.creation_flags(0x0800_0000); // CREATE_NO_WINDOW
} }
fn build_nvim_cmd_with_args(bin: &str) -> Command { fn build_nvim_cmd_with_args(bin: &str) -> Command {
let mut args = Vec::<String>::new(); let mut args = vec!["--embed".to_string()];
args.push("--embed".to_string());
args.extend(SETTINGS.get::<CmdLineSettings>().neovim_args); args.extend(SETTINGS.get::<CmdLineSettings>().neovim_args);
#[cfg(windows)] #[cfg(windows)]
@ -98,26 +169,6 @@ fn build_nvim_cmd() -> Command {
} }
} }
#[cfg(windows)]
pub fn build_neovide_command(channel: u64, num_args: u64, command: &str, event: &str) -> String {
let nargs: String = if num_args > 1 {
"+".to_string()
} else {
num_args.to_string()
};
if num_args == 0 {
return format!(
"command! -nargs={} {} call rpcnotify({}, 'neovide.{}')",
nargs, command, channel, event
);
} else {
return format!(
"command! -nargs={} -complete=expression {} call rpcnotify({}, 'neovide.{}', <args>)",
nargs, command, channel, event
);
};
}
pub fn create_nvim_command() -> Command { pub fn create_nvim_command() -> Command {
let mut cmd = build_nvim_cmd(); let mut cmd = build_nvim_cmd();
@ -134,155 +185,3 @@ pub fn create_nvim_command() -> Command {
cmd cmd
} }
enum ConnectionMode {
Child,
RemoteTcp(String),
}
fn connection_mode() -> ConnectionMode {
if let Some(arg) = SETTINGS.get::<CmdLineSettings>().remote_tcp {
ConnectionMode::RemoteTcp(arg)
} else {
ConnectionMode::Child
}
}
async fn start_neovim_runtime() {
let handler = NeovimHandler::new();
let (nvim, io_handler) = match connection_mode() {
ConnectionMode::Child => create::new_child_cmd(&mut create_nvim_command(), handler).await,
ConnectionMode::RemoteTcp(address) => create::new_tcp(address, handler).await,
}
.unwrap_or_explained_panic("Could not locate or start neovim process");
if nvim.get_api_info().await.is_err() {
error!("Cannot get neovim api info, either neovide is launched with an unknown command line option or neovim version not supported!");
std::process::exit(-1);
}
tokio::spawn(async move {
info!("Close watcher started");
match io_handler.await {
Err(join_error) => error!("Error joining IO loop: '{}'", join_error),
Ok(Err(error)) => {
if !error.is_channel_closed() {
error!("Error: '{}'", error);
}
}
Ok(Ok(())) => {}
};
RUNNING_TRACKER.quit("neovim processed failed");
});
match nvim.command_output("echo has('nvim-0.4')").await.as_deref() {
Ok("1") => {} // This is just a guard
_ => {
error!("Neovide requires nvim version 0.4 or higher. Download the latest version here https://github.com/neovim/neovim/wiki/Installing-Neovim");
std::process::exit(0);
}
}
nvim.set_var("neovide", Value::Boolean(true))
.await
.unwrap_or_explained_panic("Could not communicate with neovim process");
if let Err(command_error) = nvim.command("runtime! ginit.vim").await {
nvim.command(&format!(
"echomsg \"error encountered in ginit.vim {:?}\"",
command_error
))
.await
.ok();
}
nvim.set_client_info(
"neovide",
vec![
(Value::from("major"), Value::from(0u64)),
(Value::from("minor"), Value::from(6u64)),
],
"ui",
vec![],
vec![],
)
.await
.ok();
let neovide_channel: u64 = nvim
.list_chans()
.await
.ok()
.and_then(|channel_values| parse_channel_list(channel_values).ok())
.and_then(|channel_list| {
channel_list.iter().find_map(|channel| match channel {
ChannelInfo {
id,
client: Some(ClientInfo { name, .. }),
..
} if name == "neovide" => Some(*id),
_ => None,
})
})
.unwrap_or(0);
info!(
"Neovide registered to nvim with channel id {}",
neovide_channel
);
#[cfg(windows)]
nvim.command(&build_neovide_command(
neovide_channel,
0,
"NeovideRegisterRightClick",
"register_right_click",
))
.await
.ok();
#[cfg(windows)]
nvim.command(&build_neovide_command(
neovide_channel,
0,
"NeovideUnregisterRightClick",
"unregister_right_click",
))
.await
.ok();
nvim.set_option("lazyredraw", Value::Boolean(false))
.await
.ok();
nvim.set_option("termguicolors", Value::Boolean(true))
.await
.ok();
let settings = SETTINGS.get::<CmdLineSettings>();
let geometry = settings.geometry;
let mut options = UiAttachOptions::new();
options.set_linegrid_external(true);
options.set_multigrid_external(settings.multi_grid);
options.set_rgb(true);
nvim.ui_attach(geometry.width as i64, geometry.height as i64, &options)
.await
.unwrap_or_explained_panic("Could not attach ui to neovim process");
info!("Neovim process attached");
let nvim = Arc::new(nvim);
start_ui_command_handler(nvim.clone());
SETTINGS.read_initial_values(&nvim).await;
SETTINGS.setup_changed_listeners(&nvim).await;
}
pub struct Bridge {
_runtime: Runtime, // Necessary to keep runtime running
}
pub fn start_bridge() -> Bridge {
let runtime = Runtime::new().unwrap();
runtime.spawn(start_neovim_runtime());
Bridge { _runtime: runtime }
}

@ -0,0 +1,117 @@
use log::info;
use nvim_rs::Neovim;
use rmpv::Value;
use crate::{
bridge::{events::*, TxWrapper},
error_handling::ResultPanicExplanation,
};
pub async fn setup_neovide_specific_state(nvim: &Neovim<TxWrapper>) {
// Set variable indicating to user config that neovide is being used
nvim.set_var("neovide", Value::Boolean(true))
.await
.unwrap_or_explained_panic("Could not communicate with neovim process");
if let Err(command_error) = nvim.command("runtime! ginit.vim").await {
nvim.command(&format!(
"echomsg \"error encountered in ginit.vim {:?}\"",
command_error
))
.await
.ok();
}
// Set details about the neovide version
nvim.set_client_info(
"neovide",
vec![
(Value::from("major"), Value::from(0u64)),
(Value::from("minor"), Value::from(6u64)),
],
"ui",
vec![],
vec![],
)
.await
.ok();
// Retrieve the channel number for communicating with neovide
let neovide_channel: u64 = nvim
.list_chans()
.await
.ok()
.and_then(|channel_values| parse_channel_list(channel_values).ok())
.and_then(|channel_list| {
channel_list.iter().find_map(|channel| match channel {
ChannelInfo {
id,
client: Some(ClientInfo { name, .. }),
..
} if name == "neovide" => Some(*id),
_ => None,
})
})
.unwrap_or(0);
// Record the channel to the log
info!(
"Neovide registered to nvim with channel id {}",
neovide_channel
);
// Create a command for registering right click context hooking
#[cfg(windows)]
nvim.command(&build_neovide_command(
neovide_channel,
0,
"NeovideRegisterRightClick",
"register_right_click",
))
.await
.ok();
// Create a command for unregistering the right click context hooking
#[cfg(windows)]
nvim.command(&build_neovide_command(
neovide_channel,
0,
"NeovideUnregisterRightClick",
"unregister_right_click",
))
.await
.ok();
// Set some basic rendering options
nvim.set_option("lazyredraw", Value::Boolean(false))
.await
.ok();
nvim.set_option("termguicolors", Value::Boolean(true))
.await
.ok();
// Create auto command for retrieving exit code from neovim on quit
nvim.command("autocmd VimLeave * call rpcnotify(1, 'neovide.quit', v:exiting)")
.await
.ok();
}
#[cfg(windows)]
pub fn build_neovide_command(channel: u64, num_args: u64, command: &str, event: &str) -> String {
let nargs: String = if num_args > 1 {
"+".to_string()
} else {
num_args.to_string()
};
if num_args == 0 {
return format!(
"command! -nargs={} {} call rpcnotify({}, 'neovide.{}')",
nargs, command, channel, event
);
} else {
return format!(
"command! -nargs={} -complete=expression {} call rpcnotify({}, 'neovide.{}', <args>)",
nargs, command, channel, event
);
};
}

@ -140,8 +140,7 @@ fn main() {
CursorSettings::register(); CursorSettings::register();
KeyboardSettings::register(); KeyboardSettings::register();
// We need to keep the bridge reference around to prevent the tokio runtime from getting freed start_bridge();
let _bridge = start_bridge();
start_editor(); start_editor();
create_window(); create_window();
} }

@ -1,5 +1,5 @@
use std::sync::{ use std::sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, AtomicI32, Ordering},
Arc, Arc,
}; };
@ -11,12 +11,14 @@ lazy_static! {
pub struct RunningTracker { pub struct RunningTracker {
running: Arc<AtomicBool>, running: Arc<AtomicBool>,
exit_code: Arc<AtomicI32>,
} }
impl RunningTracker { impl RunningTracker {
fn new() -> Self { fn new() -> Self {
Self { Self {
running: Arc::new(AtomicBool::new(true)), running: Arc::new(AtomicBool::new(true)),
exit_code: Arc::new(AtomicI32::new(0)),
} }
} }
@ -25,7 +27,17 @@ impl RunningTracker {
info!("Quit {}", reason); info!("Quit {}", reason);
} }
pub fn quit_with_code(&self, code: i32, reason: &str) {
self.exit_code.store(code, Ordering::Relaxed);
self.running.store(false, Ordering::Relaxed);
info!("Quit with code {}: {}", code, reason);
}
pub fn is_running(&self) -> bool { pub fn is_running(&self) -> bool {
self.running.load(Ordering::Relaxed) self.running.load(Ordering::Relaxed)
} }
pub fn exit_code(&self) -> i32 {
self.exit_code.load(Ordering::Relaxed)
}
} }

@ -369,7 +369,8 @@ pub fn create_window() {
window_wrapper.saved_grid_size, window_wrapper.saved_grid_size,
window.outer_position().ok(), window.outer_position().ok(),
); );
std::process::exit(0);
std::process::exit(RUNNING_TRACKER.exit_code());
} }
let frame_start = Instant::now(); let frame_start = Instant::now();

Loading…
Cancel
Save