1#![warn(missing_docs)]
7#![feature(new_range_api)]
8
9use core::range::Range;
10use futures::Future;
11use oak_core::tree::RedNode;
12use oak_lsp::{
13 Hover,
14 service::LanguageService,
15 types::{CompletionItem, Diagnostic, LocationRange},
16};
17use oak_ruby::RubyLanguage;
18use oak_vfs::Vfs;
19use std::sync::Arc;
20
21pub mod lsp;
22use lsp::RubyHoverProvider;
23
24pub struct RubyLanguageService<V: Vfs> {
26 vfs: V,
27 workspace: oak_lsp::workspace::WorkspaceManager,
28 hover_provider: RubyHoverProvider,
29}
30
31impl<V: Vfs> RubyLanguageService<V> {
32 pub fn new(vfs: V) -> Self {
34 Self { vfs, workspace: oak_lsp::workspace::WorkspaceManager::default(), hover_provider: RubyHoverProvider::new() }
35 }
36
37 async fn with_root<F, T>(&self, uri: &str, f: F) -> Option<T>
39 where
40 F: FnOnce(&RedNode<'_, RubyLanguage>) -> Option<T>,
41 V: oak_vfs::WritableVfs + Send + Sync + 'static,
42 {
43 match self.get_root(uri).await {
44 Some(root) => f(&root),
45 None => None,
46 }
47 }
48}
49
50impl<V: Vfs + Send + Sync + 'static + oak_vfs::WritableVfs> LanguageService for RubyLanguageService<V> {
51 type Lang = RubyLanguage;
52 type Vfs = V;
53
54 fn vfs(&self) -> &Self::Vfs {
55 &self.vfs
56 }
57
58 fn workspace(&self) -> &oak_lsp::workspace::WorkspaceManager {
59 &self.workspace
60 }
61
62 fn get_root(&self, _uri: &str) -> impl Future<Output = Option<RedNode<'_, RubyLanguage>>> + Send + '_ {
63 async move {
64 None
66 }
67 }
68
69 fn hover(&self, uri: &str, range: Range<usize>) -> impl Future<Output = Option<Hover>> + Send + '_ {
70 let uri = uri.to_string();
71 async move { self.with_root(&uri, |root| self.hover_provider.hover(&root, range)).await }
72 }
73
74 fn completion(&self, _uri: &str, _offset: usize) -> impl Future<Output = Vec<CompletionItem>> + Send + '_ {
75 async move {
76 let mut items = Vec::new();
78
79 let keywords = ["def", "class", "module", "if", "unless", "elsif", "else", "case", "when", "for", "while", "until"];
81
82 for keyword in keywords {
83 items.push(CompletionItem {
84 label: keyword.to_string(),
85 kind: Some(oak_lsp::types::CompletionItemKind::Keyword),
86 detail: Some(format!("Ruby keyword: {}", keyword)),
87 documentation: None,
88 insert_text: Some(keyword.to_string()),
89 });
90 }
91
92 items
93 }
94 }
95
96 fn definition(&self, _uri: &str, _range: Range<usize>) -> impl Future<Output = Vec<LocationRange>> + Send + '_ {
97 async move {
98 vec![]
100 }
101 }
102
103 fn references(&self, _uri: &str, _range: Range<usize>) -> impl Future<Output = Vec<LocationRange>> + Send + '_ {
104 async move {
105 vec![]
107 }
108 }
109
110 fn diagnostics(&self, _uri: &str) -> impl Future<Output = Vec<Diagnostic>> + Send + '_ {
111 async move {
112 vec![]
114 }
115 }
116}
117
118pub async fn start_server() {
120 let vfs = oak_vfs::MemoryVfs::new();
121 let service = Arc::new(RubyLanguageService::new(vfs));
122 let server = oak_lsp::server::LspServer::new(service);
123
124 let stdin = tokio::io::stdin();
126 let stdout = tokio::io::stdout();
127
128 match server.run(stdin, stdout).await {
130 Ok(_) => println!("Ruby LSP server exited successfully"),
131 Err(e) => eprintln!("Ruby LSP server error: {}", e),
132 }
133}