ruby.rs

  1use anyhow::{anyhow, Result};
  2use async_trait::async_trait;
  3use gpui::AsyncAppContext;
  4use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
  5use lsp::LanguageServerBinary;
  6use project::project_settings::{BinarySettings, ProjectSettings};
  7use settings::Settings;
  8use std::{any::Any, ffi::OsString, path::PathBuf, sync::Arc};
  9
 10pub struct RubyLanguageServer;
 11
 12impl RubyLanguageServer {
 13    const SERVER_NAME: &'static str = "solargraph";
 14
 15    fn server_binary_arguments() -> Vec<OsString> {
 16        vec!["stdio".into()]
 17    }
 18}
 19
 20#[async_trait(?Send)]
 21impl LspAdapter for RubyLanguageServer {
 22    fn name(&self) -> LanguageServerName {
 23        LanguageServerName(Self::SERVER_NAME.into())
 24    }
 25
 26    async fn check_if_user_installed(
 27        &self,
 28        delegate: &dyn LspAdapterDelegate,
 29        cx: &AsyncAppContext,
 30    ) -> Option<LanguageServerBinary> {
 31        let configured_binary = cx.update(|cx| {
 32            ProjectSettings::get_global(cx)
 33                .lsp
 34                .get(Self::SERVER_NAME)
 35                .and_then(|s| s.binary.clone())
 36        });
 37
 38        if let Ok(Some(BinarySettings {
 39            path: Some(path),
 40            arguments,
 41        })) = configured_binary
 42        {
 43            Some(LanguageServerBinary {
 44                path: path.into(),
 45                arguments: arguments
 46                    .unwrap_or_default()
 47                    .iter()
 48                    .map(|arg| arg.into())
 49                    .collect(),
 50                env: None,
 51            })
 52        } else {
 53            let env = delegate.shell_env().await;
 54            let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
 55            Some(LanguageServerBinary {
 56                path,
 57                arguments: Self::server_binary_arguments(),
 58                env: Some(env),
 59            })
 60        }
 61    }
 62
 63    async fn fetch_latest_server_version(
 64        &self,
 65        _: &dyn LspAdapterDelegate,
 66    ) -> Result<Box<dyn 'static + Any + Send>> {
 67        Ok(Box::new(()))
 68    }
 69
 70    async fn fetch_server_binary(
 71        &self,
 72        _version: Box<dyn 'static + Send + Any>,
 73        _container_dir: PathBuf,
 74        _: &dyn LspAdapterDelegate,
 75    ) -> Result<LanguageServerBinary> {
 76        Err(anyhow!("solargraph must be installed manually"))
 77    }
 78
 79    async fn cached_server_binary(
 80        &self,
 81        _: PathBuf,
 82        _: &dyn LspAdapterDelegate,
 83    ) -> Option<LanguageServerBinary> {
 84        Some(LanguageServerBinary {
 85            path: "solargraph".into(),
 86            env: None,
 87            arguments: Self::server_binary_arguments(),
 88        })
 89    }
 90
 91    fn can_be_reinstalled(&self) -> bool {
 92        false
 93    }
 94
 95    async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
 96        None
 97    }
 98
 99    async fn label_for_completion(
100        &self,
101        item: &lsp::CompletionItem,
102        language: &Arc<language::Language>,
103    ) -> Option<language::CodeLabel> {
104        let label = &item.label;
105        let grammar = language.grammar()?;
106        let highlight_id = match item.kind? {
107            lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?,
108            lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
109            lsp::CompletionItemKind::CLASS | lsp::CompletionItemKind::MODULE => {
110                grammar.highlight_id_for_name("type")?
111            }
112            lsp::CompletionItemKind::KEYWORD => {
113                if label.starts_with(':') {
114                    grammar.highlight_id_for_name("string.special.symbol")?
115                } else {
116                    grammar.highlight_id_for_name("keyword")?
117                }
118            }
119            lsp::CompletionItemKind::VARIABLE => {
120                if label.starts_with('@') {
121                    grammar.highlight_id_for_name("property")?
122                } else {
123                    return None;
124                }
125            }
126            _ => return None,
127        };
128        Some(language::CodeLabel {
129            text: label.clone(),
130            runs: vec![(0..label.len(), highlight_id)],
131            filter_range: 0..label.len(),
132        })
133    }
134
135    async fn label_for_symbol(
136        &self,
137        label: &str,
138        kind: lsp::SymbolKind,
139        language: &Arc<language::Language>,
140    ) -> Option<language::CodeLabel> {
141        let grammar = language.grammar()?;
142        match kind {
143            lsp::SymbolKind::METHOD => {
144                let mut parts = label.split('#');
145                let classes = parts.next()?;
146                let method = parts.next()?;
147                if parts.next().is_some() {
148                    return None;
149                }
150
151                let class_id = grammar.highlight_id_for_name("type")?;
152                let method_id = grammar.highlight_id_for_name("function.method")?;
153
154                let mut ix = 0;
155                let mut runs = Vec::new();
156                for (i, class) in classes.split("::").enumerate() {
157                    if i > 0 {
158                        ix += 2;
159                    }
160                    let end_ix = ix + class.len();
161                    runs.push((ix..end_ix, class_id));
162                    ix = end_ix;
163                }
164
165                ix += 1;
166                let end_ix = ix + method.len();
167                runs.push((ix..end_ix, method_id));
168                Some(language::CodeLabel {
169                    text: label.to_string(),
170                    runs,
171                    filter_range: 0..label.len(),
172                })
173            }
174            lsp::SymbolKind::CONSTANT => {
175                let constant_id = grammar.highlight_id_for_name("constant")?;
176                Some(language::CodeLabel {
177                    text: label.to_string(),
178                    runs: vec![(0..label.len(), constant_id)],
179                    filter_range: 0..label.len(),
180                })
181            }
182            lsp::SymbolKind::CLASS | lsp::SymbolKind::MODULE => {
183                let class_id = grammar.highlight_id_for_name("type")?;
184
185                let mut ix = 0;
186                let mut runs = Vec::new();
187                for (i, class) in label.split("::").enumerate() {
188                    if i > 0 {
189                        ix += "::".len();
190                    }
191                    let end_ix = ix + class.len();
192                    runs.push((ix..end_ix, class_id));
193                    ix = end_ix;
194                }
195
196                Some(language::CodeLabel {
197                    text: label.to_string(),
198                    runs,
199                    filter_range: 0..label.len(),
200                })
201            }
202            _ => return None,
203        }
204    }
205}