Skip to main content

dk_engine/parser/langs/
csharp.rs

1//! C# language configuration for the query-driven parser.
2
3use crate::parser::lang_config::{CommentStyle, LanguageConfig};
4use dk_core::{Symbol, Visibility};
5use tree_sitter::Language;
6
7/// C# language configuration for [`QueryDrivenParser`](crate::parser::engine::QueryDrivenParser).
8pub struct CSharpConfig;
9
10impl LanguageConfig for CSharpConfig {
11    fn language(&self) -> Language {
12        tree_sitter_c_sharp::LANGUAGE.into()
13    }
14
15    fn extensions(&self) -> &'static [&'static str] {
16        &["cs"]
17    }
18
19    fn symbols_query(&self) -> &'static str {
20        include_str!("../queries/csharp_symbols.scm")
21    }
22
23    fn calls_query(&self) -> &'static str {
24        include_str!("../queries/csharp_calls.scm")
25    }
26
27    fn imports_query(&self) -> &'static str {
28        include_str!("../queries/csharp_imports.scm")
29    }
30
31    fn comment_style(&self) -> CommentStyle {
32        CommentStyle::SlashSlash
33    }
34
35    fn resolve_visibility(&self, modifiers: Option<&str>, _name: &str) -> Visibility {
36        match modifiers {
37            Some(m) if m.contains("public") => Visibility::Public,
38            Some(m) if m.contains("protected") => Visibility::Public,
39            Some(m) if m.contains("internal") => Visibility::Public,
40            // private or no modifier → Private
41            _ => Visibility::Private,
42        }
43    }
44
45    fn adjust_symbol(&self, sym: &mut Symbol, node: &tree_sitter::Node, source: &[u8]) {
46        // C# uses `repeat($.modifier)` — each keyword is a separate
47        // `modifier` node. Walk the declaration's children to collect all
48        // modifier texts and resolve visibility from the combined set.
49        let mut cursor = node.walk();
50        for child in node.children(&mut cursor) {
51            if child.kind() == "modifier" {
52                let text = &source[child.start_byte()..child.end_byte()];
53                let modifier = std::str::from_utf8(text).unwrap_or("");
54                match modifier {
55                    "public" | "protected" | "internal" => {
56                        sym.visibility = Visibility::Public;
57                        return;
58                    }
59                    "private" => {
60                        sym.visibility = Visibility::Private;
61                        return;
62                    }
63                    _ => {} // static, abstract, sealed, etc. — skip
64                }
65            }
66        }
67        // No visibility modifier found → default is private in C#
68        sym.visibility = Visibility::Private;
69    }
70
71    fn is_external_import(&self, _module_path: &str) -> bool {
72        // C# imports are namespace-based. Without project context we can't
73        // distinguish internal vs external, so treat all as external.
74        true
75    }
76}