1mod catalog;
2mod consts;
3pub mod debugging;
4mod error;
5mod pos;
6mod queries;
7mod range;
8pub mod rope_provider;
9mod sync_backend;
10#[cfg(test)]
11#[allow(clippy::pedantic)]
12mod test_helpers;
13#[cfg(test)]
14#[allow(clippy::pedantic)]
15mod tests;
16pub mod web;
17mod workspace;
18
19use debugging::timeit;
20use error::{Error, ResultExt, ResultIterator};
21use itertools::Itertools;
22use log::{debug, error, info, warn};
23use pos::Position;
24use range::Range;
25use std::collections::{HashMap, HashSet, LinkedList};
26use std::path::Path;
27use std::sync::{Arc, LazyLock};
28use tokio::sync::{OnceCell, RwLock, RwLockReadGuard, RwLockWriteGuard};
29use tokio::task::{self, JoinHandle};
30use tower_lsp::jsonrpc::Result;
31#[allow(clippy::wildcard_imports)]
33use tower_lsp::lsp_types::{self, *};
34use tower_lsp::{Client, LanguageServer};
35use tree_sitter_c2rust::Language;
36use workspace::{node_text, trim_full_iri, Workspace};
37
38use crate::sync_backend::SyncBackend;
39use crate::web::HttpClient;
40use crate::workspace::{
41 Document, DocumentReference, FormattingSettings, FrameType, InternalDocument,
42};
43
44pub use crate::workspace::clear_caches;
46
47pub static LANGUAGE: LazyLock<Language> = LazyLock::new(|| tree_sitter_owl_ms::LANGUAGE.into());
50
51#[derive(Clone)]
54pub struct Backend {
55 pub client: Client,
56 pub http_client: Arc<dyn HttpClient>,
57 position_encoding: OnceCell<PositionEncodingKind>,
58 options: OnceCell<Options>,
59 sync: SyncRef,
60}
61
62pub type SyncRef = Arc<RwLock<SyncBackend>>;
63
64impl Backend {
65 #[must_use]
68 pub fn new(client: Client, http_client: Box<dyn HttpClient>) -> Self {
69 Backend {
70 client,
71 http_client: http_client.into(),
72 position_encoding: OnceCell::new(),
73 sync: Arc::new(RwLock::new(SyncBackend::default())),
74 options: OnceCell::new(),
75 }
76 }
77
78 pub fn update_diagnostics_for_url_and_dependent(&self, file_url: Url) {
83 let mini_backend = self.clone();
84
85 task::spawn(async move {
86 let encoding = mini_backend.encoding();
87 let sync = mini_backend.sync.read().await;
88
89 #[allow(clippy::match_result_ok)]
91 if let Some((document, workspace)) = sync.get_internal_document(&file_url).ok() {
92 document
93 .publish_lsp_diagnostics(workspace, encoding, &mini_backend.client)
94 .await;
95
96 for other_internal_doc in workspace.internal_documents() {
98 let depends_on_me = other_internal_doc.reachable_urls(false).iter().any(|u| {
99 workspace.document_by_url(u) == Some(DocumentReference::Internal(document))
100 });
101
102 if depends_on_me {
103 mini_backend.update_diagnostics_for_url_and_dependent(
104 Url::from_file_path(other_internal_doc.path())
105 .expect("Document path should be convertable into file url"),
106 );
107 }
108 }
109 }
110 });
111 }
112
113 pub fn load_dependencies(&self, path: &Path) -> tokio::task::JoinHandle<()> {
117 let mini_backend = self.clone();
118 let path = path.to_owned();
119 tokio::spawn(async move {
120 let mut todo: LinkedList<(Url, u32)> = { build_todo_list(&mini_backend, &path).await };
121
122 let mut done = HashSet::<Url>::new();
123
124 debug!(
125 "Loading dependencies of {} len = {}",
126 path.display(),
127 todo.len()
128 );
129
130 while let Some((url, depth)) = todo.pop_front() {
131 debug!("Indexing {url} ...");
132 if done.contains(&url) {
133 debug!("URL {url} was indexed. Skip");
134 continue;
135 }
136 if depth > 2 {
137 debug!("Depth {depth} exceeded 2 for {url}. Skip");
138 continue;
139 }
140
141 let resolved_doc_or_err = {
142 let sync = mini_backend.sync.read().await;
143 let workspace = sync
144 .get_workspace(
145 &Url::from_file_path(&path)
146 .expect("File path should be convertable to URL"),
147 )
148 .expect("Workspace for document should exist");
149
150 Workspace::resolve_url_to_document(
152 workspace,
153 &url,
154 mini_backend.http_client.clone(),
155 )
156 .await
157 .ok()
158 };
159
160 let resolved_doc = if let Some(resolved_doc) = resolved_doc_or_err {
161 resolved_doc
162 } else {
163 warn!("Resolving {url} did not work");
165 None
166 };
167
168 if let Some(doc) = resolved_doc {
169 match doc {
170 Document::Internal(internal_document) => {
171 for ele in internal_document.reachable_urls(true) {
172 todo.push_back((ele.clone(), 1));
173 }
174 let file_url = Url::from_file_path(internal_document.path())
175 .expect("Path should also be a Url");
176 {
177 let mut sync = mini_backend.sync.write().await;
178 let workspace = sync.get_or_insert_workspace_mut(
179 &Url::from_file_path(&path)
180 .expect("File path should be convertable to URL"),
181 );
182 workspace.insert_internal_document(internal_document);
183 }
184
185 mini_backend.update_diagnostics_for_url_and_dependent(file_url);
186 }
187 Document::External(external_document) => {
188 for ele in external_document.reachable_urls() {
191 todo.push_back((ele.clone(), depth + 1));
192 }
193 {
194 let mut sync = mini_backend.sync.write().await;
195 let workspace = sync.get_or_insert_workspace_mut(
196 &Url::from_file_path(&path)
197 .expect("File path should be convertable to URL"),
198 );
199 workspace.insert_external_document(external_document);
200 }
201 }
202 }
203 } else {
204 }
206
207 refresh_inlay_hints(&mini_backend).await;
209
210 done.insert(url);
211 }
212
213 })
215 }
216
217 pub async fn wait_for_indexing(&self) {
220 let mut sync = self.write_sync().await;
221 let mut all_handles = Vec::<JoinHandle<()>>::new();
222 for workspace in sync.workspaces_mut() {
223 let handles = std::mem::take(&mut workspace.index_handles);
224 all_handles.extend(handles.into_iter());
225 }
226 drop(sync);
227 for handle in all_handles {
228 let _ = handle.await;
229 }
230 }
231
232 fn server_capabilities(position_encoding_kind: PositionEncodingKind) -> ServerCapabilities {
233 ServerCapabilities {
234 text_document_sync: Some(TextDocumentSyncCapability::Kind(
235 TextDocumentSyncKind::INCREMENTAL,
236 )),
237 hover_provider: Some(HoverProviderCapability::Simple(true)),
238 document_formatting_provider: Some(OneOf::Left(true)),
239 position_encoding: Some(position_encoding_kind),
240 inlay_hint_provider: Some(OneOf::Left(true)),
241 definition_provider: Some(OneOf::Left(true)),
242 code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
243 completion_provider: Some(CompletionOptions {
244 ..Default::default()
245 }),
246 semantic_tokens_provider: Some(
247 SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions {
248 legend: SemanticTokensLegend {
249 token_types: vec![
250 SemanticTokenType::NAMESPACE,
251 SemanticTokenType::TYPE,
252 SemanticTokenType::CLASS,
253 SemanticTokenType::ENUM,
254 SemanticTokenType::INTERFACE,
255 SemanticTokenType::STRUCT,
256 SemanticTokenType::TYPE_PARAMETER,
257 SemanticTokenType::PARAMETER,
258 SemanticTokenType::VARIABLE,
259 SemanticTokenType::PROPERTY,
260 SemanticTokenType::ENUM_MEMBER,
261 SemanticTokenType::EVENT,
262 SemanticTokenType::FUNCTION,
263 SemanticTokenType::METHOD,
264 SemanticTokenType::MACRO,
265 SemanticTokenType::KEYWORD,
266 SemanticTokenType::MODIFIER,
267 SemanticTokenType::COMMENT,
268 SemanticTokenType::STRING,
269 SemanticTokenType::NUMBER,
270 SemanticTokenType::REGEXP,
271 SemanticTokenType::OPERATOR,
272 SemanticTokenType::DECORATOR,
273 ],
274 token_modifiers: vec![],
275 },
276 full: Some(SemanticTokensFullOptions::Bool(true)),
277 range: Some(true),
278 ..Default::default()
279 }),
280 ),
281 document_symbol_provider: Some(OneOf::Left(true)),
282 workspace_symbol_provider: Some(OneOf::Right(WorkspaceSymbolOptions {
283 work_done_progress_options: WorkDoneProgressOptions {
284 work_done_progress: Some(false),
285 },
286 resolve_provider: Some(false),
287 })),
288 references_provider: Some(OneOf::Left(true)),
289 rename_provider: Some(OneOf::Right(RenameOptions {
290 prepare_provider: Some(true),
291 work_done_progress_options: WorkDoneProgressOptions {
292 work_done_progress: None,
293 },
294 })),
295 ..Default::default()
296 }
297 }
298}
299
300#[derive(Debug, Clone)]
301struct Options {
302 order_frames: bool,
303}
304
305fn parse_options(options: Option<serde_json::Value>) -> Options {
306 {
307 let mut is_order_frames = false;
308 if let Some(o) = options {
309 if let Some(root) = o.as_object() {
310 if let Some(omn) = root.get("omn") {
311 if let Some(omn) = omn.as_object() {
312 if let Some(order_frames) = omn.get("orderFrames") {
313 if let Some(order_frames) = order_frames.as_bool() {
314 is_order_frames = order_frames;
315 }
316 }
317 }
318 }
319 }
320 }
321 Options {
322 order_frames: is_order_frames,
323 }
324 }
325}
326
327async fn refresh_inlay_hints(mini_backend: &Backend) {
328 match mini_backend.client.inline_value_refresh().await {
329 Ok(()) => {
330 debug!("Refresh inline hints");
331 }
332 Err(err) => {
335 error!("{err}");
336 }
337 }
338}
339
340async fn build_todo_list(
341 mini_backend: &Backend,
342 path: &std::path::PathBuf,
343) -> LinkedList<(Url, u32)> {
344 let sync = mini_backend.sync.read().await;
345 let workspace = sync
346 .get_workspace(&Url::from_file_path(path).expect("File path should be convertable to URL"))
347 .expect("Workspace for document should exist");
348
349 let document = workspace.get_internal_document(path).unwrap();
350 document
351 .reachable_urls(true)
352 .iter()
353 .map(|u| (u.clone(), 1))
354 .collect()
355}
356
357#[tower_lsp::async_trait]
359impl LanguageServer for Backend {
360 async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
364 info!("Initialize language server -----------------------------");
365 info!("Client info:\n{:#?}", params.client_info);
366 debug!("Client capabilities:\n{:#?}", params.capabilities);
367 debug!("Options: {:#?}", params.initialization_options);
368
369 let options = params.initialization_options;
370 let options = parse_options(options);
371 debug!("Parsed Options: {options:?}");
372 self.options
373 .set(options)
374 .expect("options should not be set");
375
376 let encodings = params
377 .capabilities
378 .general
379 .and_then(|g| g.position_encodings)
380 .unwrap_or_default();
381
382 self.position_encoding
383 .set(if encodings.contains(&PositionEncodingKind::UTF8) {
384 PositionEncodingKind::UTF8
385 } else {
386 PositionEncodingKind::UTF16
387 })
388 .expect("the encoding to be unset");
389
390 let mut sync = self.write_sync().await;
391
392 for wf in params.workspace_folders.iter().flatten() {
393 sync.push_workspace(Workspace::new(wf.clone()));
394 }
395
396 let position_encoding_kind = self
399 .position_encoding
400 .get()
401 .expect("encoding should be set")
402 .clone();
403
404 Ok(InitializeResult {
405 server_info: Some(ServerInfo {
406 name: "owl-ms-language-server".to_string(),
407 version: None,
408 }),
409 capabilities: Backend::server_capabilities(position_encoding_kind),
410 })
411 }
412
413 async fn initialized(&self, _params: InitializedParams) {
414 let sync = self.read_sync().await;
415 let workspace_paths = sync
416 .workspaces()
417 .iter()
418 .map(|w| format!("{w}"))
419 .collect_vec();
420
421 info!("Initialized languag server with workspaces: {workspace_paths:?}");
422 }
423
424 async fn did_open(&self, params: DidOpenTextDocumentParams) {
425 async {
426 let file_url = params.text_document.uri;
427 info!("Did open {file_url} ...",);
428
429 let mut sync = self.write_sync().await;
430
431 let workspace = sync.get_or_insert_workspace_mut(&file_url);
432 let internal_document = InternalDocument::new(
433 file_url.clone(),
434 params.text_document.version,
435 params.text_document.text,
436 );
437
438 let doc = workspace.insert_internal_document(internal_document);
439 let path = doc.path().to_path_buf();
440
441 let handle = self.load_dependencies(&path);
442
443 #[cfg(test)]
444 {
445 drop(sync);
447 handle.await.unwrap();
448 }
449 #[cfg(not(test))]
450 {
451 workspace.index_handles.push(handle);
452 }
453
454 self.update_diagnostics_for_url_and_dependent(file_url);
455
456 debug!("Did open!");
457
458 Ok(())
459 }
460 .await
461 .log_if_error();
462 }
463
464 async fn did_change(&self, params: DidChangeTextDocumentParams) {
465 async {
466 debug!(
467 "Did change at {} with version {}",
468 params
469 .text_document
470 .uri
471 .path_segments()
472 .ok_or(Error::InvalidUrl(params.text_document.uri.clone()))?
473 .next_back()
474 .ok_or(Error::InvalidUrl(params.text_document.uri.clone()))?,
475 params.text_document.version
476 );
477
478 let url = params.text_document.uri.clone();
479
480 let mut sync = self.write_sync().await;
482 let (document, workspace) = sync.take_internal_document(&url)?;
483
484 let new_document = timeit("document.edit", || document.edit(params, self.encoding()))?;
485
486 workspace.insert_internal_document(new_document);
487
488 self.update_diagnostics_for_url_and_dependent(url);
491
492 Ok(())
493 }
494 .await
495 .log_if_error();
496 }
497
498 async fn did_close(&self, params: DidCloseTextDocumentParams) {
499 (|| {
500 debug!(
501 "Did close at {}",
502 params
503 .text_document
504 .uri
505 .path_segments()
506 .ok_or(Error::InvalidUrl(params.text_document.uri.clone()))?
507 .next_back()
508 .ok_or(Error::InvalidUrl(params.text_document.uri.clone()))?
509 );
510
511 Ok(())
512 })()
513 .log_if_error();
514
515 }
518
519 async fn formatting(&self, params: DocumentFormattingParams) -> Result<Option<Vec<TextEdit>>> {
520 info!("formatting {params:#?}");
521 let url = params.text_document.uri;
522
523 let tab_size = params.options.tab_size;
524
525 let sync = self.read_sync().await;
526 let (doc, _) = sync.get_internal_document(&url)?;
527
528 let options = FormattingSettings {
529 tab_size: if tab_size == 0 { 4 } else { tab_size },
530 ruler_width: 80,
531 order_frames: self
532 .options
533 .get()
534 .expect("options should be initilized")
535 .order_frames,
536 };
537 let text = doc.formatted(&options);
539
540 let range: Range = doc.tree().root_node().range().into();
541
542 return Ok(Some(vec![TextEdit {
543 range: range.into_lsp(doc.rope(), self.encoding())?,
544 new_text: text,
545 }]));
546 }
547
548 async fn hover(&self, params: HoverParams) -> Result<Option<Hover>> {
549 let url = params.text_document_position_params.text_document.uri;
550 info!(
551 "Hover at {:?} in file {url}",
552 params.text_document_position_params.position
553 );
554
555 let sync = self.read_sync().await;
556 let (doc, ws) = sync.get_internal_document(&url)?;
557
558 let pos: Position = Position::from_lsp(
559 params.text_document_position_params.position,
560 doc.rope(),
561 self.encoding(),
562 )?;
563 let node = doc
564 .tree()
565 .root_node()
566 .named_descendant_for_point_range(pos.into(), pos.into())
567 .ok_or(Error::PositionOutOfBounds(pos))?;
568
569 let info = ws.node_info(&node, doc);
570
571 Ok(if info.is_empty() {
572 None
573 } else {
574 let range: Range = node.range().into();
576 Some(Hover {
577 contents: HoverContents::Scalar(MarkedString::String(info)),
578 range: Some(range.into_lsp(doc.rope(), self.encoding())?),
579 })
580 })
581 }
582
583 async fn inlay_hint(&self, params: InlayHintParams) -> Result<Option<Vec<InlayHint>>> {
584 let url = params.text_document.uri;
585 info!("Inlay hint at {url}");
586
587 let sync = self.read_sync().await;
588 let (document, workspace) = sync.get_internal_document(&url)?;
589
590 let range = Range::from_lsp(¶ms.range, document.rope(), self.encoding())?;
591
592 debug!(
593 "inlay_hint at {}:{range}",
594 url.path_segments()
595 .ok_or(Error::InvalidUrl(url.clone()))?
596 .next_back()
597 .ok_or(Error::InvalidUrl(url.clone()))?
598 );
599
600 let hints = document.inlay_hint(range, self.encoding(), workspace);
601
602 Ok(Some(hints))
603 }
604
605 async fn goto_definition(
606 &self,
607 params: GotoDefinitionParams,
608 ) -> Result<Option<GotoDefinitionResponse>> {
609 let url = params.text_document_position_params.text_document.uri;
610 debug!("goto_definition at {url}");
611
612 let sync = self.read_sync().await;
613 let (doc, workspace) = sync.get_internal_document(&url)?;
614 let pos: Position = Position::from_lsp(
615 params.text_document_position_params.position,
616 doc.rope(),
617 self.encoding(),
618 )?;
619
620 let leaf_node = doc
621 .tree()
622 .root_node()
623 .named_descendant_for_point_range(pos.into(), pos.into())
624 .ok_or(Error::PositionOutOfBounds(pos))?;
625
626 let reachable_docs = doc.reachable_docs_recursive(workspace, true);
627
628 let node_is_iri = ["full_iri", "simple_iri", "abbreviated_iri"].contains(&leaf_node.kind());
629 if node_is_iri {
630 let iri = trim_full_iri(node_text(&leaf_node, doc.rope()));
631 let iri = doc.abbreviated_iri_to_full_iri(&iri).unwrap_or(iri);
632
633 debug!("Try goto definition of {iri}");
634
635 let iri_is_import_iri = leaf_node
636 .parent()
637 .expect("iri node should have parent")
638 .kind()
639 == "import";
640
641 if iri_is_import_iri {
642 let url = Url::parse(&iri).map_err(|_| Error::InvalidUrl(url.clone()))?;
643 let path = workspace.url_to_path_with_catalog(&url);
645 if let Some(path) = path {
646 return Ok(Some(single_path_response(&path)));
647 }
648 } else {
649 let frame_info =
650 Workspace::get_frame_info_recursive(workspace, &iri, &reachable_docs);
651 if let Some(frame_info) = frame_info {
652 let locations = frame_info
653 .definitions
654 .iter()
655 .sorted_by_key(|l| {
656 if l.range == Range::ZERO {
657 u32::MAX } else {
659 l.range.start.line()
660 }
661 })
662 .map(|l| l.clone().into_lsp(doc.rope(), self.encoding()))
663 .filter_and_log()
664 .collect_vec();
665
666 return Ok(Some(GotoDefinitionResponse::Array(locations)));
667 }
668 }
669 }
670 Ok(None)
671 }
672
673 async fn code_action(&self, params: CodeActionParams) -> Result<Option<CodeActionResponse>> {
674 debug!("code_action at {}", params.text_document.uri);
675 let url = params.text_document.uri;
676 let sync = self.read_sync().await;
677 let (doc, ws) = sync.get_internal_document(&url)?;
678
679 let pos: Position = Position::from_lsp(params.range.start, doc.rope(), self.encoding())?;
681
682 let node = doc
683 .tree()
684 .root_node()
685 .named_descendant_for_point_range(pos.into(), pos.into())
686 .ok_or(Error::PositionOutOfBounds(pos))?;
687
688 let diagnostics = doc.diagnostics(ws);
692 let diagnostics_under_cursor = diagnostics
693 .into_iter()
694 .filter(|diagnostic| diagnostic.range.contains(pos));
695
696 let end: Position = doc.tree().root_node().range().end_point.into();
697 let end_lsp = end.into_lsp(doc.rope(), self.encoding())?;
698 let create_missing_iri_actions = diagnostics_under_cursor.filter_map(|d| match &d.kind {
699 workspace::DiagnosticKind::MissingIri(full_iri) => {
700 let iri = doc.full_iri_to_shorter_iri(full_iri);
701 let iri_kind = node
702 .parent()
703 .expect("Missing IRI node should have parent")
704 .kind();
705
706 let frame_type = FrameType::parse(iri_kind);
707 let definition_str = frame_type.to_definition()?;
708
709 Some(CodeActionOrCommand::CodeAction(CodeAction {
710 title: format!("Create {frame_type} for {iri}",),
711 edit: Some(WorkspaceEdit {
712 changes: Some(HashMap::from([(
713 url.clone(),
714 vec![TextEdit {
715 range: lsp_types::Range {
716 start: end_lsp,
717 end: end_lsp,
718 },
719 new_text: format!("\n{definition_str} {iri}\n"),
720 }],
721 )])),
722 ..Default::default()
723 }),
724 ..Default::default()
726 }))
727 }
728 workspace::DiagnosticKind::SyntaxError { .. } => None,
729 });
730
731 let mut actions = vec![
732 ];
754
755 actions.extend(create_missing_iri_actions);
756
757 Ok(Some(actions))
758 }
759
760 async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
761 debug!(
762 "completion at {} {:?} {:?}",
763 params.text_document_position.text_document.uri,
764 params.text_document_position.position,
765 self.encoding()
766 );
767 let url = params.text_document_position.text_document.uri;
768 let sync = self.read_sync().await;
769 let (doc, workspace) = sync.get_internal_document(&url)?;
770 let pos: Position = Position::from_lsp(
771 params.text_document_position.position,
772 doc.rope(),
773 self.encoding(),
774 )?;
775
776 let kws = timeit("lookahead iterator keywords", || {
777 doc.get_keyword_competions_at(pos)
778 });
779
780 debug!("The resultingn kws are {kws:#?}");
781
782 let iri_completions = doc.get_iri_completions_at(pos, workspace);
783
784 debug!("The resulting iris are {iri_completions:#?}");
785
786 let keywords_completion_items = kws.into_iter().map(|keyword| CompletionItem {
787 label: keyword,
788 kind: Some(CompletionItemKind::KEYWORD),
789 ..Default::default()
790 });
791
792 let iri_completion_items =
793 iri_completions
794 .into_iter()
795 .map(|(label, details, insert_text)| {
796 CompletionItem {
797 label,
798 kind: Some(CompletionItemKind::REFERENCE),
799 detail: Some(details),
800 insert_text: Some(insert_text),
801 ..Default::default()
803 }
804 });
805
806 let items: Vec<CompletionItem> = iri_completion_items
807 .chain(keywords_completion_items)
808 .collect();
809
810 debug!("completion item count {}", items.len());
811
812 Ok(Some(CompletionResponse::Array(items)))
813 }
814
815 async fn semantic_tokens_full(
816 &self,
817 params: SemanticTokensParams,
818 ) -> Result<Option<SemanticTokensResult>> {
819 debug!("semantic_tokens_full at {}", params.text_document.uri);
820 let url = params.text_document.uri;
821 let sync = self.read_sync().await;
822 let (doc, _) = sync.get_internal_document(&url)?;
823
824 let tokens = doc.sematic_tokens(None, self.encoding())?;
825
826 Ok(Some(SemanticTokensResult::Tokens(SemanticTokens {
827 result_id: None,
828 data: tokens,
829 })))
830 }
831
832 async fn semantic_tokens_range(
833 &self,
834 params: SemanticTokensRangeParams,
835 ) -> Result<Option<SemanticTokensRangeResult>> {
836 debug!("semantic_tokens_range at {}", params.text_document.uri);
837 let url = params.text_document.uri;
838 let sync = self.read_sync().await;
839 let (doc, _) = sync.get_internal_document(&url)?;
840 let range = Range::from_lsp(¶ms.range, doc.rope(), self.encoding())?;
841 let tokens = doc.sematic_tokens(Some(range), self.encoding())?;
842
843 return Ok(Some(SemanticTokensRangeResult::Tokens(SemanticTokens {
844 result_id: None,
845 data: tokens,
846 })));
847 }
848
849 async fn document_symbol(
850 &self,
851 params: DocumentSymbolParams,
852 ) -> Result<Option<DocumentSymbolResponse>> {
853 let url = params.text_document.uri;
854 let sync = self.read_sync().await;
855 let (doc, _) = sync.get_internal_document(&url)?;
856 let infos = doc.all_frame_infos();
857 return Ok(Some(DocumentSymbolResponse::Flat(
858 infos
859 .flat_map(|info| {
860 let name = info.label().unwrap_or_else(|| {
861 doc.full_iri_to_abbreviated_iri(&info.iri)
862 .unwrap_or(info.iri.clone())
863 });
864 let kind: SymbolKind = info.frame_type.into();
865 let url = url.clone();
866 info.definitions.iter().map(move |def| {
867 #[allow(deprecated)] Ok(SymbolInformation {
869 name: name.clone(),
870 kind,
871 tags: None,
872 deprecated: None,
873 location: Location {
874 uri: url.clone(),
875 range: def.range.into_lsp(doc.rope(), self.encoding())?,
876 },
877 container_name: None,
878 })
879 })
880 })
881 .filter_and_log()
882 .filter(|s| !s.name.is_empty())
883 .sorted_by_cached_key(|s| format!("{:?}{}", s.kind, s.name))
884 .collect_vec(),
885 )));
886 }
887
888 async fn symbol(
889 &self,
890 params: WorkspaceSymbolParams,
891 ) -> Result<Option<Vec<SymbolInformation>>> {
892 let query = params.query;
893 info!("symbol with query: {query}");
894
895 let sync = self.read_sync().await;
896 let workspaces = sync.workspaces();
897 let all_frame_infos = workspaces
898 .iter()
899 .flat_map(workspace::Workspace::all_frame_infos);
900
901 let symbols = all_frame_infos
902 .filter_map(|fi| {
903 let score = fi.matches(&query);
904 if score > 0 {
905 Some((score, fi))
906 } else {
907 None
908 }
909 })
910 .sorted_by_key(|(score, _)| *score)
911 .rev()
912 .flat_map(|(_, fi)| {
913 let name = fi.label().unwrap_or(fi.iri.clone());
914
915 #[allow(deprecated)] fi.definitions
917 .iter()
918 .map(|definition| {
919 let url = &definition.uri;
920 let location = sync
921 .get_internal_document(url)
922 .map(|(doc, _)| {
923 definition.clone().into_lsp(doc.rope(), self.encoding())
924 })
925 .and_then(|r| r.inspect_err(|e| error!("{e}")))
926 .unwrap_or(Location {
927 uri: url.clone(),
928 range: lsp_types::Range::default(),
929 });
930
931 SymbolInformation {
932 name: name.clone(),
933 kind: fi.frame_type.into(),
934 tags: None,
935 deprecated: None,
936 location,
937 container_name: None,
938 }
939 })
940 .collect_vec()
941 })
942 .collect_vec();
943
944 Ok(Some(symbols))
945 }
946
947 async fn references(&self, params: ReferenceParams) -> Result<Option<Vec<Location>>> {
948 info!(
949 "references {}:{}:{} {:?}",
950 params.text_document_position.text_document.uri,
951 params.text_document_position.position.line,
952 params.text_document_position.position.character,
953 params.context
954 );
955
956 let url = params.text_document_position.text_document.uri;
957 let sync = self.read_sync().await;
958 let (doc, workspace) = sync.get_internal_document(&url)?;
959
960 let pos: Position = Position::from_lsp(
961 params.text_document_position.position,
962 doc.rope(),
963 self.encoding(),
964 )?;
965
966 let node = doc
967 .tree()
968 .root_node()
969 .named_descendant_for_point_range(pos.into(), pos.into())
970 .ok_or(Error::PositionOutOfBounds(pos))?;
971
972 let full_iri_option = match node.kind() {
973 "full_iri" => Some(trim_full_iri(node_text(&node, doc.rope()))),
974 "simple_iri" | "abbreviated_iri" => {
975 let iri = node_text(&node, doc.rope());
976 Some(
977 doc.abbreviated_iri_to_full_iri(&iri)
978 .unwrap_or(iri.to_string()),
979 )
980 }
981 _ => None,
982 };
983
984 Ok(if let Some(full_iri) = full_iri_option {
985 let locations = workspace
986 .internal_documents()
987 .flat_map(|doc| {
988 doc.references(&full_iri, params.context.include_declaration)
989 .into_iter()
990 .filter_map(|range| {
991 range
992 .into_lsp(doc.rope(), self.encoding())
993 .inspect_log()
994 .ok()
995 })
996 .map(|range| Location {
997 uri: Url::from_file_path(doc.path())
998 .expect("File path should be a valid URL"),
999 range,
1000 })
1001 })
1002 .collect_vec();
1003 Some(locations)
1004 } else {
1005 None
1006 })
1007 }
1008
1009 async fn prepare_rename(
1010 &self,
1011 params: TextDocumentPositionParams,
1012 ) -> Result<Option<PrepareRenameResponse>> {
1013 let url = params.text_document.uri;
1014
1015 let sync = self.read_sync().await;
1016 let (doc, _) = sync.get_internal_document(&url)?;
1017 let pos: Position = Position::from_lsp(params.position, doc.rope(), self.encoding())?;
1018
1019 fn node_range(position: Position, doc: &InternalDocument) -> Option<Range> {
1020 debug!("prepare_rename try {position:?}");
1021 let node = doc
1022 .tree()
1023 .root_node()
1024 .named_descendant_for_point_range(position.into(), position.into())?;
1025
1026 match node.parent()?.kind() {
1028 "datatype_iri"
1029 | "class_iri"
1030 | "annotation_property_iri"
1031 | "ontology_iri"
1032 | "data_property_iri"
1033 | "version_iri"
1034 | "object_property_iri"
1035 | "annotation_property_iri_annotated_list"
1036 | "individual_iri" => {}
1037 _ => return None,
1038 }
1039
1040 match node.kind() {
1041 "full_iri" => {
1042 let range: Range = node.range().into();
1043 let range = Range {
1044 start: range.start.moved_right(1, doc.rope()),
1045 end: range.end.moved_left(1, doc.rope()),
1046 };
1047 Some(range)
1048 }
1049 "simple_iri" => {
1050 let range: Range = node.range().into();
1051 Some(range)
1052 }
1053 "abbreviated_iri" => {
1054 let range: Range = node.range().into();
1055 let text = node_text(&node, doc.rope()).to_string();
1056 let col_offset = text
1057 .find(':')
1058 .expect("abbreviated_iri to contain at least one :")
1059 + 1;
1060 let range = Range {
1061 #[allow(clippy::cast_possible_truncation)]
1063 start: range.start.moved_right(col_offset as u32, doc.rope()),
1064 ..range
1065 };
1066 Some(range)
1067 }
1068 _ => None,
1069 }
1070 }
1071
1072 let range = node_range(pos, doc)
1073 .or_else(|| {
1074 debug!("prepare rename try one position left");
1079 let position = pos.moved_left(1, doc.rope());
1080 node_range(position, doc)
1081 })
1082 .map(|range| {
1083 Ok(PrepareRenameResponse::Range(
1084 range.into_lsp(doc.rope(), self.encoding())?,
1085 ))
1086 })
1087 .and_then(|r| r.inspect_err(|e: &Error| error!("{e}")).ok());
1088 debug!("prepare rename found range {range:?}");
1089
1090 Ok(range)
1091 }
1092
1093 async fn rename(&self, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
1094 debug!("rename {params:?}");
1095 let url = params.text_document_position.text_document.uri;
1096 let new_name = params.new_name;
1097
1098 let sync = self.read_sync().await;
1099 let (doc, workspace) = sync.get_internal_document(&url)?;
1100
1101 let pos: Position = Position::from_lsp(
1102 params.text_document_position.position,
1103 doc.rope(),
1104 self.encoding(),
1105 )?;
1106
1107 let old_and_new_iri = if let Some(x) = rename_helper(pos, doc, new_name.clone())? {
1108 Some(x)
1109 } else {
1110 debug!("prepare rename try one position left");
1115 let position = pos.moved_left(1, doc.rope());
1116 rename_helper(position, doc, new_name)?
1117 };
1118
1119 if let Some((full_iri, new_iri, iri_kind, original)) = old_and_new_iri {
1120 debug!("renaming resolved iris from {full_iri:?} to {new_iri:?}");
1121 let changes = workspace
1122 .internal_documents()
1123 .map(|doc| {
1124 let edits = doc
1125 .rename_edits(&full_iri, new_iri.as_ref(), &iri_kind, &original)
1126 .into_iter()
1127 .filter_map(|(range, str)| {
1128 range
1129 .into_lsp(doc.rope(), self.encoding())
1130 .inspect_log()
1131 .ok()
1132 .map(|range| TextEdit {
1133 range,
1134 new_text: str,
1135 })
1136 })
1137 .collect_vec();
1138 (doc.uri().clone(), edits)
1139 })
1140 .collect();
1141
1142 return Ok(Some(WorkspaceEdit {
1143 changes: Some(changes),
1144 document_changes: None,
1145 change_annotations: None,
1146 }));
1147 }
1148 Ok(None)
1149 }
1150
1151 async fn shutdown(&self) -> Result<()> {
1152 info!("Shutdown");
1153 Ok(())
1157 }
1158}
1159
1160fn single_path_response(path: &Path) -> GotoDefinitionResponse {
1161 GotoDefinitionResponse::Scalar(Location {
1162 uri: Url::from_file_path(path).expect("path should be valid url"),
1163 range: lsp_types::Range {
1164 start: lsp_types::Position {
1165 line: 0,
1166 character: 0,
1167 },
1168 end: lsp_types::Position {
1169 line: 0,
1170 character: 0,
1171 },
1172 },
1173 })
1174}
1175
1176type IriKindName = Option<(String, Option<String>, String, String)>;
1177
1178fn rename_helper(
1179 position: Position,
1180 doc: &InternalDocument,
1181 new_name: String,
1182) -> Result<IriKindName> {
1183 let node = doc
1184 .tree()
1185 .root_node()
1186 .named_descendant_for_point_range(position.into(), position.into())
1187 .ok_or(Error::PositionOutOfBounds(position))?;
1188
1189 match node.kind() {
1190 "full_iri" => {
1191 let iri_kind = node
1192 .parent()
1193 .expect("full_iri to have a parent")
1194 .kind()
1195 .to_string();
1196 let iri = trim_full_iri(node_text(&node, doc.rope()));
1197 Ok(Some((
1198 iri.clone(),
1199 Some(new_name.clone()),
1200 iri_kind,
1201 new_name,
1202 )))
1203 }
1204 "simple_iri" => {
1205 let iri = node_text(&node, doc.rope());
1206 let iri_kind = node
1207 .parent()
1208 .expect("simple_iri to have a parent")
1209 .kind()
1210 .to_string();
1211 Ok(Some((
1212 doc.abbreviated_iri_to_full_iri(&iri)
1213 .unwrap_or(iri.to_string()),
1214 doc.abbreviated_iri_to_full_iri(&new_name),
1215 iri_kind,
1216 new_name,
1217 )))
1218 }
1219 "abbreviated_iri" => {
1220 let iri_kind = node
1221 .parent()
1222 .expect("abbreviated_iri to have a parent")
1223 .kind()
1224 .to_string();
1225 let iri = node_text(&node, doc.rope()).to_string();
1226 let (prefix, _) = iri
1227 .split_once(':')
1228 .expect("abbreviated_iri to contain at least one :");
1229 Ok(Some((
1230 doc.abbreviated_iri_to_full_iri(&iri).unwrap_or(iri.clone()),
1231 doc.abbreviated_iri_to_full_iri(&format!("{prefix}:{new_name}")),
1232 iri_kind,
1233 format!("{prefix}:{new_name}"),
1234 )))
1235 }
1236 _ => Ok(None),
1237 }
1238}
1239
1240impl Backend {
1241 async fn read_sync(&self) -> RwLockReadGuard<'_, SyncBackend> {
1242 self.sync.read().await
1243 }
1244 async fn write_sync(&self) -> RwLockWriteGuard<'_, SyncBackend> {
1245 self.sync.write().await
1246 }
1247
1248 fn encoding(&self) -> &PositionEncodingKind {
1249 self.position_encoding
1250 .get()
1251 .expect("position should be set")
1252 }
1253}
1254
1255pub trait USizeextra
1256where
1257 Self: Sized + TryInto<u32>,
1258{
1259 fn to_u32(self) -> u32 {
1260 TryInto::<u32>::try_into(self).unwrap_or(u32::MAX)
1261 }
1262}
1263
1264impl USizeextra for usize {}