1use std::sync::Arc;
2
3use tower_lsp::jsonrpc::Result;
4use tower_lsp::lsp_types::*;
5use tower_lsp::{Client, LanguageServer};
6
7use crate::document::DocumentState;
8use crate::flycheck::{CargoFlycheck, Flycheck, FlycheckRunner};
9use crate::scheduler::{Scheduler, DEBOUNCE};
10use crate::semantic_tokens;
11use crate::state::ServerState;
12use crate::workspace::WorkspaceIndex;
13
14pub struct LogicAffeineServer {
15 client: Client,
16 state: Arc<ServerState>,
17 scheduler: Arc<Scheduler>,
18 workspace_index: Arc<WorkspaceIndex>,
19 workspace_roots: std::sync::Mutex<Vec<std::path::PathBuf>>,
20 flycheck: Arc<Flycheck>,
21 flycheck_enabled: std::sync::atomic::AtomicBool,
23}
24
25impl LogicAffeineServer {
26 pub fn new(client: Client) -> Self {
27 Self::with_flycheck(client, Box::new(CargoFlycheck::new()))
28 }
29
30 pub fn with_flycheck(client: Client, runner: Box<dyn FlycheckRunner>) -> Self {
33 LogicAffeineServer {
34 client,
35 state: Arc::new(ServerState::new()),
36 scheduler: Arc::new(Scheduler::new()),
37 workspace_index: Arc::new(WorkspaceIndex::new()),
38 workspace_roots: std::sync::Mutex::new(Vec::new()),
39 flycheck: Arc::new(Flycheck::new(runner)),
40 flycheck_enabled: std::sync::atomic::AtomicBool::new(true),
41 }
42 }
43
44 fn workspace_key(&self, uri: &Url) -> String {
47 if let Some(root) = self.workspace_roots.lock().unwrap().first() {
48 return root.display().to_string();
49 }
50 uri.to_file_path()
51 .ok()
52 .and_then(|p| p.parent().map(|d| d.display().to_string()))
53 .unwrap_or_else(|| uri.to_string())
54 }
55
56 async fn analyze_and_publish(
59 client: Client,
60 state: Arc<ServerState>,
61 scheduler: Arc<Scheduler>,
62 flycheck: Arc<Flycheck>,
63 uri: Url,
64 text: String,
65 version: i32,
66 generation: u64,
67 ) {
68 let analysis_uri = uri.clone();
69 let document = match tokio::task::spawn_blocking(move || {
70 DocumentState::with_uri(text, version, Some(&analysis_uri))
71 })
72 .await
73 {
74 Ok(document) => document,
75 Err(join_error) => {
76 log::error!("analysis panicked for {uri}: {join_error}");
80 return;
81 }
82 };
83
84 if !scheduler.is_current(&uri, generation) {
85 return;
86 }
87 let snapshot = state.install_snapshot(uri.clone(), document);
88 let mut diagnostics = snapshot.diagnostics.clone();
89 diagnostics.extend(flycheck.diagnostics_for(&uri));
90 client
91 .publish_diagnostics(uri, diagnostics, Some(snapshot.version))
92 .await;
93 }
94}
95
96#[tower_lsp::async_trait]
97impl LanguageServer for LogicAffeineServer {
98 async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
99 let mut roots = Vec::new();
100 if let Some(folders) = params.workspace_folders {
101 roots.extend(folders.iter().filter_map(|f| f.uri.to_file_path().ok()));
102 }
103 #[allow(deprecated)]
104 if roots.is_empty() {
105 if let Some(root) = params.root_uri.and_then(|u| u.to_file_path().ok()) {
106 roots.push(root);
107 }
108 }
109 *self.workspace_roots.lock().unwrap() = roots;
110
111 Ok(InitializeResult {
112 capabilities: ServerCapabilities {
113 text_document_sync: Some(TextDocumentSyncCapability::Options(
114 TextDocumentSyncOptions {
115 open_close: Some(true),
116 change: Some(TextDocumentSyncKind::INCREMENTAL),
117 save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions {
118 include_text: Some(false),
119 })),
120 ..Default::default()
121 },
122 )),
123 semantic_tokens_provider: Some(
124 SemanticTokensServerCapabilities::SemanticTokensOptions(
125 SemanticTokensOptions {
126 legend: semantic_tokens::legend(),
127 full: Some(SemanticTokensFullOptions::Delta { delta: Some(true) }),
128 range: Some(true),
129 ..Default::default()
130 },
131 ),
132 ),
133 document_symbol_provider: Some(OneOf::Left(true)),
134 definition_provider: Some(OneOf::Left(true)),
135 hover_provider: Some(HoverProviderCapability::Simple(true)),
136 completion_provider: Some(CompletionOptions {
137 trigger_characters: Some(vec![
138 ".".to_string(),
139 ":".to_string(),
140 "'".to_string(),
141 ]),
142 ..Default::default()
143 }),
144 references_provider: Some(OneOf::Left(true)),
145 signature_help_provider: Some(SignatureHelpOptions {
146 trigger_characters: Some(vec![
147 " ".to_string(),
148 ",".to_string(),
149 ]),
150 ..Default::default()
151 }),
152 code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
153 rename_provider: Some(OneOf::Right(RenameOptions {
154 prepare_provider: Some(true),
155 work_done_progress_options: Default::default(),
156 })),
157 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
158 inlay_hint_provider: Some(OneOf::Left(true)),
159 code_lens_provider: Some(CodeLensOptions {
160 resolve_provider: Some(false),
161 }),
162 document_formatting_provider: Some(OneOf::Left(true)),
163 document_range_formatting_provider: Some(OneOf::Left(true)),
164 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
165 first_trigger_character: ".".to_string(),
166 more_trigger_character: None,
167 }),
168 document_highlight_provider: Some(OneOf::Left(true)),
169 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
170 call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
171 diagnostic_provider: Some(DiagnosticServerCapabilities::Options(
172 DiagnosticOptions {
173 identifier: None,
174 inter_file_dependencies: false,
175 workspace_diagnostics: false,
176 work_done_progress_options: Default::default(),
177 },
178 )),
179 workspace_symbol_provider: Some(OneOf::Left(true)),
180 ..Default::default()
181 },
182 server_info: Some(ServerInfo {
183 name: "logicaffeine-lsp".to_string(),
184 version: Some(env!("CARGO_PKG_VERSION").to_string()),
185 }),
186 })
187 }
188
189 async fn initialized(&self, _: InitializedParams) {
190 log::info!("LogicAffeine LSP initialized");
191 let roots = self.workspace_roots.lock().unwrap().clone();
194 let index = Arc::clone(&self.workspace_index);
195 tokio::task::spawn_blocking(move || {
196 for root in roots {
197 index.scan_folder(&root);
198 }
199 });
200 }
201
202 async fn did_change_configuration(&self, params: DidChangeConfigurationParams) {
203 let enabled = params
205 .settings
206 .pointer("/logicaffeine/flycheck/enable")
207 .and_then(|v| v.as_bool())
208 .unwrap_or(true);
209 let was = self
210 .flycheck_enabled
211 .swap(enabled, std::sync::atomic::Ordering::SeqCst);
212 if was && !enabled {
213 let uris = self.scheduler.open_uris();
216 for uri in uris {
217 self.flycheck.forget(&uri);
218 if let Some(doc) = self.state.snapshot(&uri) {
219 self.client
220 .publish_diagnostics(uri, doc.diagnostics.clone(), Some(doc.version))
221 .await;
222 }
223 }
224 }
225 }
226
227 async fn did_save(&self, params: DidSaveTextDocumentParams) {
228 if !self
229 .flycheck_enabled
230 .load(std::sync::atomic::Ordering::SeqCst)
231 {
232 if let Ok(path) = params.text_document.uri.to_file_path() {
234 let index = Arc::clone(&self.workspace_index);
235 tokio::task::spawn_blocking(move || index.index_file(&path));
236 }
237 return;
238 }
239 let uri = params.text_document.uri;
240 if let Ok(path) = uri.to_file_path() {
241 let index = Arc::clone(&self.workspace_index);
242 tokio::task::spawn_blocking(move || index.index_file(&path));
243 }
244
245 let Some((text, version)) = self.scheduler.current_text(&uri) else {
248 return;
249 };
250 let generation = self.flycheck.begin_save(&uri);
251 let workspace_key = self.workspace_key(&uri);
252 let client = self.client.clone();
253 let state = Arc::clone(&self.state);
254 let flycheck = Arc::clone(&self.flycheck);
255 tokio::spawn(async move {
256 let run_text = text.clone();
257 let run_flycheck = Arc::clone(&flycheck);
258 let findings = tokio::task::spawn_blocking(move || {
259 run_flycheck.run(&run_text, &workspace_key)
260 })
261 .await
262 .expect("flycheck task panicked");
263
264 let Some(findings) = findings else { return };
266
267 let interactive = state
268 .snapshot(&uri)
269 .map(|doc| doc.diagnostics.clone())
270 .unwrap_or_default();
271 let Some(rustc_diagnostics) =
272 flycheck.complete(&uri, generation, findings, &text, &interactive)
273 else {
274 return; };
276
277 let mut diagnostics = interactive;
278 diagnostics.extend(rustc_diagnostics);
279 client
280 .publish_diagnostics(uri, diagnostics, Some(version))
281 .await;
282 });
283 }
284
285 async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) {
286 let index = Arc::clone(&self.workspace_index);
287 tokio::task::spawn_blocking(move || {
288 for change in params.changes {
289 match change.typ {
290 FileChangeType::DELETED => index.remove(&change.uri),
291 _ => {
292 if let Ok(path) = change.uri.to_file_path() {
293 index.index_file(&path);
294 }
295 }
296 }
297 }
298 });
299 }
300
301 async fn symbol(
302 &self,
303 params: WorkspaceSymbolParams,
304 ) -> Result<Option<Vec<SymbolInformation>>> {
305 let hits = self.workspace_index.query(¶ms.query, 256);
306 if hits.is_empty() {
307 return Ok(None);
308 }
309 #[allow(deprecated)]
310 Ok(Some(
311 hits.into_iter()
312 .map(|s| SymbolInformation {
313 name: s.name,
314 kind: s.kind,
315 tags: None,
316 deprecated: None,
317 location: s.location,
318 container_name: s.container,
319 })
320 .collect(),
321 ))
322 }
323
324 async fn shutdown(&self) -> Result<()> {
325 Ok(())
326 }
327
328 async fn did_open(&self, params: DidOpenTextDocumentParams) {
329 let uri = params.text_document.uri;
330 let text = params.text_document.text;
331 let version = params.text_document.version;
332
333 let generation = self.scheduler.open(uri.clone(), text.clone(), version);
334 Self::analyze_and_publish(
336 self.client.clone(),
337 Arc::clone(&self.state),
338 Arc::clone(&self.scheduler),
339 Arc::clone(&self.flycheck),
340 uri,
341 text,
342 version,
343 generation,
344 )
345 .await;
346 }
347
348 async fn did_change(&self, params: DidChangeTextDocumentParams) {
349 let uri = params.text_document.uri;
350 let version = params.text_document.version;
351
352 let Some(generation) =
353 self.scheduler
354 .apply_changes(&uri, params.content_changes, version)
355 else {
356 return;
357 };
358
359 self.flycheck.clear(&uri);
361
362 let client = self.client.clone();
365 let state = Arc::clone(&self.state);
366 let scheduler = Arc::clone(&self.scheduler);
367 let flycheck = Arc::clone(&self.flycheck);
368 tokio::spawn(async move {
369 tokio::time::sleep(DEBOUNCE).await;
370 let Some((text, version)) = scheduler.current_if(&uri, generation) else {
371 return;
372 };
373 Self::analyze_and_publish(
374 client, state, scheduler, flycheck, uri, text, version, generation,
375 )
376 .await;
377 });
378 }
379
380 async fn did_close(&self, params: DidCloseTextDocumentParams) {
381 self.scheduler.close(¶ms.text_document.uri);
382 self.state.close_document(¶ms.text_document.uri);
383 self.flycheck.forget(¶ms.text_document.uri);
384 self.client
386 .publish_diagnostics(params.text_document.uri, vec![], None)
387 .await;
388 }
389
390 async fn semantic_tokens_full(
391 &self,
392 params: SemanticTokensParams,
393 ) -> Result<Option<SemanticTokensResult>> {
394 let uri = ¶ms.text_document.uri;
395 let doc = match self.state.snapshot(uri) {
396 Some(doc) => doc,
397 None => return Ok(None),
398 };
399
400 let data = semantic_tokens::encode_document_tokens(&doc);
401 let result_id = self.state.cache_semantic_tokens(uri.clone(), data.clone());
402
403 Ok(Some(SemanticTokensResult::Tokens(SemanticTokens {
404 result_id: Some(result_id),
405 data,
406 })))
407 }
408
409 async fn semantic_tokens_full_delta(
410 &self,
411 params: SemanticTokensDeltaParams,
412 ) -> Result<Option<SemanticTokensFullDeltaResult>> {
413 let uri = ¶ms.text_document.uri;
414 let doc = match self.state.snapshot(uri) {
415 Some(doc) => doc,
416 None => return Ok(None),
417 };
418
419 let data = semantic_tokens::encode_document_tokens(&doc);
420 let previous = self.state.cached_semantic_tokens(uri);
421 let result_id = self.state.cache_semantic_tokens(uri.clone(), data.clone());
422
423 match previous {
424 Some((prev_id, prev_data)) if prev_id == params.previous_result_id => {
425 let edits = semantic_tokens::semantic_token_edits(&prev_data, &data);
426 Ok(Some(SemanticTokensFullDeltaResult::TokensDelta(
427 SemanticTokensDelta {
428 result_id: Some(result_id),
429 edits,
430 },
431 )))
432 }
433 _ => Ok(Some(SemanticTokensFullDeltaResult::Tokens(SemanticTokens {
435 result_id: Some(result_id),
436 data,
437 }))),
438 }
439 }
440
441 async fn semantic_tokens_range(
442 &self,
443 params: SemanticTokensRangeParams,
444 ) -> Result<Option<SemanticTokensRangeResult>> {
445 let uri = ¶ms.text_document.uri;
446 let doc = match self.state.snapshot(uri) {
447 Some(doc) => doc,
448 None => return Ok(None),
449 };
450
451 let start = doc.line_index.offset(params.range.start);
452 let end = doc.line_index.offset(params.range.end);
453 let data = semantic_tokens::encode_document_tokens_in_range(&doc, start, end);
454
455 Ok(Some(SemanticTokensRangeResult::Tokens(SemanticTokens {
456 result_id: None,
457 data,
458 })))
459 }
460
461 async fn document_symbol(
462 &self,
463 params: DocumentSymbolParams,
464 ) -> Result<Option<DocumentSymbolResponse>> {
465 let uri = ¶ms.text_document.uri;
466 let doc = match self.state.snapshot(uri) {
467 Some(doc) => doc,
468 None => return Ok(None),
469 };
470
471 let symbols = crate::document_symbols::document_symbols(&doc);
472 Ok(Some(DocumentSymbolResponse::Nested(symbols)))
473 }
474
475 async fn goto_definition(
476 &self,
477 params: GotoDefinitionParams,
478 ) -> Result<Option<GotoDefinitionResponse>> {
479 let uri = ¶ms.text_document_position_params.text_document.uri;
480 let position = params.text_document_position_params.position;
481
482 let doc = match self.state.snapshot(uri) {
483 Some(doc) => doc,
484 None => return Ok(None),
485 };
486
487 if let Some(local) = crate::definition::goto_definition(&doc, position, uri) {
488 return Ok(Some(local));
489 }
490
491 let offset = doc.line_index.offset(position);
494 let name = doc
495 .tokens
496 .iter()
497 .find(|t| t.span.start <= offset && offset < t.span.end)
498 .and_then(|t| crate::index::resolve_token_name(t, &doc.interner))
499 .map(|n| n.to_string());
500 Ok(name
501 .and_then(|n| self.workspace_index.definition_of(&n))
502 .map(GotoDefinitionResponse::Scalar))
503 }
504
505 async fn hover(&self, params: HoverParams) -> Result<Option<Hover>> {
506 let uri = ¶ms.text_document_position_params.text_document.uri;
507 let position = params.text_document_position_params.position;
508
509 let doc = match self.state.snapshot(uri) {
510 Some(doc) => doc,
511 None => return Ok(None),
512 };
513
514 Ok(crate::hover::hover(&doc, position))
515 }
516
517 async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
518 let uri = ¶ms.text_document_position.text_document.uri;
519 let position = params.text_document_position.position;
520
521 let doc = match self.state.snapshot(uri) {
522 Some(doc) => doc,
523 None => return Ok(None),
524 };
525
526 Ok(crate::completion::completions(&doc, position))
527 }
528
529 async fn references(&self, params: ReferenceParams) -> Result<Option<Vec<Location>>> {
530 let uri = ¶ms.text_document_position.text_document.uri;
531 let position = params.text_document_position.position;
532
533 let doc = match self.state.snapshot(uri) {
534 Some(doc) => doc,
535 None => return Ok(None),
536 };
537
538 let include_declaration = params.context.include_declaration;
539 let mut locations =
540 crate::references::find_references(&doc, position, uri, include_declaration);
541
542 if let Some(name) = crate::references::name_at(&doc, position) {
545 if crate::references::is_cross_file_symbol(&doc, &name) {
546 let open_docs = self.state.open_documents();
547 for (other_uri, other_doc) in &open_docs {
548 if other_uri == uri {
549 continue;
550 }
551 for range in crate::references::cross_file_candidates(
552 other_doc,
553 &name,
554 include_declaration,
555 ) {
556 locations.push(Location { uri: other_uri.clone(), range });
557 }
558 }
559 let skip: Vec<&Url> = open_docs.iter().map(|(u, _)| u).collect();
560 locations.extend(self.workspace_index.references_of(&name, &skip));
561 }
562 }
563
564 if locations.is_empty() {
565 Ok(None)
566 } else {
567 Ok(Some(locations))
568 }
569 }
570
571 async fn signature_help(&self, params: SignatureHelpParams) -> Result<Option<SignatureHelp>> {
572 let uri = ¶ms.text_document_position_params.text_document.uri;
573 let position = params.text_document_position_params.position;
574
575 let doc = match self.state.snapshot(uri) {
576 Some(doc) => doc,
577 None => return Ok(None),
578 };
579
580 Ok(crate::signature_help::signature_help(&doc, position))
581 }
582
583 async fn code_action(&self, params: CodeActionParams) -> Result<Option<CodeActionResponse>> {
584 let uri = ¶ms.text_document.uri;
585 let range = params.range;
586
587 let doc = match self.state.snapshot(uri) {
588 Some(doc) => doc,
589 None => return Ok(None),
590 };
591
592 let actions = crate::code_actions::code_actions(&doc, range, uri);
593 if actions.is_empty() {
594 Ok(None)
595 } else {
596 Ok(Some(actions))
597 }
598 }
599
600 async fn rename(&self, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
601 let uri = ¶ms.text_document_position.text_document.uri;
602 let position = params.text_document_position.position;
603
604 let doc = match self.state.snapshot(uri) {
605 Some(doc) => doc,
606 None => return Ok(None),
607 };
608
609 let new_name = params.new_name;
610 let mut edit = match crate::rename::rename(&doc, position, new_name.clone(), uri) {
611 Some(edit) => edit,
612 None => return Ok(None),
613 };
614
615 if let Some(name) = crate::references::name_at(&doc, position) {
618 if crate::references::is_cross_file_symbol(&doc, &name) {
619 let changes = edit.changes.get_or_insert_with(Default::default);
620 let open_docs = self.state.open_documents();
621 for (other_uri, other_doc) in &open_docs {
622 if other_uri == uri {
623 continue;
624 }
625 let ranges =
626 crate::references::cross_file_candidates(other_doc, &name, true);
627 if !ranges.is_empty() {
628 changes.entry(other_uri.clone()).or_default().extend(
629 ranges.into_iter().map(|range| TextEdit {
630 range,
631 new_text: new_name.clone(),
632 }),
633 );
634 }
635 }
636 let skip: Vec<&Url> = open_docs.iter().map(|(u, _)| u).collect();
637 for location in self.workspace_index.references_of(&name, &skip) {
638 changes.entry(location.uri).or_default().push(TextEdit {
639 range: location.range,
640 new_text: new_name.clone(),
641 });
642 }
643 }
644 }
645
646 Ok(Some(edit))
647 }
648
649 async fn prepare_rename(
650 &self,
651 params: TextDocumentPositionParams,
652 ) -> Result<Option<PrepareRenameResponse>> {
653 let uri = ¶ms.text_document.uri;
654 let position = params.position;
655
656 let doc = match self.state.snapshot(uri) {
657 Some(doc) => doc,
658 None => return Ok(None),
659 };
660
661 Ok(crate::rename::prepare_rename(&doc, position).map(|(range, text)| {
662 PrepareRenameResponse::RangeWithPlaceholder {
663 range,
664 placeholder: text,
665 }
666 }))
667 }
668
669 async fn folding_range(&self, params: FoldingRangeParams) -> Result<Option<Vec<FoldingRange>>> {
670 let uri = ¶ms.text_document.uri;
671
672 let doc = match self.state.snapshot(uri) {
673 Some(doc) => doc,
674 None => return Ok(None),
675 };
676
677 let ranges = crate::folding::folding_ranges(&doc);
678 if ranges.is_empty() {
679 Ok(None)
680 } else {
681 Ok(Some(ranges))
682 }
683 }
684
685 async fn inlay_hint(&self, params: InlayHintParams) -> Result<Option<Vec<InlayHint>>> {
686 let uri = ¶ms.text_document.uri;
687
688 let doc = match self.state.snapshot(uri) {
689 Some(doc) => doc,
690 None => return Ok(None),
691 };
692
693 let hints = crate::inlay_hints::inlay_hints(&doc, params.range);
694 if hints.is_empty() {
695 Ok(None)
696 } else {
697 Ok(Some(hints))
698 }
699 }
700
701 async fn code_lens(&self, params: CodeLensParams) -> Result<Option<Vec<CodeLens>>> {
702 let uri = ¶ms.text_document.uri;
703
704 let doc = match self.state.snapshot(uri) {
705 Some(doc) => doc,
706 None => return Ok(None),
707 };
708
709 let lenses = crate::code_lens::code_lenses(&doc, uri);
710 if lenses.is_empty() {
711 Ok(None)
712 } else {
713 Ok(Some(lenses))
714 }
715 }
716
717 async fn document_highlight(
718 &self,
719 params: DocumentHighlightParams,
720 ) -> Result<Option<Vec<DocumentHighlight>>> {
721 let uri = ¶ms.text_document_position_params.text_document.uri;
722 let doc = match self.state.snapshot(uri) {
723 Some(doc) => doc,
724 None => return Ok(None),
725 };
726 Ok(crate::document_highlights::document_highlights(
727 &doc,
728 params.text_document_position_params.position,
729 ))
730 }
731
732 async fn selection_range(
733 &self,
734 params: SelectionRangeParams,
735 ) -> Result<Option<Vec<SelectionRange>>> {
736 let doc = match self.state.snapshot(¶ms.text_document.uri) {
737 Some(doc) => doc,
738 None => return Ok(None),
739 };
740 Ok(Some(crate::selection_ranges::selection_ranges(
741 &doc,
742 ¶ms.positions,
743 )))
744 }
745
746 async fn on_type_formatting(
747 &self,
748 params: DocumentOnTypeFormattingParams,
749 ) -> Result<Option<Vec<TextEdit>>> {
750 let uri = ¶ms.text_document_position.text_document.uri;
751 let doc = match self.state.snapshot(uri) {
752 Some(doc) => doc,
753 None => return Ok(None),
754 };
755
756 let line_number = params.text_document_position.position.line as usize;
759 let Some(line) = doc.source.split('\n').nth(line_number) else {
760 return Ok(None);
761 };
762 let live = self
767 .scheduler
768 .current_text(uri)
769 .map(|(text, _)| text)
770 .unwrap_or_else(|| doc.source.clone());
771 let line_start_offset: usize = live
772 .split('\n')
773 .take(line_number)
774 .map(|l| l.len() + 1)
775 .sum();
776 let delimiters_before = live[..line_start_offset.min(live.len())]
777 .matches("\"\"\"")
778 .count();
779 if delimiters_before % 2 == 1 {
780 return Ok(None);
781 }
782 let formatted = logicaffeine_language::source_format::format_line(line);
783 if formatted == line {
784 return Ok(None);
785 }
786 let end_character = line.encode_utf16().count() as u32;
787 Ok(Some(vec![TextEdit {
788 range: Range {
789 start: Position { line: line_number as u32, character: 0 },
790 end: Position { line: line_number as u32, character: end_character },
791 },
792 new_text: formatted,
793 }]))
794 }
795
796 async fn prepare_call_hierarchy(
797 &self,
798 params: CallHierarchyPrepareParams,
799 ) -> Result<Option<Vec<CallHierarchyItem>>> {
800 let uri = ¶ms.text_document_position_params.text_document.uri;
801 let doc = match self.state.snapshot(uri) {
802 Some(doc) => doc,
803 None => return Ok(None),
804 };
805 Ok(crate::call_hierarchy::prepare(
806 &doc,
807 params.text_document_position_params.position,
808 uri,
809 ))
810 }
811
812 async fn incoming_calls(
813 &self,
814 params: CallHierarchyIncomingCallsParams,
815 ) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
816 let uri = ¶ms.item.uri;
817 let doc = match self.state.snapshot(uri) {
818 Some(doc) => doc,
819 None => return Ok(None),
820 };
821 Ok(Some(crate::call_hierarchy::incoming_calls(
822 &doc,
823 ¶ms.item,
824 uri,
825 )))
826 }
827
828 async fn outgoing_calls(
829 &self,
830 params: CallHierarchyOutgoingCallsParams,
831 ) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
832 let uri = ¶ms.item.uri;
833 let doc = match self.state.snapshot(uri) {
834 Some(doc) => doc,
835 None => return Ok(None),
836 };
837 Ok(Some(crate::call_hierarchy::outgoing_calls(
838 &doc,
839 ¶ms.item,
840 uri,
841 )))
842 }
843
844 async fn diagnostic(
845 &self,
846 params: DocumentDiagnosticParams,
847 ) -> Result<DocumentDiagnosticReportResult> {
848 let uri = ¶ms.text_document.uri;
849 let (items, result_id) = match self.state.snapshot(uri) {
850 Some(doc) => {
851 let mut items = doc.diagnostics.clone();
852 let rustc = self.flycheck.diagnostics_for(uri);
853 let result_id = format!("{}:{}", doc.version, rustc.len());
856 items.extend(rustc);
857 (items, result_id)
858 }
859 None => (Vec::new(), "closed".to_string()),
860 };
861
862 if params.previous_result_id.as_deref() == Some(result_id.as_str()) {
863 return Ok(DocumentDiagnosticReportResult::Report(
864 DocumentDiagnosticReport::Unchanged(RelatedUnchangedDocumentDiagnosticReport {
865 related_documents: None,
866 unchanged_document_diagnostic_report: UnchangedDocumentDiagnosticReport {
867 result_id,
868 },
869 }),
870 ));
871 }
872
873 Ok(DocumentDiagnosticReportResult::Report(
874 DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport {
875 related_documents: None,
876 full_document_diagnostic_report: FullDocumentDiagnosticReport {
877 result_id: Some(result_id),
878 items,
879 },
880 }),
881 ))
882 }
883
884 async fn range_formatting(
885 &self,
886 params: DocumentRangeFormattingParams,
887 ) -> Result<Option<Vec<TextEdit>>> {
888 let uri = ¶ms.text_document.uri;
889 let doc = match self.state.snapshot(uri) {
890 Some(doc) => doc,
891 None => return Ok(None),
892 };
893 let edits = crate::formatting::format_range(&doc, params.range);
894 if edits.is_empty() {
895 Ok(None)
896 } else {
897 Ok(Some(edits))
898 }
899 }
900
901 async fn formatting(&self, params: DocumentFormattingParams) -> Result<Option<Vec<TextEdit>>> {
902 let uri = ¶ms.text_document.uri;
903
904 let doc = match self.state.snapshot(uri) {
905 Some(doc) => doc,
906 None => return Ok(None),
907 };
908
909 let edits = crate::formatting::format_document(&doc);
910 if edits.is_empty() {
911 Ok(None)
912 } else {
913 Ok(Some(edits))
914 }
915 }
916}