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