1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3use client::http::HttpClient;
4use language::{LanguageServerName, LspAdapter};
5use std::{any::Any, path::PathBuf, sync::Arc};
6
7pub struct RubyLanguageServer;
8
9#[async_trait]
10impl LspAdapter for RubyLanguageServer {
11 async fn name(&self) -> LanguageServerName {
12 LanguageServerName("solargraph".into())
13 }
14
15 async fn server_args(&self) -> Vec<String> {
16 vec!["stdio".into()]
17 }
18
19 async fn fetch_latest_server_version(
20 &self,
21 _: Arc<dyn HttpClient>,
22 ) -> Result<Box<dyn 'static + Any + Send>> {
23 Ok(Box::new(()))
24 }
25
26 async fn fetch_server_binary(
27 &self,
28 _version: Box<dyn 'static + Send + Any>,
29 _: Arc<dyn HttpClient>,
30 _container_dir: PathBuf,
31 ) -> Result<PathBuf> {
32 Err(anyhow!("solargraph must be installed manually"))
33 }
34
35 async fn cached_server_binary(&self, _container_dir: PathBuf) -> Option<PathBuf> {
36 Some("solargraph".into())
37 }
38
39 async fn label_for_completion(
40 &self,
41 item: &lsp::CompletionItem,
42 language: &Arc<language::Language>,
43 ) -> Option<language::CodeLabel> {
44 let label = &item.label;
45 let grammar = language.grammar()?;
46 let highlight_id = match item.kind? {
47 lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?,
48 lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
49 lsp::CompletionItemKind::CLASS | lsp::CompletionItemKind::MODULE => {
50 grammar.highlight_id_for_name("type")?
51 }
52 lsp::CompletionItemKind::KEYWORD => {
53 if label.starts_with(':') {
54 grammar.highlight_id_for_name("string.special.symbol")?
55 } else {
56 grammar.highlight_id_for_name("keyword")?
57 }
58 }
59 lsp::CompletionItemKind::VARIABLE => {
60 if label.starts_with('@') {
61 grammar.highlight_id_for_name("property")?
62 } else {
63 return None;
64 }
65 }
66 _ => return None,
67 };
68 Some(language::CodeLabel {
69 text: label.clone(),
70 runs: vec![(0..label.len(), highlight_id)],
71 filter_range: 0..label.len(),
72 })
73 }
74
75 async fn label_for_symbol(
76 &self,
77 label: &str,
78 kind: lsp::SymbolKind,
79 language: &Arc<language::Language>,
80 ) -> Option<language::CodeLabel> {
81 let grammar = language.grammar()?;
82 match kind {
83 lsp::SymbolKind::METHOD => {
84 let mut parts = label.split('#');
85 let classes = parts.next()?;
86 let method = parts.next()?;
87 if parts.next().is_some() {
88 return None;
89 }
90
91 let class_id = grammar.highlight_id_for_name("type")?;
92 let method_id = grammar.highlight_id_for_name("function.method")?;
93
94 let mut ix = 0;
95 let mut runs = Vec::new();
96 for (i, class) in classes.split("::").enumerate() {
97 if i > 0 {
98 ix += 2;
99 }
100 let end_ix = ix + class.len();
101 runs.push((ix..end_ix, class_id));
102 ix = end_ix;
103 }
104
105 ix += 1;
106 let end_ix = ix + method.len();
107 runs.push((ix..end_ix, method_id));
108 Some(language::CodeLabel {
109 text: label.to_string(),
110 runs,
111 filter_range: 0..label.len(),
112 })
113 }
114 lsp::SymbolKind::CONSTANT => {
115 let constant_id = grammar.highlight_id_for_name("constant")?;
116 Some(language::CodeLabel {
117 text: label.to_string(),
118 runs: vec![(0..label.len(), constant_id)],
119 filter_range: 0..label.len(),
120 })
121 }
122 lsp::SymbolKind::CLASS | lsp::SymbolKind::MODULE => {
123 let class_id = grammar.highlight_id_for_name("type")?;
124
125 let mut ix = 0;
126 let mut runs = Vec::new();
127 for (i, class) in label.split("::").enumerate() {
128 if i > 0 {
129 ix += "::".len();
130 }
131 let end_ix = ix + class.len();
132 runs.push((ix..end_ix, class_id));
133 ix = end_ix;
134 }
135
136 Some(language::CodeLabel {
137 text: label.to_string(),
138 runs,
139 filter_range: 0..label.len(),
140 })
141 }
142 _ => return None,
143 }
144 }
145}