oak_lsp/
service.rs

1use crate::types::{CompletionItem, Diagnostic, FoldingRange, Hover, InitializeParams, LocationRange, StructureItem, WorkspaceEdit, WorkspaceSymbol};
2use core::range::Range;
3use oak_core::{language::Language, source::Source, tree::RedNode};
4use oak_vfs::{Vfs, WritableVfs};
5use std::future::Future;
6
7pub trait LanguageService: Send + Sync {
8    type Lang: Language;
9    type Vfs: WritableVfs;
10
11    /// Get the underlying VFS.
12    fn vfs(&self) -> &Self::Vfs;
13
14    /// Get the workspace manager.
15    fn workspace(&self) -> &crate::workspace::WorkspaceManager;
16
17    /// Helper to get source from VFS.
18    fn get_source(&self, uri: &str) -> Option<Box<dyn Source + Send + Sync>> {
19        self.vfs().get_source(uri).map(|s| Box::new(s) as Box<dyn Source + Send + Sync>)
20    }
21
22    /// Helper to get the root red node of a file.
23    /// Implementations should override this to provide actual parsing/caching.
24    fn get_root(&self, _uri: &str) -> impl Future<Output = Option<RedNode<'_, Self::Lang>>> + Send + '_ {
25        async { None }
26    }
27
28    /// Helper to run logic with the root node.
29    fn with_root<'a, R, F>(&'a self, uri: &'a str, f: F) -> impl Future<Output = Option<R>> + Send + 'a
30    where
31        R: Send,
32        F: FnOnce(RedNode<'a, Self::Lang>) -> R + Send + 'a,
33    {
34        async move {
35            let root = self.get_root(uri).await?;
36            Some(f(root))
37        }
38    }
39
40    /// Helper to run logic with multiple root nodes in parallel.
41    fn with_roots<'a, R, F>(&'a self, uris: Vec<String>, f: F) -> impl Future<Output = Vec<R>> + Send + 'a
42    where
43        R: Send + 'static,
44        F: Fn(RedNode<'a, Self::Lang>) -> R + Send + Sync + 'a,
45    {
46        let mut futures = Vec::new();
47        let f = std::sync::Arc::new(f);
48
49        for uri in uris {
50            let f = f.clone();
51            futures.push(async move { if let Some(root) = self.get_root(&uri).await { Some(f(root)) } else { None } });
52        }
53
54        async move { futures::future::join_all(futures).await.into_iter().flatten().collect() }
55    }
56
57    /// Provide hover information. Defaults to None.
58    fn hover(&self, _uri: &str, _range: Range<usize>) -> impl Future<Output = Option<Hover>> + Send + '_ {
59        async { None }
60    }
61
62    /// Provide folding ranges. Defaults to empty.
63    fn folding_ranges(&self, _uri: &str) -> impl Future<Output = Vec<FoldingRange>> + Send + '_ {
64        async { vec![] }
65    }
66
67    /// Provide document symbols. Defaults to empty.
68    fn document_symbols(&self, _uri: &str) -> impl Future<Output = Vec<StructureItem>> + Send + '_ {
69        async { vec![] }
70    }
71
72    /// Search for symbols in the entire workspace.
73    fn workspace_symbols(&self, _query: &str) -> impl Future<Output = Vec<WorkspaceSymbol>> + Send + '_ {
74        async { vec![] }
75    }
76
77    /// Helper to list all files recursively.
78    fn list_all_files(&self, root_uri: &str) -> impl Future<Output = Vec<String>> + Send + '_ {
79        let root_uri = root_uri.to_string();
80        async move {
81            let mut files = Vec::new();
82            let mut stack = vec![root_uri];
83
84            while let Some(uri) = stack.pop() {
85                if self.vfs().is_file(&uri) {
86                    files.push(uri);
87                }
88                else if self.vfs().is_dir(&uri) {
89                    if let Some(entries) = self.vfs().read_dir(&uri) {
90                        for entry in entries {
91                            stack.push(entry);
92                        }
93                    }
94                }
95            }
96            files
97        }
98    }
99
100    /// Find definition. Defaults to empty.
101    fn definition<'a>(&'a self, _uri: &'a str, _range: Range<usize>) -> impl Future<Output = Vec<LocationRange>> + Send + 'a {
102        async { vec![] }
103    }
104
105    /// Find references. Defaults to empty.
106    fn references<'a>(&'a self, _uri: &'a str, _range: Range<usize>) -> impl Future<Output = Vec<LocationRange>> + Send + 'a {
107        async { vec![] }
108    }
109
110    /// Rename a symbol.
111    fn rename<'a>(&'a self, _uri: &'a str, _range: Range<usize>, _new_name: String) -> impl Future<Output = Option<WorkspaceEdit>> + Send + 'a {
112        async { None }
113    }
114
115    /// Provide completion items. Defaults to empty.
116    fn completion<'a>(&'a self, _uri: &'a str, _position: usize) -> impl Future<Output = Vec<CompletionItem>> + Send + 'a {
117        async { vec![] }
118    }
119
120    /// Provide diagnostics for a file. Defaults to empty.
121    fn diagnostics<'a>(&'a self, _uri: &'a str) -> impl Future<Output = Vec<Diagnostic>> + Send + 'a {
122        async { vec![] }
123    }
124
125    /// Called when the language server is initialized.
126    fn initialize<'a>(&'a self, _params: InitializeParams) -> impl Future<Output = ()> + Send + 'a {
127        async {}
128    }
129
130    /// Called when the language server is initialized (notification).
131    fn initialized<'a>(&'a self) -> impl Future<Output = ()> + Send + 'a {
132        async {}
133    }
134
135    /// Called when the language server is shut down.
136    fn shutdown<'a>(&'a self) -> impl Future<Output = ()> + Send + 'a {
137        async {}
138    }
139
140    /// Called when a file is saved.
141    fn did_save<'a>(&'a self, _uri: &'a str) -> impl Future<Output = ()> + Send + 'a {
142        async {}
143    }
144
145    /// Called when a file is closed.
146    fn did_close<'a>(&'a self, _uri: &'a str) -> impl Future<Output = ()> + Send + 'a {
147        async {}
148    }
149}
150
151/// Extension trait for `LanguageService` providing additional functionality like Axum integration.
152pub trait LanguageServiceExt: LanguageService {
153    /// Converts the language service into an Axum router.
154    #[cfg(feature = "axum")]
155    fn into_axum_router(self) -> axum::Router
156    where
157        Self: Sized + 'static,
158    {
159        crate::server::axum_router(std::sync::Arc::new(self))
160    }
161}
162
163impl<T: LanguageService> LanguageServiceExt for T {}