1use crate::code_actions::code_actions_for_params_with_encoding;
2use crate::completion::{completion_for_snapshot, resolve_completion_item};
3use crate::diagnostics::analysis_payload_to_versioned_diagnostics;
4use crate::document_store::{
5 AnalyzerConfigurationChange, DiagnosticContext, DocumentDiagnosticState, DocumentStore,
6 DocumentSyncError, SemanticTokensState, SnapshotContext, StoredDocument,
7 WORKSPACE_SYMBOL_SNAPSHOT_BATCH_SIZE, analysis_options_with_lsp_resource_defaults,
8 default_lsp_analysis_options,
9};
10use crate::protocol::{
11 CONFIG_SCHEMA_METHOD, ConfigSchemaResponse, RULE_CATALOG_METHOD, RuleCatalogResponse,
12 WorkspaceEditEncoding, experimental_capabilities,
13};
14use crate::semantic_tokens::{
15 semantic_tokens_delta_result, semantic_tokens_for_snapshot, semantic_tokens_for_snapshot_range,
16 semantic_tokens_options, semantic_tokens_result_id,
17};
18use crate::snapshot::DocumentSnapshot;
19use crate::snapshot_context::{self, SnapshotContextKind};
20use crate::structure::{
21 document_symbols_with_hierarchy_support as structure_document_symbols_with_hierarchy_support,
22 folding_ranges as structure_folding_ranges, goto_definition as structure_goto_definition,
23 hover as structure_hover, prepare_rename as structure_prepare_rename,
24 references as structure_references,
25 rename_with_workspace_edit_encoding as structure_rename_with_workspace_edit_encoding,
26 selection_ranges as structure_selection_ranges,
27 workspace_symbols_for_snapshots as structure_workspace_symbols_for_snapshots,
28};
29use merman_analysis::{
30 AnalysisOptions, AnalysisPayload, Analyzer, SourceKind, document::analyze_document,
31 options_json::analysis_options_from_json_value, source_descriptor_for_kind,
32 source_discarded_after_limit_change_diagnostic, source_limit_diagnostic_for_len,
33};
34use merman_editor_core::DocumentKind;
35use std::hash::{Hash, Hasher};
36use std::sync::Arc;
37use std::sync::atomic::{AtomicBool, Ordering};
38use tokio::sync::Mutex;
39use tower_lsp::jsonrpc::Result;
40use tower_lsp::lsp_types::{
41 CodeActionKind, CodeActionOptions, CodeActionParams, CodeActionProviderCapability,
42 CodeActionResponse, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse,
43 Diagnostic, DiagnosticOptions, DiagnosticServerCapabilities, DiagnosticSeverity,
44 DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
45 DidSaveTextDocumentParams, DocumentDiagnosticParams, DocumentDiagnosticReport,
46 DocumentDiagnosticReportResult, DocumentSymbolParams, DocumentSymbolResponse, FoldingRange,
47 FoldingRangeParams, FoldingRangeProviderCapability, FullDocumentDiagnosticReport,
48 GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverParams, HoverProviderCapability,
49 InitializeParams, InitializeResult, MessageType, NumberOrString, OneOf, Position,
50 PrepareRenameResponse, Range, ReferenceParams, RelatedFullDocumentDiagnosticReport,
51 RelatedUnchangedDocumentDiagnosticReport, RenameOptions, RenameParams, SelectionRange,
52 SelectionRangeParams, SelectionRangeProviderCapability, SemanticTokensDeltaParams,
53 SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
54 SemanticTokensRangeResult, SemanticTokensResult, SemanticTokensServerCapabilities,
55 ServerCapabilities, TextDocumentPositionParams, TextDocumentSyncCapability,
56 TextDocumentSyncKind, UnchangedDocumentDiagnosticReport, WorkspaceEdit, WorkspaceSymbolParams,
57};
58use tower_lsp::{Client, ClientSocket, LanguageServer, LspService};
59
60const MAX_DIAGNOSTIC_RECOMPUTE_ATTEMPTS: usize = 3;
61
62#[derive(Debug)]
63pub struct MermanLanguageServer {
64 client: Client,
65 store: Arc<Mutex<DocumentStore>>,
66 semantic_tokens_refresh_supported: AtomicBool,
67 diagnostic_pull_supported: AtomicBool,
68 diagnostic_refresh_supported: AtomicBool,
69 workspace_edit_document_changes_supported: AtomicBool,
70 hierarchical_document_symbols_supported: AtomicBool,
71}
72
73impl MermanLanguageServer {
74 pub fn new(client: Client) -> Self {
75 Self {
76 client,
77 store: Arc::new(Mutex::new(DocumentStore::new())),
78 semantic_tokens_refresh_supported: AtomicBool::new(false),
79 diagnostic_pull_supported: AtomicBool::new(false),
80 diagnostic_refresh_supported: AtomicBool::new(false),
81 workspace_edit_document_changes_supported: AtomicBool::new(false),
82 hierarchical_document_symbols_supported: AtomicBool::new(false),
83 }
84 }
85
86 pub fn service() -> (LspService<Self>, ClientSocket) {
87 LspService::build(Self::new)
88 .custom_method(RULE_CATALOG_METHOD, Self::rule_catalog)
89 .custom_method(CONFIG_SCHEMA_METHOD, Self::config_schema)
90 .finish()
91 }
92
93 pub fn capabilities() -> ServerCapabilities {
94 ServerCapabilities {
95 text_document_sync: Some(TextDocumentSyncCapability::Kind(
96 TextDocumentSyncKind::INCREMENTAL,
97 )),
98 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
99 hover_provider: Some(HoverProviderCapability::Simple(true)),
100 completion_provider: Some(CompletionOptions {
101 resolve_provider: Some(true),
102 trigger_characters: Some(vec![
103 " ".to_string(),
104 "\n".to_string(),
105 "-".to_string(),
106 ">".to_string(),
107 "%".to_string(),
108 "[".to_string(),
109 "(".to_string(),
110 "{".to_string(),
111 "/".to_string(),
112 "\\".to_string(),
113 "@".to_string(),
114 ":".to_string(),
115 ]),
116 ..CompletionOptions::default()
117 }),
118 definition_provider: Some(OneOf::Left(true)),
119 references_provider: Some(OneOf::Left(true)),
120 rename_provider: Some(OneOf::Right(RenameOptions {
121 prepare_provider: Some(true),
122 work_done_progress_options: Default::default(),
123 })),
124 document_symbol_provider: Some(OneOf::Left(true)),
125 workspace_symbol_provider: Some(OneOf::Left(true)),
126 diagnostic_provider: Some(DiagnosticServerCapabilities::Options(
127 Self::diagnostic_options(),
128 )),
129 code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions {
130 code_action_kinds: Some(vec![CodeActionKind::QUICKFIX]),
131 work_done_progress_options: Default::default(),
132 resolve_provider: Some(false),
133 })),
134 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
135 semantic_tokens_provider: Some(
136 SemanticTokensServerCapabilities::SemanticTokensOptions(semantic_tokens_options()),
137 ),
138 experimental: Some(experimental_capabilities()),
139 ..ServerCapabilities::default()
140 }
141 }
142
143 pub async fn rule_catalog(&self) -> Result<RuleCatalogResponse> {
144 Ok(RuleCatalogResponse::current())
145 }
146
147 pub async fn config_schema(&self) -> Result<ConfigSchemaResponse> {
148 Ok(ConfigSchemaResponse::current())
149 }
150
151 fn diagnostic_options() -> DiagnosticOptions {
152 DiagnosticOptions {
153 identifier: Some("merman".to_string()),
154 inter_file_dependencies: false,
155 workspace_diagnostics: false,
156 work_done_progress_options: Default::default(),
157 }
158 }
159
160 #[cfg(test)]
161 async fn snapshot_for_uri(
162 &self,
163 uri: &tower_lsp::lsp_types::Url,
164 ) -> Option<Arc<DocumentSnapshot>> {
165 snapshot_context::snapshot_context_for_uri(&self.store, uri, SnapshotContextKind::Structure)
166 .await
167 .ok()
168 .flatten()
169 .map(|context| context.snapshot)
170 }
171
172 async fn structure_snapshot_result<T>(
173 &self,
174 uri: &tower_lsp::lsp_types::Url,
175 compute: impl FnOnce(&DocumentSnapshot) -> Result<Option<T>>,
176 ) -> Result<Option<T>> {
177 snapshot_context::snapshot_result(&self.store, uri, SnapshotContextKind::Structure, compute)
178 .await
179 }
180
181 async fn semantic_snapshot_context_for_uri(
182 &self,
183 uri: &tower_lsp::lsp_types::Url,
184 ) -> Result<Option<SnapshotContext>> {
185 snapshot_context::snapshot_context_for_uri(
186 &self.store,
187 uri,
188 SnapshotContextKind::SemanticTokens,
189 )
190 .await
191 }
192
193 fn diagnostics_for_document(document: &StoredDocument, analyzer: &Analyzer) -> Vec<Diagnostic> {
194 let source = source_descriptor_for_document(&document.uri, document.kind);
195 let payload = if let Some(resource_limit) = document.resource_limit {
196 AnalysisPayload::new(
197 source,
198 vec![source_limit_diagnostic_for_len(
199 resource_limit.source_len,
200 resource_limit.max_source_bytes,
201 )],
202 )
203 } else if let Some(discarded_source) = document.discarded_source {
204 AnalysisPayload::new(
205 source,
206 vec![source_discarded_after_limit_change_diagnostic(
207 discarded_source.source_len,
208 discarded_source.previous_max_source_bytes,
209 )],
210 )
211 } else if let Some(sync_error) = document.sync_error {
212 return vec![document_sync_error_diagnostic(sync_error, document.version)];
213 } else {
214 analyze_document(document.text.as_ref(), analyzer, source)
215 };
216 analysis_payload_to_versioned_diagnostics(&payload, &document.uri, document.version)
217 }
218
219 fn diagnostic_result_id(diagnostics: &[tower_lsp::lsp_types::Diagnostic]) -> String {
220 let serialized = serde_json::to_vec(diagnostics).unwrap_or_default();
221 let mut hasher = std::collections::hash_map::DefaultHasher::new();
222 serialized.hash(&mut hasher);
223 format!("{:016x}", hasher.finish())
224 }
225
226 fn document_diagnostic_report(
227 diagnostics: Vec<tower_lsp::lsp_types::Diagnostic>,
228 result_id: Option<String>,
229 previous_result_id: Option<&str>,
230 ) -> DocumentDiagnosticReportResult {
231 if let Some(result_id) = result_id.clone()
232 && previous_result_id == Some(result_id.as_str())
233 {
234 return DocumentDiagnosticReportResult::Report(DocumentDiagnosticReport::Unchanged(
235 RelatedUnchangedDocumentDiagnosticReport {
236 related_documents: None,
237 unchanged_document_diagnostic_report: UnchangedDocumentDiagnosticReport {
238 result_id,
239 },
240 },
241 ));
242 }
243
244 DocumentDiagnosticReportResult::Report(DocumentDiagnosticReport::Full(
245 RelatedFullDocumentDiagnosticReport {
246 related_documents: None,
247 full_document_diagnostic_report: FullDocumentDiagnosticReport {
248 result_id,
249 items: diagnostics,
250 },
251 },
252 ))
253 }
254
255 async fn diagnostics_for_current_context(
256 &self,
257 context: &DiagnosticContext,
258 ) -> Option<Vec<tower_lsp::lsp_types::Diagnostic>> {
259 let diagnostics = Self::diagnostics_for_document(&context.document, &context.analyzer);
260 let store = self.store.lock().await;
261 store
262 .is_diagnostic_context_current(context)
263 .then_some(diagnostics)
264 }
265
266 async fn diagnostics_or_recompute_latest(
267 &self,
268 mut context: DiagnosticContext,
269 ) -> Result<(DiagnosticContext, Vec<tower_lsp::lsp_types::Diagnostic>)> {
270 for _ in 0..MAX_DIAGNOSTIC_RECOMPUTE_ATTEMPTS {
271 if let Some(diagnostics) = self.diagnostics_for_current_context(&context).await {
272 return Ok((context, diagnostics));
273 }
274
275 let Some(latest_context) = ({
276 let store = self.store.lock().await;
277 store.diagnostic_context(&context.document.uri)
278 }) else {
279 return Ok((context, Vec::new()));
280 };
281 context = latest_context;
282 }
283
284 Err(stale_diagnostic_recompute_error())
285 }
286
287 async fn commit_diagnostic_state_if_current(
288 &self,
289 context: &DiagnosticContext,
290 state: DocumentDiagnosticState,
291 ) -> Result<()> {
292 let mut store = self.store.lock().await;
293 if store.set_diagnostic_state_if_current(context, state) {
294 Ok(())
295 } else {
296 Err(stale_diagnostic_recompute_error())
297 }
298 }
299
300 async fn publish_for_uri(&self, uri: &tower_lsp::lsp_types::Url) {
301 if self.diagnostic_pull_supported.load(Ordering::Relaxed) {
302 return;
303 }
304
305 let context = {
306 let store = self.store.lock().await;
307 store.diagnostic_context(uri)
308 };
309
310 let Some(context) = context else {
311 return;
312 };
313
314 self.publish_current_diagnostics(&context).await;
315 }
316
317 async fn publish_current_diagnostics(&self, context: &DiagnosticContext) {
318 if let Some(diagnostics) = self.diagnostics_for_current_context(context).await {
319 self.client
320 .publish_diagnostics(
321 context.document.uri.clone(),
322 diagnostics,
323 Some(context.document.version),
324 )
325 .await;
326 }
327 }
328
329 async fn record_semantic_tokens_state(
330 &self,
331 context: &SnapshotContext,
332 tokens: Vec<tower_lsp::lsp_types::SemanticToken>,
333 result_id: Option<String>,
334 ) -> Result<()> {
335 let mut store = self.store.lock().await;
336 if store.set_semantic_tokens_state_if_current(
337 context,
338 SemanticTokensState::new(result_id, tokens),
339 ) {
340 Ok(())
341 } else {
342 Err(SnapshotContextKind::SemanticTokens.stale_error())
343 }
344 }
345
346 async fn ensure_workspace_symbol_snapshots_current(
347 &self,
348 contexts: &[SnapshotContext],
349 ) -> Result<()> {
350 let store = self.store.lock().await;
351 if store.workspace_symbol_snapshot_contexts_current(contexts) {
352 Ok(())
353 } else {
354 Err(SnapshotContextKind::WorkspaceSymbols.stale_error())
355 }
356 }
357
358 async fn workspace_symbol_snapshot_contexts(&self) -> Result<Vec<SnapshotContext>> {
359 loop {
360 let plan = {
361 let store = self.store.lock().await;
362 store.workspace_symbol_snapshot_build_plan(WORKSPACE_SYMBOL_SNAPSHOT_BATCH_SIZE)
363 };
364
365 if plan.batches.is_empty() {
366 return Ok(plan.contexts);
367 }
368
369 for batch in plan.batches {
370 let built = batch
371 .into_iter()
372 .map(|request| {
373 let snapshot = request.build();
374 (request, snapshot)
375 })
376 .collect();
377 let commit = self
378 .store
379 .lock()
380 .await
381 .snapshot_contexts_for_requests(built);
382 if commit.stale_open_documents {
383 return Err(SnapshotContextKind::WorkspaceSymbols.stale_error());
384 }
385 tokio::task::yield_now().await;
387 }
388 }
389 }
390
391 async fn replace_analyzer(&self, options: AnalysisOptions) -> AnalyzerConfigurationChange {
392 let mut store = self.store.lock().await;
393 store.apply_analyzer_options(options)
394 }
395
396 fn client_supports_semantic_tokens_refresh(params: &InitializeParams) -> bool {
397 params
398 .capabilities
399 .workspace
400 .as_ref()
401 .and_then(|workspace| workspace.semantic_tokens.as_ref())
402 .and_then(|semantic_tokens| semantic_tokens.refresh_support)
403 .unwrap_or(false)
404 }
405
406 fn client_supports_diagnostic_pull(params: &InitializeParams) -> bool {
407 params
408 .capabilities
409 .text_document
410 .as_ref()
411 .and_then(|text_document| text_document.diagnostic.as_ref())
412 .is_some()
413 }
414
415 fn client_supports_diagnostic_refresh(params: &InitializeParams) -> bool {
416 params
417 .capabilities
418 .workspace
419 .as_ref()
420 .and_then(|workspace| workspace.diagnostic.as_ref())
421 .and_then(|diagnostic| diagnostic.refresh_support)
422 .unwrap_or(false)
423 }
424
425 fn client_supports_workspace_edit_document_changes(params: &InitializeParams) -> bool {
426 params
427 .capabilities
428 .workspace
429 .as_ref()
430 .and_then(|workspace| workspace.workspace_edit.as_ref())
431 .and_then(|workspace_edit| workspace_edit.document_changes)
432 .unwrap_or(false)
433 }
434
435 fn client_supports_hierarchical_document_symbols(params: &InitializeParams) -> bool {
436 params
437 .capabilities
438 .text_document
439 .as_ref()
440 .and_then(|text_document| text_document.document_symbol.as_ref())
441 .and_then(|document_symbol| document_symbol.hierarchical_document_symbol_support)
442 .unwrap_or(false)
443 }
444
445 async fn apply_initialization_options(
446 &self,
447 initialization_options: Option<serde_json::Value>,
448 ) -> tower_lsp::jsonrpc::Result<()> {
449 match initialization_options {
450 None => {
451 self.replace_analyzer(default_lsp_analysis_options()).await;
452 Ok(())
453 }
454 Some(value) => {
455 let options = analysis_options_with_lsp_resource_defaults(
456 analysis_options_from_json_value(&value).map_err(|err| {
457 tower_lsp::jsonrpc::Error::invalid_params(err.to_string())
458 })?,
459 );
460 self.replace_analyzer(options).await;
461 Ok(())
462 }
463 }
464 }
465
466 async fn republish_all(&self) {
467 if self.diagnostic_pull_supported.load(Ordering::Relaxed) {
468 return;
469 }
470
471 let contexts = {
472 let store = self.store.lock().await;
473 store.diagnostic_contexts()
474 };
475
476 for context in contexts {
477 self.publish_current_diagnostics(&context).await;
478 }
479 }
480}
481
482#[tower_lsp::async_trait]
483impl LanguageServer for MermanLanguageServer {
484 async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
485 self.semantic_tokens_refresh_supported.store(
486 Self::client_supports_semantic_tokens_refresh(¶ms),
487 Ordering::Relaxed,
488 );
489 self.diagnostic_pull_supported.store(
490 Self::client_supports_diagnostic_pull(¶ms),
491 Ordering::Relaxed,
492 );
493 self.diagnostic_refresh_supported.store(
494 Self::client_supports_diagnostic_refresh(¶ms),
495 Ordering::Relaxed,
496 );
497 self.workspace_edit_document_changes_supported.store(
498 Self::client_supports_workspace_edit_document_changes(¶ms),
499 Ordering::Relaxed,
500 );
501 self.hierarchical_document_symbols_supported.store(
502 Self::client_supports_hierarchical_document_symbols(¶ms),
503 Ordering::Relaxed,
504 );
505 self.apply_initialization_options(params.initialization_options)
506 .await?;
507 Ok(InitializeResult {
508 capabilities: Self::capabilities(),
509 ..InitializeResult::default()
510 })
511 }
512
513 async fn initialized(&self, _: tower_lsp::lsp_types::InitializedParams) {
514 self.client
515 .log_message(MessageType::INFO, "merman-lsp initialized")
516 .await;
517 }
518
519 async fn shutdown(&self) -> Result<()> {
520 Ok(())
521 }
522
523 async fn did_open(&self, params: DidOpenTextDocumentParams) {
524 let doc = params.text_document;
525 let kind = document_kind_for_language_id(&doc.language_id, &doc.uri);
526 self.store
527 .lock()
528 .await
529 .open_text(doc.uri.clone(), doc.version, doc.text, kind);
530 self.publish_for_uri(&doc.uri).await;
531 }
532
533 async fn did_change(&self, params: DidChangeTextDocumentParams) {
534 let doc = params.text_document;
535 let update = self.store.lock().await.apply_text_changes(
536 doc.uri.clone(),
537 doc.version,
538 params.content_changes,
539 );
540 if update.affects_document_state() {
541 self.publish_for_uri(&doc.uri).await;
542 }
543 }
544
545 async fn did_save(&self, params: DidSaveTextDocumentParams) {
546 let uri = params.text_document.uri;
547 self.publish_for_uri(&uri).await;
548 }
549
550 async fn did_close(&self, params: DidCloseTextDocumentParams) {
551 let uri = params.text_document.uri;
552 self.store.lock().await.remove(&uri);
553 if !self.diagnostic_pull_supported.load(Ordering::Relaxed) {
554 self.client.publish_diagnostics(uri, Vec::new(), None).await;
555 }
556 }
557
558 async fn did_change_configuration(
559 &self,
560 params: tower_lsp::lsp_types::DidChangeConfigurationParams,
561 ) {
562 let options = if params.settings.is_null() {
563 default_lsp_analysis_options()
564 } else {
565 match analysis_options_from_json_value(¶ms.settings) {
566 Ok(options) => analysis_options_with_lsp_resource_defaults(options),
567 Err(err) => {
568 self.client
569 .log_message(
570 MessageType::ERROR,
571 format!("invalid merman analysis settings: {err}"),
572 )
573 .await;
574 return;
575 }
576 }
577 };
578
579 let change = self.replace_analyzer(options).await;
580 if change.affects_diagnostics() {
581 self.republish_all().await;
582 }
583 if change.affects_snapshots()
584 && self
585 .semantic_tokens_refresh_supported
586 .load(Ordering::Relaxed)
587 {
588 let _ = self.client.semantic_tokens_refresh().await;
589 }
590 if change.affects_diagnostics()
591 && self.diagnostic_pull_supported.load(Ordering::Relaxed)
592 && self.diagnostic_refresh_supported.load(Ordering::Relaxed)
593 {
594 let _ = self.client.workspace_diagnostic_refresh().await;
595 }
596 }
597
598 async fn diagnostic(
599 &self,
600 params: DocumentDiagnosticParams,
601 ) -> Result<DocumentDiagnosticReportResult> {
602 let uri = params.text_document.uri;
603 let previous_result_id = params.previous_result_id.as_deref();
604 let (context, cached) = {
605 let store = self.store.lock().await;
606 (store.diagnostic_context(&uri), store.diagnostic_state(&uri))
607 };
608 if let Some(cached) = cached {
609 return Ok(Self::document_diagnostic_report(
610 cached.diagnostics,
611 Some(cached.result_id),
612 previous_result_id,
613 ));
614 }
615
616 let Some(context) = context else {
617 let diagnostics = Vec::new();
618 let result_id = Some(Self::diagnostic_result_id(&diagnostics));
619 return Ok(Self::document_diagnostic_report(
620 diagnostics,
621 result_id,
622 previous_result_id,
623 ));
624 };
625
626 let (context, diagnostics) = self.diagnostics_or_recompute_latest(context).await?;
627 let state = DocumentDiagnosticState {
628 result_id: Self::diagnostic_result_id(&diagnostics),
629 diagnostics,
630 };
631 self.commit_diagnostic_state_if_current(&context, state.clone())
632 .await?;
633 Ok(Self::document_diagnostic_report(
634 state.diagnostics,
635 Some(state.result_id),
636 previous_result_id,
637 ))
638 }
639
640 async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
641 let uri = params.text_document_position.text_document.uri;
642 let position = params.text_document_position.position;
643
644 self.structure_snapshot_result(&uri, |snapshot| {
645 Ok(Some(CompletionResponse::List(completion_for_snapshot(
646 snapshot, position,
647 ))))
648 })
649 .await
650 }
651
652 async fn completion_resolve(&self, item: CompletionItem) -> Result<CompletionItem> {
653 Ok(resolve_completion_item(item))
654 }
655
656 async fn code_action(&self, params: CodeActionParams) -> Result<Option<CodeActionResponse>> {
657 let current_document_version = {
658 let store = self.store.lock().await;
659 store
660 .get(¶ms.text_document.uri)
661 .map(|document| document.version)
662 };
663 let workspace_edit_encoding = WorkspaceEditEncoding::from_document_changes_support(
664 self.workspace_edit_document_changes_supported
665 .load(Ordering::Relaxed),
666 );
667 Ok(code_actions_for_params_with_encoding(
668 ¶ms,
669 current_document_version,
670 workspace_edit_encoding,
671 ))
672 }
673
674 async fn semantic_tokens_full(
675 &self,
676 params: SemanticTokensParams,
677 ) -> Result<Option<SemanticTokensResult>> {
678 let uri = params.text_document.uri;
679 let snapshot_context = self.semantic_snapshot_context_for_uri(&uri).await?;
680
681 let Some(snapshot_context) = snapshot_context else {
682 return Ok(None);
683 };
684 let snapshot = &snapshot_context.snapshot;
685
686 let mut tokens = semantic_tokens_for_snapshot(snapshot);
687 let result_id = semantic_tokens_result_id(snapshot, &tokens.data);
688 tokens.result_id = Some(result_id.clone());
689 self.record_semantic_tokens_state(&snapshot_context, tokens.data.clone(), Some(result_id))
690 .await?;
691
692 Ok(Some(SemanticTokensResult::Tokens(tokens)))
693 }
694
695 async fn semantic_tokens_full_delta(
696 &self,
697 params: SemanticTokensDeltaParams,
698 ) -> Result<Option<SemanticTokensFullDeltaResult>> {
699 let uri = params.text_document.uri;
700 let snapshot_context = self.semantic_snapshot_context_for_uri(&uri).await?;
701 let Some(snapshot_context) = snapshot_context else {
702 return Ok(None);
703 };
704 let snapshot = &snapshot_context.snapshot;
705
706 let current_tokens = semantic_tokens_for_snapshot(snapshot);
707 let current_result_id = semantic_tokens_result_id(snapshot, ¤t_tokens.data);
708 let previous = {
709 let store = self.store.lock().await;
710 store.semantic_tokens_state_for_delta(&uri, params.previous_result_id.as_str())
711 };
712 let delta = match previous {
713 Some(previous) => semantic_tokens_delta_result(
714 &previous.tokens,
715 ¤t_tokens.data,
716 current_result_id.clone(),
717 ),
718 _ => {
719 let mut tokens = current_tokens.clone();
720 tokens.result_id = Some(current_result_id.clone());
721 SemanticTokensFullDeltaResult::Tokens(tokens)
722 }
723 };
724
725 self.record_semantic_tokens_state(
726 &snapshot_context,
727 current_tokens.data,
728 Some(current_result_id),
729 )
730 .await?;
731
732 Ok(Some(delta))
733 }
734
735 async fn semantic_tokens_range(
736 &self,
737 params: SemanticTokensRangeParams,
738 ) -> Result<Option<SemanticTokensRangeResult>> {
739 let uri = params.text_document.uri;
740 let snapshot_context = self.semantic_snapshot_context_for_uri(&uri).await?;
741
742 let Some(snapshot_context) = snapshot_context else {
743 return Ok(None);
744 };
745 let result = semantic_tokens_for_snapshot_range(&snapshot_context.snapshot, params.range);
746 snapshot_context::ensure_snapshot_current(
747 &self.store,
748 &snapshot_context,
749 SnapshotContextKind::SemanticTokens,
750 )
751 .await?;
752
753 Ok(Some(result.into()))
754 }
755
756 async fn hover(&self, params: HoverParams) -> Result<Option<Hover>> {
757 let uri = params.text_document_position_params.text_document.uri;
758 let position = params.text_document_position_params.position;
759
760 self.structure_snapshot_result(&uri, |snapshot| Ok(structure_hover(snapshot, position)))
761 .await
762 }
763
764 async fn selection_range(
765 &self,
766 params: SelectionRangeParams,
767 ) -> Result<Option<Vec<SelectionRange>>> {
768 let uri = params.text_document.uri;
769
770 self.structure_snapshot_result(&uri, |snapshot| {
771 Ok(structure_selection_ranges(snapshot, ¶ms.positions))
772 })
773 .await
774 }
775
776 async fn folding_range(&self, params: FoldingRangeParams) -> Result<Option<Vec<FoldingRange>>> {
777 let uri = params.text_document.uri;
778
779 self.structure_snapshot_result(&uri, |snapshot| {
780 Ok(Some(structure_folding_ranges(snapshot)))
781 })
782 .await
783 }
784
785 async fn document_symbol(
786 &self,
787 params: DocumentSymbolParams,
788 ) -> Result<Option<DocumentSymbolResponse>> {
789 let uri = params.text_document.uri;
790 let hierarchical_supported = self
791 .hierarchical_document_symbols_supported
792 .load(Ordering::Relaxed);
793
794 self.structure_snapshot_result(&uri, |snapshot| {
795 Ok(Some(structure_document_symbols_with_hierarchy_support(
796 snapshot,
797 hierarchical_supported,
798 )))
799 })
800 .await
801 }
802
803 async fn goto_definition(
804 &self,
805 params: GotoDefinitionParams,
806 ) -> Result<Option<GotoDefinitionResponse>> {
807 let uri = params.text_document_position_params.text_document.uri;
808 let position = params.text_document_position_params.position;
809
810 self.structure_snapshot_result(&uri, |snapshot| {
811 Ok(structure_goto_definition(snapshot, position))
812 })
813 .await
814 }
815
816 async fn references(
817 &self,
818 params: ReferenceParams,
819 ) -> Result<Option<Vec<tower_lsp::lsp_types::Location>>> {
820 let uri = params.text_document_position.text_document.uri;
821 let position = params.text_document_position.position;
822
823 self.structure_snapshot_result(&uri, |snapshot| {
824 Ok(structure_references(
825 snapshot,
826 position,
827 params.context.include_declaration,
828 ))
829 })
830 .await
831 }
832
833 async fn prepare_rename(
834 &self,
835 params: TextDocumentPositionParams,
836 ) -> Result<Option<PrepareRenameResponse>> {
837 let uri = params.text_document.uri;
838 let position = params.position;
839
840 self.structure_snapshot_result(&uri, |snapshot| {
841 Ok(structure_prepare_rename(snapshot, position))
842 })
843 .await
844 }
845
846 async fn rename(&self, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
847 let uri = params.text_document_position.text_document.uri.clone();
848 let workspace_edit_encoding = WorkspaceEditEncoding::from_document_changes_support(
849 self.workspace_edit_document_changes_supported
850 .load(Ordering::Relaxed),
851 );
852
853 self.structure_snapshot_result(&uri, |snapshot| {
854 structure_rename_with_workspace_edit_encoding(snapshot, params, workspace_edit_encoding)
855 })
856 .await
857 }
858
859 async fn symbol(
860 &self,
861 params: WorkspaceSymbolParams,
862 ) -> Result<Option<Vec<tower_lsp::lsp_types::SymbolInformation>>> {
863 let contexts = self.workspace_symbol_snapshot_contexts().await?;
864
865 let snapshots = contexts
866 .iter()
867 .map(|context| Arc::clone(&context.snapshot))
868 .collect::<Vec<_>>();
869 let symbols = structure_workspace_symbols_for_snapshots(&snapshots, ¶ms.query);
870
871 self.ensure_workspace_symbol_snapshots_current(&contexts)
872 .await?;
873
874 Ok(Some(symbols))
875 }
876}
877
878fn stale_diagnostic_recompute_error() -> tower_lsp::jsonrpc::Error {
879 let mut error = tower_lsp::jsonrpc::Error::content_modified();
880 error.message = "diagnostic document changed while recomputing".into();
881 error
882}
883
884fn document_sync_error_diagnostic(
885 sync_error: DocumentSyncError,
886 document_version: i32,
887) -> Diagnostic {
888 let message = match sync_error {
889 DocumentSyncError::InvalidIncrementalRange => {
890 "document text is out of sync after an invalid incremental edit range; send a full document replacement or reopen the document"
891 }
892 };
893 Diagnostic {
894 range: Range::new(Position::new(0, 0), Position::new(0, 0)),
895 severity: Some(DiagnosticSeverity::ERROR),
896 code: Some(NumberOrString::String(
897 "merman.lsp.document_sync_lost".to_string(),
898 )),
899 source: Some("merman".to_string()),
900 message: message.to_string(),
901 related_information: None,
902 tags: None,
903 code_description: None,
904 data: Some(serde_json::json!({
905 "documentVersion": document_version,
906 })),
907 }
908}
909
910fn source_descriptor_for_document(
911 uri: &tower_lsp::lsp_types::Url,
912 kind: DocumentKind,
913) -> merman_analysis::SourceDescriptor {
914 let source_kind = match kind {
915 DocumentKind::Diagram => SourceKind::Diagram,
916 DocumentKind::Markdown => SourceKind::Markdown,
917 DocumentKind::Mdx => SourceKind::Mdx,
918 };
919 source_descriptor_for_kind(Some(uri.as_str()), source_kind)
920}
921
922fn document_kind_for_language_id(
923 language_id: &str,
924 uri: &tower_lsp::lsp_types::Url,
925) -> DocumentKind {
926 match language_id {
927 "markdown" => DocumentKind::Markdown,
928 "mdx" => DocumentKind::Mdx,
929 "mermaid" => DocumentKind::Diagram,
930 _ => DocumentKind::from_path(uri.path()),
931 }
932}
933
934#[cfg(test)]
935mod tests;