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}