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 let highlight_name = match completion.kind? {
24 CompletionKind::Class | CompletionKind::Module => "type",
25 CompletionKind::Constant => "constant",
26 CompletionKind::Method => "function.method",
27 CompletionKind::Keyword => {
28 if completion.label.starts_with(':') {
29 "string.special.symbol"
30 } else {
31 "keyword"
32 }
33 }
34 CompletionKind::Variable => {
35 if completion.label.starts_with('@') {
36 "property"
37 } else {
38 return None;
39 }
40 }
41 _ => return None,
42 };
43
44 let len = completion.label.len();
45 let name_span = CodeLabelSpan::literal(completion.label, Some(highlight_name.to_string()));
46
47 Some(CodeLabel {
48 code: Default::default(),
49 spans: if let Some(detail) = completion.detail {
50 vec![
51 name_span,
52 CodeLabelSpan::literal(" ", None),
53 CodeLabelSpan::literal(detail, None),
54 ]
55 } else {
56 vec![name_span]
57 },
58 filter_range: (0..len).into(),
59 })
60 }
61
62 pub fn label_for_symbol(&self, symbol: Symbol) -> Option<CodeLabel> {
63 let name = &symbol.name;
64
65 return match symbol.kind {
66 SymbolKind::Method => {
67 let mut parts = name.split('#');
68 let container_name = parts.next()?;
69 let method_name = parts.next()?;
70
71 if parts.next().is_some() {
72 return None;
73 }
74
75 let filter_range = 0..name.len();
76
77 let spans = vec![
78 CodeLabelSpan::literal(container_name, Some("type".to_string())),
79 CodeLabelSpan::literal("#", None),
80 CodeLabelSpan::literal(method_name, Some("function.method".to_string())),
81 ];
82
83 Some(CodeLabel {
84 code: name.to_string(),
85 spans,
86 filter_range: filter_range.into(),
87 })
88 }
89 SymbolKind::Class | SymbolKind::Module => {
90 let class = "class ";
91 let code = format!("{class}{name}");
92 let filter_range = 0..name.len();
93 let display_range = class.len()..class.len() + name.len();
94
95 Some(CodeLabel {
96 code,
97 spans: vec![CodeLabelSpan::code_range(display_range)],
98 filter_range: filter_range.into(),
99 })
100 }
101 SymbolKind::Constant => {
102 let code = name.to_uppercase().to_string();
103 let filter_range = 0..name.len();
104 let display_range = 0..name.len();
105
106 Some(CodeLabel {
107 code,
108 spans: vec![CodeLabelSpan::code_range(display_range)],
109 filter_range: filter_range.into(),
110 })
111 }
112 _ => None,
113 };
114 }
115}