leekscript_rs/
signatures.rs1use std::collections::HashMap;
8use std::fs;
9use std::path::{Path, PathBuf};
10
11use sipha::line_index::LineIndex;
12use sipha::red::SyntaxNode;
13
14use crate::parse_signatures;
15use leekscript_core::syntax::Kind;
16
17pub const DEFAULT_SIGNATURES_DIR: &str = "examples/signatures";
20
21#[must_use]
23pub fn load_signatures_from_dir(dir: &Path) -> Vec<SyntaxNode> {
24 let mut roots = Vec::new();
25 let Ok(entries) = fs::read_dir(dir) else {
26 return roots;
27 };
28 let mut files: Vec<_> = entries
29 .filter_map(std::result::Result::ok)
30 .map(|e| e.path())
31 .filter(|p| p.is_file() && p.extension().is_some_and(|e| e == "sig"))
32 .collect();
33 files.sort_by_key(|p| p.as_os_str().to_owned());
34 for path in files {
35 if let Ok(s) = fs::read_to_string(&path) {
36 if let Ok(Some(node)) = parse_signatures(&s) {
37 roots.push(node);
38 }
39 }
40 }
41 roots
42}
43
44#[must_use]
48pub fn default_signature_roots() -> Vec<SyntaxNode> {
49 default_signature_roots_with_locations().0
50}
51
52#[must_use]
54pub fn build_sig_definition_locations(
55 path: PathBuf,
56 source: &str,
57 root: &SyntaxNode,
58) -> HashMap<String, (PathBuf, u32)> {
59 let mut out = HashMap::new();
60 let line_index = LineIndex::new(source.as_bytes());
61 let file_nodes: Vec<SyntaxNode> = if root.kind_as::<Kind>() == Some(Kind::NodeSigFile) {
62 vec![root.clone()]
63 } else {
64 root.children()
65 .filter_map(|c| c.as_node().cloned())
66 .filter(|n| n.kind_as::<Kind>() == Some(Kind::NodeSigFile))
67 .collect()
68 };
69 for file in file_nodes {
70 for child in file.children().filter_map(|c| c.as_node().cloned()) {
71 if child.kind_as::<Kind>() == Some(Kind::NodeSigFunction)
72 || child.kind_as::<Kind>() == Some(Kind::NodeSigGlobal)
73 {
74 let name = child
75 .descendant_tokens()
76 .into_iter()
77 .find(|t| t.kind_as::<Kind>() == Some(Kind::TokIdent))
78 .map(|t| t.text().to_string());
79 if let Some(name) = name {
80 let start = child.text_range().start;
81 let (line, _) = line_index.line_col(start as u32);
82 out.insert(name, (path.clone(), line));
83 }
84 }
85 }
86 }
87 out
88}
89
90#[must_use]
93pub fn default_signature_roots_with_locations() -> (Vec<SyntaxNode>, HashMap<String, (PathBuf, u32)>)
94{
95 let candidates: Vec<PathBuf> =
96 if let Some(ref d) = std::env::var_os("LEEKSCRIPT_SIGNATURES_DIR") {
97 vec![d.into()]
98 } else {
99 vec![
100 PathBuf::from(DEFAULT_SIGNATURES_DIR),
101 PathBuf::from("leekscript-rs/examples/signatures"),
102 ]
103 };
104 for dir in candidates {
105 if dir.is_dir() {
106 let (roots, locations) = load_signatures_from_dir_with_locations(&dir);
107 if !roots.is_empty() {
108 return (roots, locations);
109 }
110 }
111 }
112 (Vec::new(), HashMap::new())
113}
114
115#[must_use]
117pub fn load_signatures_from_dir_with_locations(
118 dir: &Path,
119) -> (Vec<SyntaxNode>, HashMap<String, (PathBuf, u32)>) {
120 let mut roots = Vec::new();
121 let mut locations = HashMap::new();
122 let Ok(entries) = fs::read_dir(dir) else {
123 return (roots, locations);
124 };
125 let mut files: Vec<_> = entries
126 .filter_map(std::result::Result::ok)
127 .map(|e| e.path())
128 .filter(|p| p.is_file() && p.extension().is_some_and(|e| e == "sig"))
129 .collect();
130 files.sort_by_key(|p| p.as_os_str().to_owned());
131 for path in files {
132 if let Ok(s) = fs::read_to_string(&path) {
133 if let Ok(Some(node)) = parse_signatures(&s) {
134 let path_buf = path.to_path_buf();
135 for (name, (_, line)) in build_sig_definition_locations(path_buf.clone(), &s, &node)
136 {
137 locations.insert(name, (path_buf.clone(), line));
138 }
139 roots.push(node);
140 }
141 }
142 }
143 (roots, locations)
144}