use std::path::{Path, PathBuf};
use code_moniker_core::core::code_graph::CodeGraph;
use code_moniker_core::core::moniker::Moniker;
use code_moniker_core::core::uri::{UriConfig, to_uri};
use code_moniker_core::lang::Lang;
use std::sync::Arc;
mod project_config;
pub use project_config::{PROJECT_CONFIG_FILE, TelemetryConfig, load_telemetry_config};
pub type ExtractContext = crate::extract::Context;
pub type IdentityResolver = crate::source::LocalIdentityResolver;
pub type IndexedSourceMaterial = crate::source::CodeIndexMaterial;
pub type ResourceCache = crate::source::LocalResourceCache;
pub type SourceFile = crate::sources::SourceFile;
pub type SourceFileSet = crate::sources::SourceSet;
pub type SourceRoot = crate::sources::SourceRoot;
pub fn discover_sources(
paths: &[PathBuf],
project: Option<String>,
) -> anyhow::Result<SourceFileSet> {
crate::sources::discover(paths, project)
}
pub fn discover_source_files(
root: &Path,
files: &[PathBuf],
project: Option<String>,
) -> anyhow::Result<SourceFileSet> {
crate::sources::discover_files(root, files, project)
}
pub fn discover_source_catalog(
root: &Path,
project: Option<String>,
) -> anyhow::Result<SourceFileSet> {
crate::sources::discover_catalog(root, project)
}
pub fn language_for_path(path: &Path) -> anyhow::Result<Lang> {
Ok(crate::lang::path_to_lang(path)?)
}
pub fn load_or_extract_source(
path: &Path,
anchor: &Path,
lang: Lang,
cache_dir: Option<&Path>,
ctx: &ExtractContext,
) -> anyhow::Result<(CodeGraph, Option<String>)> {
Ok(crate::cache::load_or_extract_result(
path, anchor, lang, cache_dir, ctx,
)?)
}
pub fn cached_index_material(
cache: &ResourceCache,
generation: crate::snapshot::ResourceGeneration,
) -> Option<std::sync::Arc<IndexedSourceMaterial>> {
cache.index_material(generation)
}
pub fn cached_index_diff(
cache: &ResourceCache,
generation: crate::snapshot::ResourceGeneration,
) -> Option<(
crate::snapshot::ResourceGeneration,
Arc<crate::code::CodeIndexGraphDiff>,
)> {
cache.index_diff(generation)
}
pub fn next_resource_generation(cache: &ResourceCache) -> crate::snapshot::ResourceGeneration {
cache.next_generation()
}
#[cfg(test)]
pub fn extract_source(lang: Lang, source: &str, path: &Path) -> CodeGraph {
crate::extract::extract(lang, source, path)
}
pub fn extract_source_with(
lang: Lang,
source: &str,
path: &Path,
ctx: &ExtractContext,
) -> CodeGraph {
crate::extract::extract_with(lang, source, path, ctx)
}
pub fn symbol_records_for_graph(
file_idx: usize,
source_id: crate::snapshot::SourceId,
graph: &CodeGraph,
source: &str,
lang: Lang,
scheme: &str,
) -> Vec<crate::snapshot::SymbolRecord> {
let identity = crate::source::LocalIdentityResolver::new(scheme);
let lines = crate::lines::LineIndex::new(source);
graph
.defs()
.enumerate()
.map(|(def_idx, def)| crate::snapshot::SymbolRecord {
id: identity.symbol_id(file_idx, def_idx),
source: source_id,
identity: Arc::from(identity.moniker_uri(&def.moniker)),
name: crate::code::last_name(&def.moniker),
kind: crate::code::def_kind(def),
visibility: std::str::from_utf8(&def.visibility)
.unwrap_or("")
.to_string(),
signature: String::from_utf8_lossy(&def.signature).to_string(),
call_name: (!def.call_name.is_empty())
.then(|| String::from_utf8_lossy(&def.call_name).to_string()),
call_arity: def.call_arity,
navigable: crate::code::is_navigable_def(lang, def),
line_range: def
.position
.map(|(start, end)| lines.line_range(start, end)),
parent: def
.parent
.map(|parent_idx| identity.symbol_id(file_idx, parent_idx)),
})
.collect()
}
pub fn source_root_moniker(lang: Lang, path: &Path, ctx: &ExtractContext) -> Option<Moniker> {
crate::extract::source_root(lang, path, ctx)
}
pub fn line_range(source: &str, start: u32, end: u32) -> (u32, u32) {
crate::lines::line_range(source, start, end)
}
pub fn compact_moniker(moniker: &Moniker, scheme: &str) -> String {
render_compact_moniker(moniker).unwrap_or_else(|| to_uri(moniker, &UriConfig { scheme }))
}
fn render_compact_moniker(moniker: &Moniker) -> Option<String> {
let view = moniker.as_view();
let mut lang: Option<String> = None;
let mut packages: Vec<String> = Vec::new();
let mut dirs: Vec<String> = Vec::new();
let mut modules: Vec<String> = Vec::new();
let mut rest: Vec<(String, String)> = Vec::new();
for segment in view.segments() {
let kind = std::str::from_utf8(segment.kind).ok()?.to_string();
let name = std::str::from_utf8(segment.name).ok()?.to_string();
match kind.as_str() {
"lang" => lang = Some(name),
"package" => packages.push(name),
"dir" => dirs.push(name),
"module" => modules.push(name),
_ => rest.push((kind, name)),
}
}
let head = lang.unwrap_or_else(|| {
std::str::from_utf8(view.project())
.unwrap_or(".")
.to_string()
});
if packages.is_empty() && dirs.is_empty() && modules.is_empty() && rest.is_empty() {
return Some(head);
}
let mut out = String::new();
out.push_str(&head);
out.push(':');
let mut wrote_scope = false;
if !packages.is_empty() {
out.push_str(&packages.join("."));
wrote_scope = true;
} else if !dirs.is_empty() {
out.push_str(&dirs.join("/"));
wrote_scope = true;
}
let has_module = !modules.is_empty();
if has_module {
if wrote_scope {
out.push('/');
}
out.push_str(&modules.join("."));
}
for (idx, (kind, name)) in rest.iter().enumerate() {
if idx == 0 && has_module {
out.push('.');
} else if wrote_scope || has_module || idx > 0 {
out.push('/');
}
out.push_str(kind);
out.push(':');
out.push_str(name);
}
Some(out)
}