Skip to content

Commit

Permalink
Split neovim bridge into launch and attach phases
Browse files Browse the repository at this point in the history
This makes it possible to create the window with the correct size after
it's launched, and ginit.vim/lua is processed.
  • Loading branch information
fredizzimo committed Oct 1, 2023
1 parent c35f216 commit 042b353
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 33 deletions.
98 changes: 72 additions & 26 deletions src/bridge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,42 @@ pub mod session;
mod setup;
mod ui_commands;

use std::{process::exit, sync::Arc, thread};

use log::{error, info};
use nvim_rs::{error::CallError, Neovim, UiAttachOptions, Value};
use std::{io::Error, process::exit};
use tokio::{
runtime::{Builder, Runtime},
task::JoinHandle,
};

use crate::{
cmd_line::CmdLineSettings, error_handling::ResultPanicExplanation,
event_aggregator::EVENT_AGGREGATOR, running_tracker::*, settings::*, window::WindowCommand,
};
use handler::NeovimHandler;
use session::{NeovimInstance, NeovimSession};
use setup::setup_neovide_specific_state;

pub use command::create_nvim_command;
pub use events::*;
use handler::NeovimHandler;
pub use session::NeovimWriter;
use session::{NeovimInstance, NeovimSession};
use setup::setup_neovide_specific_state;
pub use ui_commands::{start_ui_command_handler, ParallelCommand, SerialCommand, UiCommand};

const INTRO_MESSAGE_LUA: &str = include_str!("../../lua/intro.lua");
const NEOVIM_REQUIRED_VERSION: &str = "0.9.2";

enum RuntimeState {
Idle,
Invalid,
Launched(NeovimSession),
Attached(JoinHandle<()>),
}

pub struct NeovimRuntime {
runtime: Runtime,
state: RuntimeState,
}

fn neovim_instance() -> NeovimInstance {
if let Some(address) = SETTINGS.get::<CmdLineSettings>().server {
NeovimInstance::Server { address }
Expand All @@ -35,14 +50,6 @@ fn neovim_instance() -> NeovimInstance {
}
}

pub fn start_bridge() {
// hoisted out of the actual thread so error messages while trying to find nvim can be printed before forking
let instance = neovim_instance();
thread::spawn(|| {
start_neovim_runtime(instance);
});
}

pub async fn setup_intro_message_autocommand(
nvim: &Neovim<NeovimWriter>,
) -> Result<Value, Box<CallError>> {
Expand All @@ -60,17 +67,16 @@ pub async fn show_intro_message(
nvim.exec_lua(INTRO_MESSAGE_LUA, args).await
}

#[tokio::main]
async fn start_neovim_runtime(instance: NeovimInstance) {
async fn launch() -> NeovimSession {
let neovim_instance = neovim_instance();
let handler = NeovimHandler::new();
let session = NeovimSession::new(instance, handler)
let session = NeovimSession::new(neovim_instance, handler)
.await
.unwrap_or_explained_panic("Could not locate or start neovim process");

let nvim = Arc::new(session.neovim);

// Check the neovim version to ensure its high enough
match nvim
match session
.neovim
.command_output(&format!("echo has('nvim-{NEOVIM_REQUIRED_VERSION}')"))
.await
.as_deref()
Expand All @@ -81,12 +87,19 @@ async fn start_neovim_runtime(instance: NeovimInstance) {
exit(0);
}
}

let settings = SETTINGS.get::<CmdLineSettings>();

let should_handle_clipboard = settings.wsl || settings.server.is_some();
setup_neovide_specific_state(&nvim, should_handle_clipboard).await;
setup_neovide_specific_state(&session.neovim, should_handle_clipboard).await;

start_ui_command_handler(session.neovim.clone());
SETTINGS.read_initial_values(&session.neovim).await;
SETTINGS.setup_changed_listeners(&session.neovim).await;
session
}

async fn run(session: NeovimSession) {
let settings = SETTINGS.get::<CmdLineSettings>();
let mut options = UiAttachOptions::new();
options.set_linegrid_external(true);
options.set_multigrid_external(!settings.no_multi_grid);
Expand All @@ -95,17 +108,15 @@ async fn start_neovim_runtime(instance: NeovimInstance) {
// Triggers loading the user's config
// Set to DEFAULT_WINDOW_GEOMETRY first, draw_frame will resize it later
let geometry = DEFAULT_WINDOW_GEOMETRY;
nvim.ui_attach(geometry.width as i64, geometry.height as i64, &options)
session
.neovim
.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");
EVENT_AGGREGATOR.send(WindowCommand::UIEnter);

start_ui_command_handler(nvim.clone());
SETTINGS.read_initial_values(&nvim).await;
SETTINGS.setup_changed_listeners(&nvim).await;

match session.io_handle.await {
Err(join_error) => error!("Error joining IO loop: '{}'", join_error),
Ok(Err(error)) => {
Expand All @@ -117,3 +128,38 @@ async fn start_neovim_runtime(instance: NeovimInstance) {
};
RUNNING_TRACKER.quit("neovim processed failed");
}

impl NeovimRuntime {
pub fn new() -> Result<Self, Error> {
let runtime = Builder::new_multi_thread().enable_all().build()?;

Ok(Self {
runtime,
state: RuntimeState::Idle,
})
}

pub fn launch(&mut self) {
assert!(matches!(self.state, RuntimeState::Idle));
self.state = RuntimeState::Launched(self.runtime.block_on(launch()));
}

pub fn attach(&mut self) {
assert!(matches!(self.state, RuntimeState::Launched(..)));
if let RuntimeState::Launched(session) =
std::mem::replace(&mut self.state, RuntimeState::Invalid)
{
self.state = RuntimeState::Attached(self.runtime.spawn(run(session)));
}
}
}

impl Drop for NeovimRuntime {
fn drop(&mut self) {
if let RuntimeState::Attached(join_handle) =
std::mem::replace(&mut self.state, RuntimeState::Idle)
{
let _ = self.runtime.block_on(join_handle);
}
}
}
8 changes: 6 additions & 2 deletions src/bridge/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use std::{
io::{Error, ErrorKind, Result},
process::Stdio,
sync::Arc,
};

use nvim_rs::{error::LoopError, neovim::Neovim, Handler};
Expand All @@ -22,7 +23,7 @@ type BoxedReader = Box<dyn AsyncRead + Send + Unpin + 'static>;
type BoxedWriter = Box<dyn AsyncWrite + Send + Unpin + 'static>;

pub struct NeovimSession {
pub neovim: Neovim<NeovimWriter>,
pub neovim: Arc<Neovim<NeovimWriter>>,
pub io_handle: JoinHandle<std::result::Result<(), Box<LoopError>>>,
}

Expand All @@ -36,7 +37,10 @@ impl NeovimSession {
Neovim::<NeovimWriter>::new(reader.compat(), Box::new(writer.compat_write()), handler);
let io_handle = spawn(io);

Ok(Self { neovim, io_handle })
Ok(Self {
neovim: Arc::new(neovim),
io_handle,
})
}
}

Expand Down
13 changes: 8 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use flexi_logger::{Cleanup, Criterion, Duplicate, FileSpec, Logger, Naming};
use log::trace;

use backtrace::Backtrace;
use bridge::start_bridge;
use bridge::NeovimRuntime;
use cmd_line::CmdLineSettings;
use editor::start_editor;
use renderer::{cursor_renderer::CursorSettings, RendererSettings};
Expand Down Expand Up @@ -174,12 +174,15 @@ fn protected_main() -> ExitCode {
CursorSettings::register();
KeyboardSettings::register();

start_bridge();
start_editor();
maybe_disown();

let mut runtime = NeovimRuntime::new().unwrap();
runtime.launch();
let event_loop = create_event_loop();
let window = create_window(&event_loop);
runtime.attach();

maybe_disown();
start_editor();

match main_loop(window, event_loop) {
Ok(()) => 0,
// All error codes have to be u8, so just do a direct cast with wrap around, even if the value is negative,
Expand Down

0 comments on commit 042b353

Please sign in to comment.