Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
catch_unwind inside the lsp and return a friendly error message
Browse files Browse the repository at this point in the history
  • Loading branch information
xunilrj committed Nov 15, 2022
1 parent 8e34486 commit 6da7eb5
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 17 deletions.
51 changes: 51 additions & 0 deletions crates/rome_diagnostics/src/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub mod error;
pub mod location;
pub mod serde;

use std::panic::UnwindSafe;

#[doc(hidden)]
// Convenience re-export for procedural macro
pub use rome_console as console;
Expand All @@ -26,6 +28,55 @@ pub use display::{
pub use error::{Error, Result};
pub use location::{FileId, FilePath, LineIndex, LineIndexBuf, Location, Resource, SourceCode};

#[derive(Default, Debug)]
pub struct PanicError {
pub info: String,
pub backtrace: Option<std::backtrace::Backtrace>,
}

thread_local! {
static LAST_PANIC: std::cell::Cell<Option<PanicError>> = std::cell::Cell::new(None);
}

impl std::fmt::Display for PanicError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let r = f.write_fmt(format_args!("{}\n", self.info));
if let Some(backtrace) = &self.backtrace {
f.write_fmt(format_args!("Backtrace: {}", backtrace))
} else {
r
}
}
}

/// Take and set a specific panic hook before calling `f` inside a `catch_unwind`, then
/// return the old set_hook.
///
/// If `f` panicks am `Error` with the panic message plus backtrace will be returned.
pub fn catch_unwind<F, R>(f: F) -> Result<R, PanicError>
where
F: FnOnce() -> R + UnwindSafe,
{
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(|info| {
let info = info.to_string();
let backtrace = std::backtrace::Backtrace::capture();
LAST_PANIC.with(|cell| {
cell.set(Some(PanicError {
info,
backtrace: Some(backtrace),
}))
})
}));

let result = std::panic::catch_unwind(f)
.map_err(|_| LAST_PANIC.with(|cell| cell.take()).unwrap_or_default());

std::panic::set_hook(prev);

result
}

pub mod prelude {
//! Anonymously re-exports all the traits declared by this module, this is
//! intended to be imported as `use rome_diagnostics::v2::prelude::*;` to
Expand Down
52 changes: 35 additions & 17 deletions crates/rome_lsp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use rome_fs::CONFIG_NAME;
use rome_service::workspace::{RageEntry, RageParams, RageResult};
use rome_service::{workspace, Workspace};
use std::collections::HashMap;
use std::panic::RefUnwindSafe;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use tokio::io::{AsyncRead, AsyncWrite};
Expand All @@ -32,6 +33,8 @@ pub struct LSPServer {
is_initialized: Arc<AtomicBool>,
}

impl RefUnwindSafe for LSPServer {}

impl LSPServer {
fn new(
session: SessionHandle,
Expand Down Expand Up @@ -302,44 +305,59 @@ impl LanguageServer for LSPServer {
}

async fn code_action(&self, params: CodeActionParams) -> LspResult<Option<CodeActionResponse>> {
handlers::analysis::code_actions(&self.session, params).map_err(into_lsp_error)
rome_diagnostics::v2::catch_unwind(move || {
handlers::analysis::code_actions(&self.session, params).map_err(into_lsp_error)
})
.map_err(into_lsp_error)?
}

async fn formatting(
&self,
params: DocumentFormattingParams,
) -> LspResult<Option<Vec<TextEdit>>> {
handlers::formatting::format(&self.session, params).map_err(into_lsp_error)
rome_diagnostics::v2::catch_unwind(move || {
handlers::formatting::format(&self.session, params).map_err(into_lsp_error)
})
.map_err(into_lsp_error)?
}

async fn range_formatting(
&self,
params: DocumentRangeFormattingParams,
) -> LspResult<Option<Vec<TextEdit>>> {
handlers::formatting::format_range(&self.session, params).map_err(into_lsp_error)
rome_diagnostics::v2::catch_unwind(move || {
handlers::formatting::format_range(&self.session, params).map_err(into_lsp_error)
})
.map_err(into_lsp_error)?
}

async fn on_type_formatting(
&self,
params: DocumentOnTypeFormattingParams,
) -> LspResult<Option<Vec<TextEdit>>> {
handlers::formatting::format_on_type(&self.session, params).map_err(into_lsp_error)
rome_diagnostics::v2::catch_unwind(move || {
handlers::formatting::format_on_type(&self.session, params).map_err(into_lsp_error)
})
.map_err(into_lsp_error)?
}

async fn rename(&self, params: RenameParams) -> LspResult<Option<WorkspaceEdit>> {
let rename_enabled = self
.session
.config
.read()
.ok()
.and_then(|config| config.settings.rename)
.unwrap_or(false);

if rename_enabled {
handlers::rename::rename(&self.session, params).map_err(into_lsp_error)
} else {
Ok(None)
}
rome_diagnostics::v2::catch_unwind(move || {
let rename_enabled = self
.session
.config
.read()
.ok()
.and_then(|config| config.settings.rename)
.unwrap_or(false);

if rename_enabled {
handlers::rename::rename(&self.session, params).map_err(into_lsp_error)
} else {
Ok(None)
}
})
.map_err(into_lsp_error)?
}
}

Expand Down

0 comments on commit 6da7eb5

Please sign in to comment.