1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3use gpui::AsyncAppContext;
4use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
5use lsp::LanguageServerBinary;
6use project::project_settings::{BinarySettings, ProjectSettings};
7use settings::Settings;
8use std::{any::Any, ffi::OsString, path::PathBuf, sync::Arc};
9
10pub struct RubyLanguageServer;
11
12impl RubyLanguageServer {
13 const SERVER_NAME: &'static str = "solargraph";
14
15 fn server_binary_arguments() -> Vec<OsString> {
16 vec!["stdio".into()]
17 }
18}
19
20#[async_trait(?Send)]
21impl LspAdapter for RubyLanguageServer {
22 fn name(&self) -> LanguageServerName {
23 LanguageServerName(Self::SERVER_NAME.into())
24 }
25
26 async fn check_if_user_installed(
27 &self,
28 delegate: &dyn LspAdapterDelegate,
29 cx: &AsyncAppContext,
30 ) -> Option<LanguageServerBinary> {
31 let configured_binary = cx.update(|cx| {
32 ProjectSettings::get_global(cx)
33 .lsp
34 .get(Self::SERVER_NAME)
35 .and_then(|s| s.binary.clone())
36 });
37
38 if let Ok(Some(BinarySettings {
39 path: Some(path),
40 arguments,
41 })) = configured_binary
42 {
43 Some(LanguageServerBinary {
44 path: path.into(),
45 arguments: arguments
46 .unwrap_or_default()
47 .iter()
48 .map(|arg| arg.into())
49 .collect(),
50 env: None,
51 })
52 } else {
53 let env = delegate.shell_env().await;
54 let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
55 Some(LanguageServerBinary {
56 path,
57 arguments: Self::server_binary_arguments(),
58 env: Some(env),
59 })
60 }
61 }
62
63 async fn fetch_latest_server_version(
64 &self,
65 _: &dyn LspAdapterDelegate,
66 ) -> Result<Box<dyn 'static + Any + Send>> {
67 Ok(Box::new(()))
68 }
69
70 async fn fetch_server_binary(
71 &self,
72 _version: Box<dyn 'static + Send + Any>,
73 _container_dir: PathBuf,
74 _: &dyn LspAdapterDelegate,
75 ) -> Result<LanguageServerBinary> {
76 Err(anyhow!("solargraph must be installed manually"))
77 }
78
79 async fn cached_server_binary(
80 &self,
81 _: PathBuf,
82 _: &dyn LspAdapterDelegate,
83 ) -> Option<LanguageServerBinary> {
84 Some(LanguageServerBinary {
85 path: "solargraph".into(),
86 env: None,
87 arguments: Self::server_binary_arguments(),
88 })
89 }
90
91 fn can_be_reinstalled(&self) -> bool {
92 false
93 }
94
95 async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
96 None
97 }
98
99 async fn label_for_completion(
100 &self,
101 item: &lsp::CompletionItem,
102 language: &Arc<language::Language>,
103 ) -> Option<language::CodeLabel> {
104 let label = &item.label;
105 let grammar = language.grammar()?;
106 let highlight_id = match item.kind? {
107 lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?,
108 lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
109 lsp::CompletionItemKind::CLASS | lsp::CompletionItemKind::MODULE => {
110 grammar.highlight_id_for_name("type")?
111 }
112 lsp::CompletionItemKind::KEYWORD => {
113 if label.starts_with(':') {
114 grammar.highlight_id_for_name("string.special.symbol")?
115 } else {
116 grammar.highlight_id_for_name("keyword")?
117 }
118 }
119 lsp::CompletionItemKind::VARIABLE => {
120 if label.starts_with('@') {
121 grammar.highlight_id_for_name("property")?
122 } else {
123 return None;
124 }
125 }
126 _ => return None,
127 };
128 Some(language::CodeLabel {
129 text: label.clone(),
130 runs: vec![(0..label.len(), highlight_id)],
131 filter_range: 0..label.len(),
132 })
133 }
134
135 async fn label_for_symbol(
136 &self,
137 label: &str,
138 kind: lsp::SymbolKind,
139 language: &Arc<language::Language>,
140 ) -> Option<language::CodeLabel> {
141 let grammar = language.grammar()?;
142 match kind {
143 lsp::SymbolKind::METHOD => {
144 let mut parts = label.split('#');
145 let classes = parts.next()?;
146 let method = parts.next()?;
147 if parts.next().is_some() {
148 return None;
149 }
150
151 let class_id = grammar.highlight_id_for_name("type")?;
152 let method_id = grammar.highlight_id_for_name("function.method")?;
153
154 let mut ix = 0;
155 let mut runs = Vec::new();
156 for (i, class) in classes.split("::").enumerate() {
157 if i > 0 {
158 ix += 2;
159 }
160 let end_ix = ix + class.len();
161 runs.push((ix..end_ix, class_id));
162 ix = end_ix;
163 }
164
165 ix += 1;
166 let end_ix = ix + method.len();
167 runs.push((ix..end_ix, method_id));
168 Some(language::CodeLabel {
169 text: label.to_string(),
170 runs,
171 filter_range: 0..label.len(),
172 })
173 }
174 lsp::SymbolKind::CONSTANT => {
175 let constant_id = grammar.highlight_id_for_name("constant")?;
176 Some(language::CodeLabel {
177 text: label.to_string(),
178 runs: vec![(0..label.len(), constant_id)],
179 filter_range: 0..label.len(),
180 })
181 }
182 lsp::SymbolKind::CLASS | lsp::SymbolKind::MODULE => {
183 let class_id = grammar.highlight_id_for_name("type")?;
184
185 let mut ix = 0;
186 let mut runs = Vec::new();
187 for (i, class) in label.split("::").enumerate() {
188 if i > 0 {
189 ix += "::".len();
190 }
191 let end_ix = ix + class.len();
192 runs.push((ix..end_ix, class_id));
193 ix = end_ix;
194 }
195
196 Some(language::CodeLabel {
197 text: label.to_string(),
198 runs,
199 filter_range: 0..label.len(),
200 })
201 }
202 _ => return None,
203 }
204 }
205}