backtrace_ls/lsp/
server.rs

1//! LSP server entry point.
2
3use std::sync::Arc;
4
5#[cfg(unix)]
6use async_lsp::stdio::{PipeStdin, PipeStdout};
7use async_lsp::{
8    client_monitor::ClientProcessMonitorLayer,
9    concurrency::ConcurrencyLayer,
10    lsp_types::{
11        CodeActionKind, CodeActionOptions, CodeActionProviderCapability, DiagnosticOptions,
12        DiagnosticServerCapabilities, ExecuteCommandOptions, ServerCapabilities, ServerInfo,
13        TextDocumentSyncCapability, TextDocumentSyncKind, WorkDoneProgressOptions,
14    },
15    panic::CatchUnwindLayer,
16    router::Router,
17    server::LifecycleLayer,
18};
19use tower::ServiceBuilder;
20
21use super::{code_action, handlers, state::ServerState};
22use crate::error::LSError;
23
24#[must_use]
25pub fn server_capabilities() -> ServerCapabilities {
26    ServerCapabilities {
27        diagnostic_provider: Some(DiagnosticServerCapabilities::Options(DiagnosticOptions {
28            identifier: None,
29            inter_file_dependencies: false,
30            workspace_diagnostics: true,
31            work_done_progress_options: WorkDoneProgressOptions::default(),
32        })),
33        text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::NONE)),
34        code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions {
35            code_action_kinds: Some(vec![CodeActionKind::QUICKFIX]),
36            work_done_progress_options: WorkDoneProgressOptions::default(),
37            resolve_provider: None,
38        })),
39        execute_command_provider: Some(ExecuteCommandOptions {
40            commands: code_action::commands::all(),
41            work_done_progress_options: WorkDoneProgressOptions::default(),
42        }),
43        ..ServerCapabilities::default()
44    }
45}
46
47#[must_use]
48pub fn server_info() -> ServerInfo {
49    ServerInfo {
50        name: "backtrace-ls".to_string(),
51        version: Some(env!("CARGO_PKG_VERSION").to_string()),
52    }
53}
54
55/// Runs the LSP server.
56pub async fn run() -> Result<(), LSError> {
57    let (server, _) = async_lsp::MainLoop::new_server(|client| {
58        let state = ServerState::new(client.clone());
59        let mut router = Router::new(Arc::clone(&state));
60
61        handlers::register_all(&mut router, &state);
62
63        ServiceBuilder::new()
64            .layer(LifecycleLayer::default())
65            .layer(CatchUnwindLayer::default())
66            .layer(ConcurrencyLayer::default())
67            .layer(ClientProcessMonitorLayer::new(client))
68            .service(router)
69    });
70
71    #[cfg(unix)]
72    let (stdin, stdout) = (
73        PipeStdin::lock_tokio().unwrap(),
74        PipeStdout::lock_tokio().unwrap(),
75    );
76
77    #[cfg(not(unix))]
78    let (stdin, stdout) = (
79        tokio_util::compat::TokioAsyncReadCompatExt::compat(tokio::io::stdin()),
80        tokio_util::compat::TokioAsyncWriteCompatExt::compat_write(tokio::io::stdout()),
81    );
82
83    server
84        .run_buffered(stdin, stdout)
85        .await
86        .map_err(|e| LSError::Generic(format!("LSP server error: {e}")))?;
87
88    Ok(())
89}