@@ -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<LanguageRegistry>, _executor: Arc<Background>)
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));
}
@@ -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<String> {
+ vec!["stdio".into()]
+ }
+
+ async fn fetch_latest_server_version(
+ &self,
+ _: Arc<dyn HttpClient>,
+ ) -> Result<Box<dyn 'static + Any + Send>> {
+ Ok(Box::new(()))
+ }
+
+ async fn fetch_server_binary(
+ &self,
+ _version: Box<dyn 'static + Send + Any>,
+ _: Arc<dyn HttpClient>,
+ _container_dir: PathBuf,
+ ) -> Result<PathBuf> {
+ Err(anyhow!("solargraph must be installed manually"))
+ }
+
+ async fn cached_server_binary(&self, _container_dir: PathBuf) -> Option<PathBuf> {
+ Some("solargraph".into())
+ }
+
+ async fn label_for_completion(
+ &self,
+ item: &lsp::CompletionItem,
+ language: &Arc<language::Language>,
+ ) -> Option<language::CodeLabel> {
+ 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<language::Language>,
+ ) -> Option<language::CodeLabel> {
+ 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,
+ }
+ }
+}