solidity_language_server/
links.rs1use crate::goto::{CachedBuild, bytes_to_pos};
2use crate::types::SourceLoc;
3use tower_lsp::lsp_types::{DocumentLink, Range, Url};
4
5pub fn document_links(
11 build: &CachedBuild,
12 file_uri: &Url,
13 source_bytes: &[u8],
14) -> Vec<DocumentLink> {
15 let mut links = Vec::new();
16
17 let file_path = match file_uri.to_file_path() {
18 Ok(p) => p,
19 Err(_) => return links,
20 };
21 let file_path_str = match file_path.to_str() {
22 Some(s) => s,
23 None => return links,
24 };
25
26 let abs_path = match build.path_to_abs.get(file_path_str) {
27 Some(a) => a.as_str(),
28 None => return links,
29 };
30
31 let file_nodes = match build.nodes.get(abs_path) {
32 Some(n) => n,
33 None => return links,
34 };
35
36 for (_id, node_info) in file_nodes.iter() {
37 if node_info.node_type.as_deref() == Some("ImportDirective")
38 && let Some(link) = import_link(node_info, source_bytes)
39 {
40 links.push(link);
41 }
42 }
43
44 links.sort_by(|a, b| {
45 a.range
46 .start
47 .line
48 .cmp(&b.range.start.line)
49 .then(a.range.start.character.cmp(&b.range.start.character))
50 });
51
52 links
53}
54
55fn import_link(node_info: &crate::goto::NodeInfo, source_bytes: &[u8]) -> Option<DocumentLink> {
58 let absolute_path = node_info.absolute_path.as_deref()?;
59 let src_loc = SourceLoc::parse(&node_info.src)?;
60 let (start_byte, length) = (src_loc.offset, src_loc.length);
61 let end_byte = start_byte + length;
62
63 if end_byte > source_bytes.len() || end_byte < 3 {
64 return None;
65 }
66
67 let close_quote = end_byte - 2;
69 let open_quote = (start_byte..close_quote)
70 .rev()
71 .find(|&i| source_bytes[i] == b'"' || source_bytes[i] == b'\'')?;
72
73 let start_pos = bytes_to_pos(source_bytes, open_quote + 1)?;
74 let end_pos = bytes_to_pos(source_bytes, close_quote)?;
75
76 let target_path = std::path::Path::new(absolute_path);
77 let full_path = if target_path.is_absolute() {
78 target_path.to_path_buf()
79 } else {
80 std::env::current_dir().ok()?.join(target_path)
81 };
82 let target_uri = Url::from_file_path(&full_path).ok()?;
83
84 Some(DocumentLink {
85 range: Range {
86 start: start_pos,
87 end: end_pos,
88 },
89 target: Some(target_uri),
90 tooltip: Some(absolute_path.to_string()),
91 data: None,
92 })
93}