Skip to main content

bynk_lsp/
symbols.rs

1//! Re-exports `bynk-ide`'s symbol logic (#808) — moved there so it's
2//! reachable from `bynk-wasm` too, which can't depend on this crate's
3//! `tower-lsp`/`tokio` stack. See `bynk_ide::symbols` for the real module.
4//!
5//! The one exception is the cross-file lookup trio below: `bynk-ide`'s
6//! versions are `PathBuf`-keyed (wasm has no `Url`), so this crate re-wraps
7//! them in the `Url` shape the LSP handlers actually use. Rust's shadowing
8//! rule lets these locally-defined names win over the glob import.
9pub use bynk_ide::symbols::*;
10
11use std::collections::HashMap;
12use std::path::{Path, PathBuf};
13
14use bynk_syntax::span::Span;
15use tower_lsp::lsp_types::Url;
16
17/// A cross-file declaration lookup result: the URI of the file containing
18/// the declaration, the declaration's source span, and the full source
19/// text of that file (returned because callers need it to convert the
20/// span to an LSP range and to build hover content).
21pub struct CrossFileSymbol {
22    pub uri: Url,
23    pub span: Span,
24    pub source: String,
25}
26
27/// Find `name`'s declaration in any project file other than `current_uri`.
28/// `files` is a pre-read `(path, content)` map (content-ownership track,
29/// #1086, slice 1) — see `bynk_ide::symbols::find_declaration_cross_file`
30/// for the pure logic.
31pub fn find_declaration_cross_file(
32    files: &HashMap<PathBuf, String>,
33    current_uri: &Url,
34    name: &str,
35) -> Option<CrossFileSymbol> {
36    let current_path = current_uri.to_file_path().ok()?;
37    let found = bynk_ide::symbols::find_declaration_cross_file(files, &current_path, name)?;
38    Some(CrossFileSymbol {
39        uri: Url::from_file_path(&found.path).ok()?,
40        span: found.span,
41        source: found.source,
42    })
43}
44
45/// Markdown hover content for `name` from any project file other than
46/// `current_uri`, plus the URI of the file that contributed it. See
47/// `bynk_ide::symbols::describe_symbol_cross_file` for the pure logic.
48pub fn describe_symbol_cross_file(
49    files: &HashMap<PathBuf, String>,
50    current_uri: &Url,
51    name: &str,
52) -> Option<(Url, String)> {
53    let current_path = current_uri.to_file_path().ok()?;
54    let (path, desc) = bynk_ide::symbols::describe_symbol_cross_file(files, &current_path, name)?;
55    Some((Url::from_file_path(&path).ok()?, desc))
56}
57
58/// #848: rewrite every resolvable intra-doc-link candidate in `content`
59/// (hover Markdown — a ```bynk fenced signature, optionally followed by the
60/// declaration's doc-comment prose) into a Markdown link, resolved against
61/// `owner_unit`'s doc-link scope order (`crate::index_queries::resolve_doc_link`).
62/// The fenced signature block (and any author example fence) is never
63/// touched — `scan_doc_link_candidates` skips fenced regions itself. An
64/// unresolved candidate is left exactly as authored — no diagnostic,
65/// matching the render-only decision.
66pub fn linkify_doc_links(
67    content: &str,
68    index: &bynk_check::index::ProjectIndex,
69    doc_scope: &HashMap<String, Vec<String>>,
70    project_root: &Path,
71    owner_unit: &str,
72) -> String {
73    let candidates = bynk_ide::symbols::scan_doc_link_candidates(content);
74    if candidates.is_empty() {
75        return content.to_string();
76    }
77    let mut out = String::with_capacity(content.len());
78    let mut last = 0;
79    for cand in candidates {
80        let Some(def) =
81            crate::index_queries::resolve_doc_link(index, doc_scope, owner_unit, &cand.name)
82        else {
83            continue;
84        };
85        let Ok(url) = Url::from_file_path(project_root.join(&def.path)) else {
86            continue;
87        };
88        out.push_str(&content[last..cand.span.start]);
89        out.push_str(&format!("[{}]({url})", cand.display));
90        last = cand.span.end;
91    }
92    out.push_str(&content[last..]);
93    out
94}