codelens_engine/call_graph/
language.rs1use std::path::Path;
2use tree_sitter::Language;
3
4use super::queries::*;
5
6pub(crate) struct CallLanguageConfig {
7 pub(crate) language_key: &'static str,
9 pub(crate) language: Language,
10 pub(crate) func_query: &'static str,
12 pub(crate) call_query: &'static str,
14}
15
16pub(crate) fn call_language_for_path(path: &Path) -> Option<CallLanguageConfig> {
17 let lang_config = crate::lang_config::language_for_path(path)?;
18 let (language_key, func_query, call_query) = match lang_config.extension {
20 "py" => ("py", PYTHON_FUNC_QUERY, PYTHON_CALL_QUERY),
21 "js" => ("js", JS_FUNC_QUERY, JS_JSX_CALL_QUERY),
22 "ts" => ("ts", JS_FUNC_QUERY, JS_CALL_QUERY),
23 "tsx" => ("tsx", JS_FUNC_QUERY, JS_JSX_CALL_QUERY),
24 "go" => ("go", GO_FUNC_QUERY, GO_CALL_QUERY),
25 "java" => ("java", JAVA_FUNC_QUERY, JAVA_CALL_QUERY),
26 "kt" => ("kt", KOTLIN_FUNC_QUERY, KOTLIN_CALL_QUERY),
27 "rs" => ("rs", RUST_FUNC_QUERY, RUST_CALL_QUERY),
28 _ => return None,
29 };
30 Some(CallLanguageConfig {
31 language_key,
32 language: lang_config.language,
33 func_query,
34 call_query,
35 })
36}
37
38pub(crate) fn call_language_key_for_path(path: &str) -> Option<&'static str> {
39 match Path::new(path).extension().and_then(|value| value.to_str()) {
40 Some("py") => Some("py"),
41 Some("js") => Some("js"),
42 Some("jsx") => Some("jsx"),
43 Some("ts") => Some("ts"),
44 Some("tsx") => Some("tsx"),
45 Some("go") => Some("go"),
46 Some("java") => Some("java"),
47 Some("kt") => Some("kt"),
48 Some("rs") => Some("rs"),
49 _ => None,
50 }
51}
52
53pub(crate) fn same_call_language(a: &str, b: &str) -> bool {
54 call_language_key_for_path(a)
55 .is_some_and(|a_lang| Some(a_lang) == call_language_key_for_path(b))
56}
57
58pub(crate) fn shared_parent_component_count(a: &str, b: &str) -> usize {
59 let a_components: Vec<String> = Path::new(a)
60 .parent()
61 .into_iter()
62 .flat_map(|path| path.components())
63 .map(|component| component.as_os_str().to_string_lossy().into_owned())
64 .collect();
65 let b_components: Vec<String> = Path::new(b)
66 .parent()
67 .into_iter()
68 .flat_map(|path| path.components())
69 .map(|component| component.as_os_str().to_string_lossy().into_owned())
70 .collect();
71
72 a_components
73 .iter()
74 .zip(b_components.iter())
75 .take_while(|(a, b)| a == b)
76 .count()
77}
78
79pub(crate) fn best_path_proximity_candidate<'a>(
80 caller_file: &str,
81 defs: &'a [String],
82) -> Option<&'a String> {
83 defs.iter()
84 .filter(|def| {
85 same_call_language(caller_file, def)
86 && shared_parent_component_count(caller_file, def) > 0
87 })
88 .max_by_key(|def| shared_parent_component_count(caller_file, def))
89}