Merge pull request #262 from zellij-org/error-model
Make error model more autonomous
This commit is contained in:
commit
138889f49a
4 changed files with 45 additions and 48 deletions
|
|
@ -1,7 +1,7 @@
|
|||
//! Error context system based on a thread-local representation of the call stack, itself based on
|
||||
//! the instructions that are sent between threads.
|
||||
|
||||
use super::{AppInstruction, OPENCALLS};
|
||||
use super::{AppInstruction, ASYNCOPENCALLS, OPENCALLS};
|
||||
use crate::pty_bus::PtyInstruction;
|
||||
use crate::screen::ScreenInstruction;
|
||||
|
||||
|
|
@ -72,6 +72,12 @@ pub fn handle_panic(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_current_ctx() -> ErrorContext {
|
||||
ASYNCOPENCALLS
|
||||
.try_with(|ctx| *ctx.borrow())
|
||||
.unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow()))
|
||||
}
|
||||
|
||||
/// A representation of the call stack.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ErrorContext {
|
||||
|
|
@ -95,7 +101,9 @@ impl ErrorContext {
|
|||
break;
|
||||
}
|
||||
}
|
||||
OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self);
|
||||
ASYNCOPENCALLS
|
||||
.try_with(|ctx| *ctx.borrow_mut() = *self)
|
||||
.unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,9 +58,6 @@ impl InputHandler {
|
|||
fn handle_input(&mut self) {
|
||||
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
|
||||
err_ctx.add_call(ContextType::StdinHandler);
|
||||
self.send_pty_instructions.update(err_ctx);
|
||||
self.send_app_instructions.update(err_ctx);
|
||||
self.send_screen_instructions.update(err_ctx);
|
||||
let keybinds = self.config.keybinds.clone();
|
||||
let alt_left_bracket = vec![27, 91];
|
||||
loop {
|
||||
|
|
|
|||
|
|
@ -25,9 +25,13 @@ use crate::cli::CliArgs;
|
|||
use crate::common::input::config::Config;
|
||||
use crate::layout::Layout;
|
||||
use crate::panes::PaneId;
|
||||
use async_std::task_local;
|
||||
use command_is_executing::CommandIsExecuting;
|
||||
use directories_next::ProjectDirs;
|
||||
use errors::{AppContext, ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext};
|
||||
use errors::{
|
||||
get_current_ctx, AppContext, ContextType, ErrorContext, PluginContext, PtyContext,
|
||||
ScreenContext,
|
||||
};
|
||||
use input::handler::input_loop;
|
||||
use os_input_output::OsApi;
|
||||
use pty_bus::{PtyBus, PtyInstruction};
|
||||
|
|
@ -72,32 +76,23 @@ enum SenderType<T: Clone> {
|
|||
/// synchronously or asynchronously depending on the underlying [`SenderType`].
|
||||
#[derive(Clone)]
|
||||
pub struct SenderWithContext<T: Clone> {
|
||||
err_ctx: ErrorContext,
|
||||
sender: SenderType<T>,
|
||||
}
|
||||
|
||||
impl<T: Clone> SenderWithContext<T> {
|
||||
fn new(err_ctx: ErrorContext, sender: SenderType<T>) -> Self {
|
||||
Self { err_ctx, sender }
|
||||
fn new(sender: SenderType<T>) -> Self {
|
||||
Self { sender }
|
||||
}
|
||||
|
||||
/// Sends an event, along with the current [`ErrorContext`], on this
|
||||
/// [`SenderWithContext`]'s channel.
|
||||
pub fn send(&self, event: T) -> Result<(), mpsc::SendError<(T, ErrorContext)>> {
|
||||
let err_ctx = get_current_ctx();
|
||||
match self.sender {
|
||||
SenderType::Sender(ref s) => s.send((event, self.err_ctx)),
|
||||
SenderType::SyncSender(ref s) => s.send((event, self.err_ctx)),
|
||||
SenderType::Sender(ref s) => s.send((event, err_ctx)),
|
||||
SenderType::SyncSender(ref s) => s.send((event, err_ctx)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates this [`SenderWithContext`]'s [`ErrorContext`]. This is the way one adds
|
||||
/// a call to the error context.
|
||||
///
|
||||
/// Updating [`ErrorContext`]s works in this way so that these contexts are only ever
|
||||
/// allocated on the stack (which is thread-specific), and not on the heap.
|
||||
pub fn update(&mut self, new_ctx: ErrorContext) {
|
||||
self.err_ctx = new_ctx;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Clone> Send for SenderWithContext<T> {}
|
||||
|
|
@ -109,6 +104,12 @@ thread_local!(
|
|||
static OPENCALLS: RefCell<ErrorContext> = RefCell::default()
|
||||
);
|
||||
|
||||
task_local! {
|
||||
/// A key to some task local storage that holds a representation of the task's call
|
||||
/// stack in the form of an [`ErrorContext`].
|
||||
static ASYNCOPENCALLS: RefCell<ErrorContext> = RefCell::default()
|
||||
}
|
||||
|
||||
/// Instructions related to the entire application.
|
||||
#[derive(Clone)]
|
||||
pub enum AppInstruction {
|
||||
|
|
@ -140,25 +141,23 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
let (send_screen_instructions, receive_screen_instructions): ChannelWithContext<
|
||||
ScreenInstruction,
|
||||
> = mpsc::channel();
|
||||
let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
|
||||
let mut send_screen_instructions =
|
||||
SenderWithContext::new(err_ctx, SenderType::Sender(send_screen_instructions));
|
||||
let send_screen_instructions =
|
||||
SenderWithContext::new(SenderType::Sender(send_screen_instructions));
|
||||
|
||||
let (send_pty_instructions, receive_pty_instructions): ChannelWithContext<PtyInstruction> =
|
||||
mpsc::channel();
|
||||
let mut send_pty_instructions =
|
||||
SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions));
|
||||
let send_pty_instructions = SenderWithContext::new(SenderType::Sender(send_pty_instructions));
|
||||
|
||||
let (send_plugin_instructions, receive_plugin_instructions): ChannelWithContext<
|
||||
PluginInstruction,
|
||||
> = mpsc::channel();
|
||||
let send_plugin_instructions =
|
||||
SenderWithContext::new(err_ctx, SenderType::Sender(send_plugin_instructions));
|
||||
SenderWithContext::new(SenderType::Sender(send_plugin_instructions));
|
||||
|
||||
let (send_app_instructions, receive_app_instructions): SyncChannelWithContext<AppInstruction> =
|
||||
mpsc::sync_channel(0);
|
||||
let send_app_instructions =
|
||||
SenderWithContext::new(err_ctx, SenderType::SyncSender(send_app_instructions));
|
||||
SenderWithContext::new(SenderType::SyncSender(send_app_instructions));
|
||||
|
||||
let mut pty_bus = PtyBus::new(
|
||||
receive_pty_instructions,
|
||||
|
|
@ -195,7 +194,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
.recv()
|
||||
.expect("failed to receive event on channel");
|
||||
err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
|
||||
pty_bus.send_screen_instructions.update(err_ctx);
|
||||
match event {
|
||||
PtyInstruction::SpawnTerminal(file_to_open) => {
|
||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||
|
|
@ -272,8 +270,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
.recv()
|
||||
.expect("failed to receive event on channel");
|
||||
err_ctx.add_call(ContextType::Screen(ScreenContext::from(&event)));
|
||||
screen.send_app_instructions.update(err_ctx);
|
||||
screen.send_pty_instructions.update(err_ctx);
|
||||
match event {
|
||||
ScreenInstruction::PtyBytes(pid, vte_bytes) => {
|
||||
let active_tab = screen.get_active_tab_mut().unwrap();
|
||||
|
|
@ -433,9 +429,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
let wasm_thread = thread::Builder::new()
|
||||
.name("wasm".to_string())
|
||||
.spawn({
|
||||
let mut send_pty_instructions = send_pty_instructions.clone();
|
||||
let mut send_screen_instructions = send_screen_instructions.clone();
|
||||
let mut send_app_instructions = send_app_instructions.clone();
|
||||
let send_pty_instructions = send_pty_instructions.clone();
|
||||
let send_screen_instructions = send_screen_instructions.clone();
|
||||
let send_app_instructions = send_app_instructions.clone();
|
||||
|
||||
let store = Store::default();
|
||||
let mut plugin_id = 0;
|
||||
|
|
@ -445,9 +441,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
.recv()
|
||||
.expect("failed to receive event on channel");
|
||||
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
|
||||
send_screen_instructions.update(err_ctx);
|
||||
send_pty_instructions.update(err_ctx);
|
||||
send_app_instructions.update(err_ctx);
|
||||
match event {
|
||||
PluginInstruction::Load(pid_tx, path) => {
|
||||
let project_dirs =
|
||||
|
|
@ -556,16 +549,14 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
.name("ipc_server".to_string())
|
||||
.spawn({
|
||||
use std::io::Read;
|
||||
let mut send_pty_instructions = send_pty_instructions.clone();
|
||||
let mut send_screen_instructions = send_screen_instructions.clone();
|
||||
let send_pty_instructions = send_pty_instructions.clone();
|
||||
let send_screen_instructions = send_screen_instructions.clone();
|
||||
move || {
|
||||
std::fs::remove_file(ZELLIJ_IPC_PIPE).ok();
|
||||
let listener = std::os::unix::net::UnixListener::bind(ZELLIJ_IPC_PIPE)
|
||||
.expect("could not listen on ipc socket");
|
||||
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
|
||||
err_ctx.add_call(ContextType::IpcServer);
|
||||
send_pty_instructions.update(err_ctx);
|
||||
send_screen_instructions.update(err_ctx);
|
||||
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
|
|
@ -637,8 +628,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
.expect("failed to receive app instruction on channel");
|
||||
|
||||
err_ctx.add_call(ContextType::App(AppContext::from(&app_instruction)));
|
||||
send_screen_instructions.update(err_ctx);
|
||||
send_pty_instructions.update(err_ctx);
|
||||
match app_instruction {
|
||||
AppInstruction::Exit => {
|
||||
break;
|
||||
|
|
@ -652,7 +641,11 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
let _ = wasm_thread.join();
|
||||
os_input.unset_raw_mode(0);
|
||||
let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
|
||||
let error = format!("{}\n{}", goto_start_of_last_line, backtrace);
|
||||
let restore_snapshot = "\u{1b}[?1049l";
|
||||
let error = format!(
|
||||
"{}\n{}{}",
|
||||
goto_start_of_last_line, restore_snapshot, backtrace
|
||||
);
|
||||
let _ = os_input
|
||||
.get_stdout_writer()
|
||||
.write(error.as_bytes())
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ use ::std::sync::mpsc::Receiver;
|
|||
use ::std::time::{Duration, Instant};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{ScreenInstruction, SenderWithContext, OPENCALLS};
|
||||
use super::{ScreenInstruction, SenderWithContext};
|
||||
use crate::os_input_output::OsApi;
|
||||
use crate::utils::logging::debug_to_file;
|
||||
use crate::{
|
||||
errors::{ContextType, ErrorContext},
|
||||
errors::{get_current_ctx, ContextType, ErrorContext},
|
||||
panes::PaneId,
|
||||
};
|
||||
use crate::{layout::Layout, wasm_vm::PluginInstruction};
|
||||
|
|
@ -89,15 +89,14 @@ pub struct PtyBus {
|
|||
|
||||
fn stream_terminal_bytes(
|
||||
pid: RawFd,
|
||||
mut send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
||||
send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
||||
os_input: Box<dyn OsApi>,
|
||||
debug: bool,
|
||||
) -> JoinHandle<()> {
|
||||
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
|
||||
let mut err_ctx = get_current_ctx();
|
||||
task::spawn({
|
||||
async move {
|
||||
err_ctx.add_call(ContextType::AsyncTask);
|
||||
send_screen_instructions.update(err_ctx);
|
||||
let mut terminal_bytes = ReadFromPid::new(&pid, os_input);
|
||||
|
||||
let mut last_byte_receive_time: Option<Instant> = None;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue