Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:37:26.761Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

signature_help.rs

272 lines · 11.1 KB · rust
1use std::{ops::Range, sync::Arc};
2
3use gpui::{App, AppContext, Entity, FontWeight, HighlightStyle, SharedString};
4use language::LanguageRegistry;
5use lsp::LanguageServerId;
6use markdown::Markdown;
7use rpc::proto::{self, documentation};
8use util::maybe;
9
10#[derive(Debug)]
11pub struct SignatureHelp {
12    pub active_signature: usize,
13    pub signatures: Vec<SignatureHelpData>,
14    pub(super) original_data: lsp::SignatureHelp,
15}
16
17#[derive(Debug, Clone)]
18pub struct SignatureHelpData {
19    pub label: SharedString,
20    pub documentation: Option<Entity<Markdown>>,
21    pub highlights: Vec<(Range<usize>, HighlightStyle)>,
22    pub active_parameter: Option<usize>,
23    pub parameters: Vec<ParameterInfo>,
24}
25
26#[derive(Debug, Clone)]
27pub struct ParameterInfo {
28    pub label_range: Option<Range<usize>>,
29    pub documentation: Option<Entity<Markdown>>,
30}
31
32impl SignatureHelp {
33    pub fn new(
34        help: lsp::SignatureHelp,
35        language_registry: Option<Arc<LanguageRegistry>>,
36        lang_server_id: Option<LanguageServerId>,
37        cx: &mut App,
38    ) -> Option<Self> {
39        if help.signatures.is_empty() {
40            return None;
41        }
42        let active_signature = help.active_signature.unwrap_or(0) as usize;
43        let mut signatures = Vec::<SignatureHelpData>::with_capacity(help.signatures.capacity());
44        for signature in &help.signatures {
45            let label = SharedString::from(signature.label.clone());
46            let active_parameter = signature
47                .active_parameter
48                .unwrap_or_else(|| help.active_parameter.unwrap_or(0))
49                as usize;
50            let mut highlights = Vec::new();
51            let mut parameter_infos = Vec::new();
52
53            if let Some(parameters) = &signature.parameters {
54                for (index, parameter) in parameters.iter().enumerate() {
55                    let label_range = match &parameter.label {
56                        &lsp::ParameterLabel::LabelOffsets([offset1, offset2]) => {
57                            maybe!({
58                                let offset1 = offset1 as usize;
59                                let offset2 = offset2 as usize;
60                                if offset1 < offset2 {
61                                    let mut indices = label.char_indices().scan(
62                                        0,
63                                        |utf16_offset_acc, (offset, c)| {
64                                            let utf16_offset = *utf16_offset_acc;
65                                            *utf16_offset_acc += c.len_utf16();
66                                            Some((utf16_offset, offset))
67                                        },
68                                    );
69                                    let (_, offset1) = indices
70                                        .find(|(utf16_offset, _)| *utf16_offset == offset1)?;
71                                    let (_, offset2) = indices
72                                        .find(|(utf16_offset, _)| *utf16_offset == offset2)?;
73                                    Some(offset1..offset2)
74                                } else {
75                                    log::warn!(
76                                        "language server {lang_server_id:?} produced invalid parameter label range: {offset1:?}..{offset2:?}",
77                                    );
78                                    None
79                                }
80                            })
81                        }
82                        lsp::ParameterLabel::Simple(parameter_label) => {
83                            if let Some(start) = signature.label.find(parameter_label) {
84                                Some(start..start + parameter_label.len())
85                            } else {
86                                None
87                            }
88                        }
89                    };
90
91                    if let Some(label_range) = &label_range
92                        && index == active_parameter
93                    {
94                        highlights.push((
95                            label_range.clone(),
96                            HighlightStyle {
97                                font_weight: Some(FontWeight::EXTRA_BOLD),
98                                ..HighlightStyle::default()
99                            },
100                        ));
101                    }
102
103                    let documentation = parameter
104                        .documentation
105                        .as_ref()
106                        .map(|doc| documentation_to_markdown(doc, language_registry.clone(), cx));
107
108                    parameter_infos.push(ParameterInfo {
109                        label_range,
110                        documentation,
111                    });
112                }
113            }
114
115            let documentation = signature
116                .documentation
117                .as_ref()
118                .map(|doc| documentation_to_markdown(doc, language_registry.clone(), cx));
119
120            signatures.push(SignatureHelpData {
121                label,
122                documentation,
123                highlights,
124                active_parameter: Some(active_parameter),
125                parameters: parameter_infos,
126            });
127        }
128        Some(Self {
129            signatures,
130            active_signature,
131            original_data: help,
132        })
133    }
134}
135
136fn documentation_to_markdown(
137    documentation: &lsp::Documentation,
138    language_registry: Option<Arc<LanguageRegistry>>,
139    cx: &mut App,
140) -> Entity<Markdown> {
141    match documentation {
142        lsp::Documentation::String(string) => {
143            cx.new(|cx| Markdown::new_text(SharedString::from(string), cx))
144        }
145        lsp::Documentation::MarkupContent(markup) => match markup.kind {
146            lsp::MarkupKind::PlainText => {
147                cx.new(|cx| Markdown::new_text(SharedString::from(&markup.value), cx))
148            }
149            lsp::MarkupKind::Markdown => cx.new(|cx| {
150                Markdown::new(
151                    SharedString::from(&markup.value),
152                    language_registry,
153                    None,
154                    cx,
155                )
156            }),
157        },
158    }
159}
160
161pub fn lsp_to_proto_signature(lsp_help: lsp::SignatureHelp) -> proto::SignatureHelp {
162    proto::SignatureHelp {
163        signatures: lsp_help
164            .signatures
165            .into_iter()
166            .map(|signature| proto::SignatureInformation {
167                label: signature.label,
168                documentation: signature.documentation.map(lsp_to_proto_documentation),
169                parameters: signature
170                    .parameters
171                    .unwrap_or_default()
172                    .into_iter()
173                    .map(|parameter_info| proto::ParameterInformation {
174                        label: Some(match parameter_info.label {
175                            lsp::ParameterLabel::Simple(label) => {
176                                proto::parameter_information::Label::Simple(label)
177                            }
178                            lsp::ParameterLabel::LabelOffsets(offsets) => {
179                                proto::parameter_information::Label::LabelOffsets(
180                                    proto::LabelOffsets {
181                                        start: offsets[0],
182                                        end: offsets[1],
183                                    },
184                                )
185                            }
186                        }),
187                        documentation: parameter_info.documentation.map(lsp_to_proto_documentation),
188                    })
189                    .collect(),
190                active_parameter: signature.active_parameter,
191            })
192            .collect(),
193        active_signature: lsp_help.active_signature,
194        active_parameter: lsp_help.active_parameter,
195    }
196}
197
198fn lsp_to_proto_documentation(documentation: lsp::Documentation) -> proto::Documentation {
199    proto::Documentation {
200        content: Some(match documentation {
201            lsp::Documentation::String(string) => proto::documentation::Content::Value(string),
202            lsp::Documentation::MarkupContent(content) => {
203                proto::documentation::Content::MarkupContent(proto::MarkupContent {
204                    is_markdown: matches!(content.kind, lsp::MarkupKind::Markdown),
205                    value: content.value,
206                })
207            }
208        }),
209    }
210}
211
212pub fn proto_to_lsp_signature(proto_help: proto::SignatureHelp) -> lsp::SignatureHelp {
213    lsp::SignatureHelp {
214        signatures: proto_help
215            .signatures
216            .into_iter()
217            .map(|signature| lsp::SignatureInformation {
218                label: signature.label,
219                documentation: signature.documentation.and_then(proto_to_lsp_documentation),
220                parameters: Some(
221                    signature
222                        .parameters
223                        .into_iter()
224                        .filter_map(|parameter_info| {
225                            Some(lsp::ParameterInformation {
226                                label: match parameter_info.label? {
227                                    proto::parameter_information::Label::Simple(string) => {
228                                        lsp::ParameterLabel::Simple(string)
229                                    }
230                                    proto::parameter_information::Label::LabelOffsets(offsets) => {
231                                        lsp::ParameterLabel::LabelOffsets([
232                                            offsets.start,
233                                            offsets.end,
234                                        ])
235                                    }
236                                },
237                                documentation: parameter_info
238                                    .documentation
239                                    .and_then(proto_to_lsp_documentation),
240                            })
241                        })
242                        .collect(),
243                ),
244                active_parameter: signature.active_parameter,
245            })
246            .collect(),
247        active_signature: proto_help.active_signature,
248        active_parameter: proto_help.active_parameter,
249    }
250}
251
252fn proto_to_lsp_documentation(documentation: proto::Documentation) -> Option<lsp::Documentation> {
253    {
254        Some(match documentation.content? {
255            documentation::Content::Value(string) => lsp::Documentation::String(string),
256            documentation::Content::MarkupContent(markup) => {
257                lsp::Documentation::MarkupContent(if markup.is_markdown {
258                    lsp::MarkupContent {
259                        kind: lsp::MarkupKind::Markdown,
260                        value: markup.value,
261                    }
262                } else {
263                    lsp::MarkupContent {
264                        kind: lsp::MarkupKind::PlainText,
265                        value: markup.value,
266                    }
267                })
268            }
269        })
270    }
271}
272
Served at tenant.openagents/omega Member data and write actions are omitted.