Skip to main content

squawk_server/
server.rs

1use anyhow::Result;
2use log::info;
3use lsp_server::{Connection, Message};
4use lsp_types::{
5    CodeActionKind, CodeActionOptions, CodeActionProviderCapability, CompletionOptions,
6    DiagnosticOptions, DiagnosticServerCapabilities, FoldingRangeProviderCapability,
7    HoverProviderCapability, InitializeParams, OneOf, SelectionRangeProviderCapability,
8    ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, WorkDoneProgressOptions,
9    notification::{DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument},
10    request::{
11        CodeActionRequest, Completion, DocumentDiagnosticRequest, DocumentSymbolRequest,
12        FoldingRangeRequest, GotoDefinition, HoverRequest, InlayHintRequest, References,
13        SelectionRangeRequest,
14    },
15};
16
17use crate::dispatch::{NotificationDispatcher, RequestDispatcher};
18use crate::handlers::{
19    SyntaxTreeRequest, TokensRequest, handle_code_action, handle_completion, handle_did_change,
20    handle_did_close, handle_did_open, handle_document_diagnostic, handle_document_symbol,
21    handle_folding_range, handle_goto_definition, handle_hover, handle_inlay_hints,
22    handle_references, handle_selection_range, handle_syntax_tree, handle_tokens,
23};
24use crate::system::GlobalState;
25
26pub fn run() -> Result<()> {
27    info!("Starting Squawk LSP server");
28
29    let (connection, io_threads) = Connection::stdio();
30
31    let server_capabilities = serde_json::to_value(&ServerCapabilities {
32        text_document_sync: Some(TextDocumentSyncCapability::Kind(
33            TextDocumentSyncKind::INCREMENTAL,
34        )),
35        code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions {
36            code_action_kinds: Some(vec![
37                CodeActionKind::QUICKFIX,
38                CodeActionKind::REFACTOR_REWRITE,
39            ]),
40            work_done_progress_options: WorkDoneProgressOptions {
41                work_done_progress: None,
42            },
43            resolve_provider: None,
44        })),
45        selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
46        references_provider: Some(OneOf::Left(true)),
47        definition_provider: Some(OneOf::Left(true)),
48        hover_provider: Some(HoverProviderCapability::Simple(true)),
49        inlay_hint_provider: Some(OneOf::Left(true)),
50        diagnostic_provider: Some(DiagnosticServerCapabilities::Options(DiagnosticOptions {
51            identifier: None,
52            inter_file_dependencies: false,
53            workspace_diagnostics: false,
54            work_done_progress_options: WorkDoneProgressOptions {
55                work_done_progress: None,
56            },
57        })),
58        document_symbol_provider: Some(OneOf::Left(true)),
59        folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
60        completion_provider: Some(CompletionOptions {
61            resolve_provider: Some(false),
62            trigger_characters: Some(vec![".".to_owned()]),
63            all_commit_characters: None,
64            work_done_progress_options: WorkDoneProgressOptions {
65                work_done_progress: None,
66            },
67            completion_item: None,
68        }),
69        ..Default::default()
70    })
71    .unwrap();
72
73    info!("LSP server initializing connection...");
74    let initialization_params = connection.initialize(server_capabilities)?;
75    info!("LSP server initialized, entering main loop");
76
77    main_loop(connection, initialization_params)?;
78
79    info!("LSP server shutting down");
80
81    io_threads.join()?;
82    Ok(())
83}
84
85fn main_loop(connection: Connection, params: serde_json::Value) -> Result<()> {
86    info!("Server main loop");
87
88    let init_params: InitializeParams = serde_json::from_value(params).unwrap_or_default();
89    info!("Client process ID: {:?}", init_params.process_id);
90    let client_name = init_params.client_info.map(|x| x.name);
91    info!("Client name: {client_name:?}");
92
93    let mut system = GlobalState::new();
94
95    for msg in &connection.receiver {
96        match msg {
97            Message::Request(req) => {
98                info!("Received request: method={}, id={:?}", req.method, req.id);
99
100                if connection.handle_shutdown(&req)? {
101                    info!("Received shutdown request, exiting");
102                    return Ok(());
103                }
104
105                RequestDispatcher::new(&connection, req, &system)
106                    .on::<GotoDefinition>(handle_goto_definition)?
107                    .on::<HoverRequest>(handle_hover)?
108                    .on::<CodeActionRequest>(handle_code_action)?
109                    .on::<SelectionRangeRequest>(handle_selection_range)?
110                    .on::<InlayHintRequest>(handle_inlay_hints)?
111                    .on::<DocumentSymbolRequest>(handle_document_symbol)?
112                    .on::<FoldingRangeRequest>(handle_folding_range)?
113                    .on::<Completion>(handle_completion)?
114                    .on::<DocumentDiagnosticRequest>(handle_document_diagnostic)?
115                    .on::<SyntaxTreeRequest>(handle_syntax_tree)?
116                    .on::<TokensRequest>(handle_tokens)?
117                    .on::<References>(handle_references)?
118                    .finish();
119            }
120            Message::Response(resp) => {
121                info!("Received response: id={:?}", resp.id);
122            }
123            Message::Notification(notif) => {
124                info!("Received notification: method={}", notif.method);
125
126                NotificationDispatcher::new(&connection, notif, &mut system)
127                    .on::<DidOpenTextDocument>(handle_did_open)?
128                    .on::<DidChangeTextDocument>(handle_did_change)?
129                    .on::<DidCloseTextDocument>(handle_did_close)?
130                    .finish();
131            }
132        }
133    }
134    Ok(())
135}