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