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