1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
4use lsp::LanguageServerBinary;
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 fn short_name(&self) -> &'static str {
16 "solargraph"
17 }
18
19 async fn fetch_latest_server_version(
20 &self,
21 _: &dyn LspAdapterDelegate,
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 _container_dir: PathBuf,
30 _: &dyn LspAdapterDelegate,
31 ) -> Result<LanguageServerBinary> {
32 Err(anyhow!("solargraph must be installed manually"))
33 }
34
35 async fn cached_server_binary(
36 &self,
37 _: PathBuf,
38 _: &dyn LspAdapterDelegate,
39 ) -> Option<LanguageServerBinary> {
40 Some(LanguageServerBinary {
41 path: "solargraph".into(),
42 arguments: vec!["stdio".into()],
43 })
44 }
45
46 fn can_be_reinstalled(&self) -> bool {
47 false
48 }
49
50 async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
51 None
52 }
53
54 async fn label_for_completion(
55 &self,
56 item: &lsp::CompletionItem,
57 language: &Arc<language::Language>,
58 ) -> Option<language::CodeLabel> {
59 let label = &item.label;
60 let grammar = language.grammar()?;
61 let highlight_id = match item.kind? {
62 lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?,
63 lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
64 lsp::CompletionItemKind::CLASS | lsp::CompletionItemKind::MODULE => {
65 grammar.highlight_id_for_name("type")?
66 }
67 lsp::CompletionItemKind::KEYWORD => {
68 if label.starts_with(':') {
69 grammar.highlight_id_for_name("string.special.symbol")?
70 } else {
71 grammar.highlight_id_for_name("keyword")?
72 }
73 }
74 lsp::CompletionItemKind::VARIABLE => {
75 if label.starts_with('@') {
76 grammar.highlight_id_for_name("property")?
77 } else {
78 return None;
79 }
80 }
81 _ => return None,
82 };
83 Some(language::CodeLabel {
84 text: label.clone(),
85 runs: vec![(0..label.len(), highlight_id)],
86 filter_range: 0..label.len(),
87 })
88 }
89
90 async fn label_for_symbol(
91 &self,
92 label: &str,
93 kind: lsp::SymbolKind,
94 language: &Arc<language::Language>,
95 ) -> Option<language::CodeLabel> {
96 let grammar = language.grammar()?;
97 match kind {
98 lsp::SymbolKind::METHOD => {
99 let mut parts = label.split('#');
100 let classes = parts.next()?;
101 let method = parts.next()?;
102 if parts.next().is_some() {
103 return None;
104 }
105
106 let class_id = grammar.highlight_id_for_name("type")?;
107 let method_id = grammar.highlight_id_for_name("function.method")?;
108
109 let mut ix = 0;
110 let mut runs = Vec::new();
111 for (i, class) in classes.split("::").enumerate() {
112 if i > 0 {
113 ix += 2;
114 }
115 let end_ix = ix + class.len();
116 runs.push((ix..end_ix, class_id));
117 ix = end_ix;
118 }
119
120 ix += 1;
121 let end_ix = ix + method.len();
122 runs.push((ix..end_ix, method_id));
123 Some(language::CodeLabel {
124 text: label.to_string(),
125 runs,
126 filter_range: 0..label.len(),
127 })
128 }
129 lsp::SymbolKind::CONSTANT => {
130 let constant_id = grammar.highlight_id_for_name("constant")?;
131 Some(language::CodeLabel {
132 text: label.to_string(),
133 runs: vec![(0..label.len(), constant_id)],
134 filter_range: 0..label.len(),
135 })
136 }
137 lsp::SymbolKind::CLASS | lsp::SymbolKind::MODULE => {
138 let class_id = grammar.highlight_id_for_name("type")?;
139
140 let mut ix = 0;
141 let mut runs = Vec::new();
142 for (i, class) in label.split("::").enumerate() {
143 if i > 0 {
144 ix += "::".len();
145 }
146 let end_ix = ix + class.len();
147 runs.push((ix..end_ix, class_id));
148 ix = end_ix;
149 }
150
151 Some(language::CodeLabel {
152 text: label.to_string(),
153 runs,
154 filter_range: 0..label.len(),
155 })
156 }
157 _ => return None,
158 }
159 }
160}