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