dk_engine/parser/lang_config.rs
1//! Per-language configuration for the query-driven parser engine.
2//!
3//! Each supported language implements [`LanguageConfig`] to provide its
4//! tree-sitter grammar, S-expression queries, comment style, and any
5//! language-specific fixups.
6
7use dk_core::{Symbol, SymbolKind, Visibility};
8
9// ── Comment style ──
10
11/// How doc-comments are written in a given language.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum CommentStyle {
14 /// Rust-style `///` doc comments.
15 TripleSlash,
16 /// Python / Ruby / shell `#` comments.
17 Hash,
18 /// Go / Java / TypeScript / C `//` comments.
19 SlashSlash,
20}
21
22// ── Language configuration trait ──
23
24/// Configuration a language must supply so the generic
25/// [`QueryDrivenParser`](super) can extract symbols, calls, and imports.
26pub trait LanguageConfig: Send + Sync {
27 /// The tree-sitter [`Language`] grammar.
28 fn language(&self) -> tree_sitter::Language;
29
30 /// File extensions this language handles (without leading dot).
31 fn extensions(&self) -> &'static [&'static str];
32
33 /// Tree-sitter S-expression query that captures symbols.
34 ///
35 /// Capture names must end with a kind suffix that
36 /// [`default_kind_mapping`] (or an override of
37 /// [`map_capture_to_kind`](Self::map_capture_to_kind)) understands,
38 /// e.g. `@definition.function`, `@definition.class`.
39 fn symbols_query(&self) -> &'static str;
40
41 /// Tree-sitter S-expression query that captures call-sites.
42 fn calls_query(&self) -> &'static str;
43
44 /// Tree-sitter S-expression query that captures import statements.
45 fn imports_query(&self) -> &'static str;
46
47 /// The comment style used for doc-comments.
48 fn comment_style(&self) -> CommentStyle;
49
50 /// Resolve the visibility of a symbol from its modifier keywords and name.
51 fn resolve_visibility(&self, modifiers: Option<&str>, name: &str) -> Visibility;
52
53 // ── Default implementations ──
54
55 /// Map a tree-sitter capture suffix (e.g. `"function"`) to a
56 /// [`SymbolKind`]. The default delegates to [`default_kind_mapping`].
57 fn map_capture_to_kind(&self, capture_suffix: &str) -> Option<SymbolKind> {
58 default_kind_mapping(capture_suffix)
59 }
60
61 /// Post-process hook that can mutate a [`Symbol`] after extraction.
62 ///
63 /// Override this when the generic engine cannot capture all details
64 /// through queries alone (e.g. Rust `pub(crate)` modifiers).
65 #[allow(unused_variables)]
66 fn adjust_symbol(&self, sym: &mut Symbol, node: &tree_sitter::Node, source: &[u8]) {
67 // no-op by default
68 }
69
70 /// Return `true` if `module_path` refers to an external dependency.
71 ///
72 /// Paths starting with `.`, `crate`, `self`, or `super` are considered
73 /// internal by default.
74 fn is_external_import(&self, module_path: &str) -> bool {
75 !module_path.starts_with('.')
76 && !module_path.starts_with("crate")
77 && !module_path.starts_with("self")
78 && !module_path.starts_with("super")
79 }
80}
81
82// ── Shared helpers ──
83
84/// Map a capture-name suffix to a [`SymbolKind`].
85///
86/// This is the default implementation used by
87/// [`LanguageConfig::map_capture_to_kind`]. Language configs can call it
88/// directly or override the method entirely.
89pub fn default_kind_mapping(capture_suffix: &str) -> Option<SymbolKind> {
90 match capture_suffix {
91 "function" | "method" => Some(SymbolKind::Function),
92 "class" => Some(SymbolKind::Class),
93 "struct" => Some(SymbolKind::Struct),
94 "enum" => Some(SymbolKind::Enum),
95 "trait" => Some(SymbolKind::Trait),
96 "impl" => Some(SymbolKind::Impl),
97 "interface" => Some(SymbolKind::Interface),
98 "type_alias" | "type" => Some(SymbolKind::TypeAlias),
99 "const" | "expression" => Some(SymbolKind::Const),
100 "static" => Some(SymbolKind::Static),
101 "module" => Some(SymbolKind::Module),
102 "variable" => Some(SymbolKind::Variable),
103 _ => None,
104 }
105}