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