diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 76bb4394dd..4c33e7329f 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -12,6 +12,7 @@ mod installation; mod json; mod language_plugin; mod python; +mod ruby; mod rust; mod typescript; @@ -116,8 +117,16 @@ pub async fn init(languages: Arc, _executor: Arc) tree_sitter_html::language(), Some(CachedLspAdapter::new(html::HtmlLspAdapter).await), ), - ("ruby", tree_sitter_ruby::language(), None), - ("erb", tree_sitter_embedded_template::language(), None), + ( + "ruby", + tree_sitter_ruby::language(), + Some(CachedLspAdapter::new(ruby::RubyLanguageServer).await), + ), + ( + "erb", + tree_sitter_embedded_template::language(), + Some(CachedLspAdapter::new(ruby::RubyLanguageServer).await), + ), ] { languages.add(language(name, grammar, lsp_adapter)); } diff --git a/crates/zed/src/languages/ruby.rs b/crates/zed/src/languages/ruby.rs new file mode 100644 index 0000000000..6aad293d34 --- /dev/null +++ b/crates/zed/src/languages/ruby.rs @@ -0,0 +1,145 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use client::http::HttpClient; +use language::{LanguageServerName, LspAdapter}; +use std::{any::Any, path::PathBuf, sync::Arc}; + +pub struct RubyLanguageServer; + +#[async_trait] +impl LspAdapter for RubyLanguageServer { + async fn name(&self) -> LanguageServerName { + LanguageServerName("solargraph".into()) + } + + async fn server_args(&self) -> Vec { + vec!["stdio".into()] + } + + async fn fetch_latest_server_version( + &self, + _: Arc, + ) -> Result> { + Ok(Box::new(())) + } + + async fn fetch_server_binary( + &self, + _version: Box, + _: Arc, + _container_dir: PathBuf, + ) -> Result { + Err(anyhow!("solargraph must be installed manually")) + } + + async fn cached_server_binary(&self, _container_dir: PathBuf) -> Option { + Some("solargraph".into()) + } + + async fn label_for_completion( + &self, + item: &lsp::CompletionItem, + language: &Arc, + ) -> Option { + let label = &item.label; + let grammar = language.grammar()?; + let highlight_id = match item.kind? { + lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?, + lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?, + lsp::CompletionItemKind::CLASS | lsp::CompletionItemKind::MODULE => { + grammar.highlight_id_for_name("type")? + } + lsp::CompletionItemKind::KEYWORD => { + if label.starts_with(":") { + grammar.highlight_id_for_name("string.special.symbol")? + } else { + grammar.highlight_id_for_name("keyword")? + } + } + lsp::CompletionItemKind::VARIABLE => { + if label.starts_with("@") { + grammar.highlight_id_for_name("property")? + } else { + return None; + } + } + _ => return None, + }; + Some(language::CodeLabel { + text: label.clone(), + runs: vec![(0..label.len(), highlight_id)], + filter_range: 0..label.len(), + }) + } + + async fn label_for_symbol( + &self, + label: &str, + kind: lsp::SymbolKind, + language: &Arc, + ) -> Option { + let grammar = language.grammar()?; + match kind { + lsp::SymbolKind::METHOD => { + let mut parts = label.split('#'); + let classes = parts.next()?; + let method = parts.next()?; + if parts.next().is_some() { + return None; + } + + let class_id = grammar.highlight_id_for_name("type")?; + let method_id = grammar.highlight_id_for_name("function.method")?; + + let mut ix = 0; + let mut runs = Vec::new(); + for (i, class) in classes.split("::").enumerate() { + if i > 0 { + ix += 2; + } + let end_ix = ix + class.len(); + runs.push((ix..end_ix, class_id)); + ix = end_ix; + } + + ix += 1; + let end_ix = ix + method.len(); + runs.push((ix..end_ix, method_id)); + Some(language::CodeLabel { + text: label.to_string(), + runs, + filter_range: 0..label.len(), + }) + } + lsp::SymbolKind::CONSTANT => { + let constant_id = grammar.highlight_id_for_name("constant")?; + Some(language::CodeLabel { + text: label.to_string(), + runs: vec![(0..label.len(), constant_id)], + filter_range: 0..label.len(), + }) + } + lsp::SymbolKind::CLASS | lsp::SymbolKind::MODULE => { + let class_id = grammar.highlight_id_for_name("type")?; + + let mut ix = 0; + let mut runs = Vec::new(); + for (i, class) in label.split("::").enumerate() { + if i > 0 { + ix += "::".len(); + } + let end_ix = ix + class.len(); + runs.push((ix..end_ix, class_id)); + ix = end_ix; + } + + Some(language::CodeLabel { + text: label.to_string(), + runs, + filter_range: 0..label.len(), + }) + } + _ => return None, + } + } +}