Skip to main content

chronicle/ast/
mod.rs

1pub mod anchor;
2pub mod outline;
3
4#[cfg(feature = "lang-c")]
5mod outline_c;
6#[cfg(feature = "lang-cpp")]
7mod outline_cpp;
8#[cfg(feature = "lang-go")]
9mod outline_go;
10#[cfg(feature = "lang-java")]
11mod outline_java;
12#[cfg(feature = "lang-objc")]
13mod outline_objc;
14#[cfg(feature = "lang-python")]
15mod outline_python;
16#[cfg(feature = "lang-ruby")]
17mod outline_ruby;
18#[cfg(feature = "lang-swift")]
19mod outline_swift;
20#[cfg(feature = "lang-typescript")]
21mod outline_typescript;
22
23pub use anchor::AnchorMatch;
24pub use outline::{OutlineEntry, SemanticKind};
25
26use crate::error::AstError;
27
28/// Supported languages for AST parsing.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum Language {
31    Rust,
32    TypeScript,
33    Tsx,
34    JavaScript,
35    Jsx,
36    Python,
37    Go,
38    Java,
39    C,
40    Cpp,
41    Ruby,
42    ObjC,
43    Swift,
44    Unsupported,
45}
46
47impl Language {
48    /// Detect language from file extension.
49    pub fn from_extension(ext: &str) -> Self {
50        match ext {
51            "rs" => Language::Rust,
52            "ts" | "mts" | "cts" => Language::TypeScript,
53            "tsx" => Language::Tsx,
54            "js" | "mjs" | "cjs" => Language::JavaScript,
55            "jsx" => Language::Jsx,
56            "py" | "pyi" => Language::Python,
57            "go" => Language::Go,
58            "java" => Language::Java,
59            "c" | "h" => Language::C,
60            "cc" | "cpp" | "cxx" | "hpp" | "hxx" | "hh" => Language::Cpp,
61            "rb" | "rake" | "gemspec" => Language::Ruby,
62            "m" | "mm" => Language::ObjC,
63            "swift" => Language::Swift,
64            _ => Language::Unsupported,
65        }
66    }
67
68    /// Detect language from file path.
69    pub fn from_path(path: &str) -> Self {
70        path.rsplit('.')
71            .next()
72            .map(Self::from_extension)
73            .unwrap_or(Language::Unsupported)
74    }
75}
76
77/// Extract an outline of semantic units from source code.
78pub fn extract_outline(source: &str, language: Language) -> Result<Vec<OutlineEntry>, AstError> {
79    match language {
80        #[cfg(feature = "lang-rust")]
81        Language::Rust => outline::extract_rust_outline(source),
82
83        #[cfg(feature = "lang-typescript")]
84        Language::TypeScript | Language::JavaScript => {
85            outline_typescript::extract_typescript_outline(source, false)
86        }
87        #[cfg(feature = "lang-typescript")]
88        Language::Tsx | Language::Jsx => {
89            outline_typescript::extract_typescript_outline(source, true)
90        }
91
92        #[cfg(feature = "lang-python")]
93        Language::Python => outline_python::extract_python_outline(source),
94
95        #[cfg(feature = "lang-go")]
96        Language::Go => outline_go::extract_go_outline(source),
97
98        #[cfg(feature = "lang-java")]
99        Language::Java => outline_java::extract_java_outline(source),
100
101        #[cfg(feature = "lang-c")]
102        Language::C => outline_c::extract_c_outline(source),
103
104        #[cfg(feature = "lang-cpp")]
105        Language::Cpp => outline_cpp::extract_cpp_outline(source),
106
107        #[cfg(feature = "lang-ruby")]
108        Language::Ruby => outline_ruby::extract_ruby_outline(source),
109
110        #[cfg(feature = "lang-objc")]
111        Language::ObjC => outline_objc::extract_objc_outline(source),
112
113        #[cfg(feature = "lang-swift")]
114        Language::Swift => outline_swift::extract_swift_outline(source),
115
116        Language::Unsupported => Err(AstError::UnsupportedLanguage {
117            extension: "unknown".to_string(),
118            location: snafu::Location::new(file!(), line!(), 0),
119        }),
120
121        // Feature-disabled arms: language recognized but grammar not compiled in
122        #[allow(unreachable_patterns)]
123        _ => Err(AstError::UnsupportedLanguage {
124            extension: format!("{:?} (feature not enabled)", language),
125            location: snafu::Location::new(file!(), line!(), 0),
126        }),
127    }
128}
129
130/// Resolve an anchor name against an outline, returning match quality.
131pub fn resolve_anchor(
132    outline: &[OutlineEntry],
133    unit_type: &str,
134    name: &str,
135) -> Option<AnchorMatch> {
136    anchor::resolve(outline, unit_type, name)
137}