1use std::path::{Path, PathBuf};
2
3use code_moniker_core::core::code_graph::CodeGraph;
4use code_moniker_core::core::moniker::Moniker;
5use code_moniker_core::core::uri::{UriConfig, to_uri};
6use code_moniker_core::lang::Lang;
7use std::sync::Arc;
8
9mod project_config;
10
11pub use project_config::{PROJECT_CONFIG_FILE, TelemetryConfig, load_telemetry_config};
12
13pub type ExtractContext = crate::extract::Context;
14pub type IdentityResolver = crate::source::LocalIdentityResolver;
15pub type IndexedSourceMaterial = crate::source::CodeIndexMaterial;
16pub type ResourceCache = crate::source::LocalResourceCache;
17pub type SourceFile = crate::sources::SourceFile;
18pub type SourceFileSet = crate::sources::SourceSet;
19pub type SourceRoot = crate::sources::SourceRoot;
20
21pub fn discover_sources(
22 paths: &[PathBuf],
23 project: Option<String>,
24) -> anyhow::Result<SourceFileSet> {
25 crate::sources::discover(paths, project)
26}
27
28pub fn discover_source_files(
29 root: &Path,
30 files: &[PathBuf],
31 project: Option<String>,
32) -> anyhow::Result<SourceFileSet> {
33 crate::sources::discover_files(root, files, project)
34}
35
36pub fn discover_source_catalog(
37 root: &Path,
38 project: Option<String>,
39) -> anyhow::Result<SourceFileSet> {
40 crate::sources::discover_catalog(root, project)
41}
42
43pub fn language_for_path(path: &Path) -> anyhow::Result<Lang> {
44 Ok(crate::lang::path_to_lang(path)?)
45}
46
47pub fn load_or_extract_source(
48 path: &Path,
49 anchor: &Path,
50 lang: Lang,
51 cache_dir: Option<&Path>,
52 ctx: &ExtractContext,
53) -> anyhow::Result<(CodeGraph, Option<String>)> {
54 Ok(crate::cache::load_or_extract_result(
55 path, anchor, lang, cache_dir, ctx,
56 )?)
57}
58
59pub fn cached_index_material(
60 cache: &ResourceCache,
61 generation: crate::snapshot::ResourceGeneration,
62) -> Option<std::sync::Arc<IndexedSourceMaterial>> {
63 cache.index_material(generation)
64}
65
66pub fn cached_index_diff(
67 cache: &ResourceCache,
68 generation: crate::snapshot::ResourceGeneration,
69) -> Option<(
70 crate::snapshot::ResourceGeneration,
71 Arc<crate::code::CodeIndexGraphDiff>,
72)> {
73 cache.index_diff(generation)
74}
75
76pub fn next_resource_generation(cache: &ResourceCache) -> crate::snapshot::ResourceGeneration {
77 cache.next_generation()
78}
79
80#[cfg(test)]
81pub fn extract_source(lang: Lang, source: &str, path: &Path) -> CodeGraph {
82 crate::extract::extract(lang, source, path)
83}
84
85pub fn extract_source_with(
86 lang: Lang,
87 source: &str,
88 path: &Path,
89 ctx: &ExtractContext,
90) -> CodeGraph {
91 crate::extract::extract_with(lang, source, path, ctx)
92}
93
94pub fn symbol_records_for_graph(
95 file_idx: usize,
96 source_id: crate::snapshot::SourceId,
97 graph: &CodeGraph,
98 source: &str,
99 lang: Lang,
100 scheme: &str,
101) -> Vec<crate::snapshot::SymbolRecord> {
102 let identity = crate::source::LocalIdentityResolver::new(scheme);
103 let lines = crate::lines::LineIndex::new(source);
104 graph
105 .defs()
106 .enumerate()
107 .map(|(def_idx, def)| crate::snapshot::SymbolRecord {
108 id: identity.symbol_id(file_idx, def_idx),
109 source: source_id,
110 identity: Arc::from(identity.moniker_uri(&def.moniker)),
111 name: crate::code::last_name(&def.moniker),
112 kind: crate::code::def_kind(def),
113 visibility: std::str::from_utf8(&def.visibility)
114 .unwrap_or("")
115 .to_string(),
116 signature: String::from_utf8_lossy(&def.signature).to_string(),
117 call_name: (!def.call_name.is_empty())
118 .then(|| String::from_utf8_lossy(&def.call_name).to_string()),
119 call_arity: def.call_arity,
120 navigable: crate::code::is_navigable_def(lang, def),
121 line_range: def
122 .position
123 .map(|(start, end)| lines.line_range(start, end)),
124 parent: def
125 .parent
126 .map(|parent_idx| identity.symbol_id(file_idx, parent_idx)),
127 })
128 .collect()
129}
130
131pub fn source_root_moniker(lang: Lang, path: &Path, ctx: &ExtractContext) -> Option<Moniker> {
132 crate::extract::source_root(lang, path, ctx)
133}
134
135pub fn line_range(source: &str, start: u32, end: u32) -> (u32, u32) {
136 crate::lines::line_range(source, start, end)
137}
138
139pub fn compact_moniker(moniker: &Moniker, scheme: &str) -> String {
140 render_compact_moniker(moniker).unwrap_or_else(|| to_uri(moniker, &UriConfig { scheme }))
141}
142
143fn render_compact_moniker(moniker: &Moniker) -> Option<String> {
144 let view = moniker.as_view();
145 let mut lang: Option<String> = None;
146 let mut packages: Vec<String> = Vec::new();
147 let mut dirs: Vec<String> = Vec::new();
148 let mut modules: Vec<String> = Vec::new();
149 let mut rest: Vec<(String, String)> = Vec::new();
150 for segment in view.segments() {
151 let kind = std::str::from_utf8(segment.kind).ok()?.to_string();
152 let name = std::str::from_utf8(segment.name).ok()?.to_string();
153 match kind.as_str() {
154 "lang" => lang = Some(name),
155 "package" => packages.push(name),
156 "dir" => dirs.push(name),
157 "module" => modules.push(name),
158 _ => rest.push((kind, name)),
159 }
160 }
161 let head = lang.unwrap_or_else(|| {
162 std::str::from_utf8(view.project())
163 .unwrap_or(".")
164 .to_string()
165 });
166 if packages.is_empty() && dirs.is_empty() && modules.is_empty() && rest.is_empty() {
167 return Some(head);
168 }
169 let mut out = String::new();
170 out.push_str(&head);
171 out.push(':');
172 let mut wrote_scope = false;
173 if !packages.is_empty() {
174 out.push_str(&packages.join("."));
175 wrote_scope = true;
176 } else if !dirs.is_empty() {
177 out.push_str(&dirs.join("/"));
178 wrote_scope = true;
179 }
180 let has_module = !modules.is_empty();
181 if has_module {
182 if wrote_scope {
183 out.push('/');
184 }
185 out.push_str(&modules.join("."));
186 }
187 for (idx, (kind, name)) in rest.iter().enumerate() {
188 if idx == 0 && has_module {
189 out.push('.');
190 } else if wrote_scope || has_module || idx > 0 {
191 out.push('/');
192 }
193 out.push_str(kind);
194 out.push(':');
195 out.push_str(name);
196 }
197 Some(out)
198}