solargraph.rs

  1use zed::lsp::{Completion, CompletionKind, Symbol, SymbolKind};
  2use zed::{CodeLabel, CodeLabelSpan};
  3use zed_extension_api::{self as zed, Result};
  4
  5pub struct Solargraph {}
  6
  7impl Solargraph {
  8    pub const LANGUAGE_SERVER_ID: &'static str = "solargraph";
  9
 10    pub fn new() -> Self {
 11        Self {}
 12    }
 13
 14    pub fn server_script_path(&mut self, worktree: &zed::Worktree) -> Result<String> {
 15        let path = worktree
 16            .which("solargraph")
 17            .ok_or_else(|| "solargraph must be installed manually".to_string())?;
 18
 19        Ok(path)
 20    }
 21
 22    pub fn label_for_completion(&self, completion: Completion) -> Option<CodeLabel> {
 23        match completion.kind? {
 24            CompletionKind::Method => {
 25                let highlight_name = match completion.kind? {
 26                    CompletionKind::Class | CompletionKind::Module => "type",
 27                    CompletionKind::Constant => "constant",
 28                    CompletionKind::Method => "function.method",
 29                    CompletionKind::Keyword => {
 30                        if completion.label.starts_with(':') {
 31                            "string.special.symbol"
 32                        } else {
 33                            "keyword"
 34                        }
 35                    }
 36                    CompletionKind::Variable => {
 37                        if completion.label.starts_with('@') {
 38                            "property"
 39                        } else {
 40                            return None;
 41                        }
 42                    }
 43                    _ => return None,
 44                };
 45
 46                let len = completion.label.len();
 47                let name_span =
 48                    CodeLabelSpan::literal(completion.label, Some(highlight_name.to_string()));
 49
 50                Some(CodeLabel {
 51                    code: Default::default(),
 52                    spans: if let Some(detail) = completion.detail {
 53                        vec![
 54                            name_span,
 55                            CodeLabelSpan::literal(" ", None),
 56                            CodeLabelSpan::literal(detail, None),
 57                        ]
 58                    } else {
 59                        vec![name_span]
 60                    },
 61                    filter_range: (0..len).into(),
 62                })
 63            }
 64            _ => None,
 65        }
 66    }
 67
 68    pub fn label_for_symbol(&self, symbol: Symbol) -> Option<CodeLabel> {
 69        let name = &symbol.name;
 70
 71        return match symbol.kind {
 72            SymbolKind::Method => {
 73                let mut parts = name.split('#');
 74                let container_name = parts.next()?;
 75                let method_name = parts.next()?;
 76
 77                if parts.next().is_some() {
 78                    return None;
 79                }
 80
 81                let filter_range = 0..name.len();
 82
 83                let spans = vec![
 84                    CodeLabelSpan::literal(container_name, Some("type".to_string())),
 85                    CodeLabelSpan::literal("#", None),
 86                    CodeLabelSpan::literal(method_name, Some("function.method".to_string())),
 87                ];
 88
 89                Some(CodeLabel {
 90                    code: name.to_string(),
 91                    spans,
 92                    filter_range: filter_range.into(),
 93                })
 94            }
 95            SymbolKind::Class | SymbolKind::Module => {
 96                let class = "class ";
 97                let code = format!("{class}{name}");
 98                let filter_range = 0..name.len();
 99                let display_range = class.len()..class.len() + name.len();
100
101                Some(CodeLabel {
102                    code,
103                    spans: vec![CodeLabelSpan::code_range(display_range)],
104                    filter_range: filter_range.into(),
105                })
106            }
107            SymbolKind::Constant => {
108                let code = name.to_uppercase().to_string();
109                let filter_range = 0..name.len();
110                let display_range = 0..name.len();
111
112                Some(CodeLabel {
113                    code,
114                    spans: vec![CodeLabelSpan::code_range(display_range)],
115                    filter_range: filter_range.into(),
116                })
117            }
118            _ => None,
119        };
120    }
121}