Skip to main content

ruby_lsp/
lib.rs

1//! Ruby Language Server Protocol implementation
2//!
3//! This crate provides a Language Server Protocol (LSP) implementation for Ruby
4//! using the Oak LSP framework.
5
6#![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
24/// Language service implementation for Ruby.
25pub 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    /// Creates a new `RubyLanguageService`.
33    pub fn new(vfs: V) -> Self {
34        Self { vfs, workspace: oak_lsp::workspace::WorkspaceManager::default(), hover_provider: RubyHoverProvider::new() }
35    }
36
37    /// Gets the root node of the parsed tree for the given URI.
38    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            // TODO: Implement proper caching of parsed trees in LanguageService
65            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            // TODO: Implement proper completion based on context
77            let mut items = Vec::new();
78
79            // Add some basic Ruby keywords
80            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            // TODO: Implement proper definition finding
99            vec![]
100        }
101    }
102
103    fn references(&self, _uri: &str, _range: Range<usize>) -> impl Future<Output = Vec<LocationRange>> + Send + '_ {
104        async move {
105            // TODO: Implement proper reference finding
106            vec![]
107        }
108    }
109
110    fn diagnostics(&self, _uri: &str) -> impl Future<Output = Vec<Diagnostic>> + Send + '_ {
111        async move {
112            // TODO: Implement proper diagnostics
113            vec![]
114        }
115    }
116}
117
118/// Starts the Ruby LSP server.
119pub 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    // Get standard input and output streams
125    let stdin = tokio::io::stdin();
126    let stdout = tokio::io::stdout();
127
128    // Run the server
129    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}