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}