feat: Add csharp support to repo_map (#1073)
This commit is contained in:
parent
2d5306b55c
commit
143519abae
@ -20,8 +20,10 @@ function Build-FromSource($feature) {
|
|||||||
$SCRIPT_DIR = $PSScriptRoot
|
$SCRIPT_DIR = $PSScriptRoot
|
||||||
$targetTokenizerFile = "avante_tokenizers.dll"
|
$targetTokenizerFile = "avante_tokenizers.dll"
|
||||||
$targetTemplatesFile = "avante_templates.dll"
|
$targetTemplatesFile = "avante_templates.dll"
|
||||||
|
$targetRepoMapFile = "avante_repo_map.dll"
|
||||||
Copy-Item (Join-Path $SCRIPT_DIR "target\release\avante_tokenizers.dll") (Join-Path $BuildDir $targetTokenizerFile)
|
Copy-Item (Join-Path $SCRIPT_DIR "target\release\avante_tokenizers.dll") (Join-Path $BuildDir $targetTokenizerFile)
|
||||||
Copy-Item (Join-Path $SCRIPT_DIR "target\release\avante_templates.dll") (Join-Path $BuildDir $targetTemplatesFile)
|
Copy-Item (Join-Path $SCRIPT_DIR "target\release\avante_templates.dll") (Join-Path $BuildDir $targetTemplatesFile)
|
||||||
|
Copy-Item (Join-Path $SCRIPT_DIR "target\release\avante_repo_map.dll") (Join-Path $BuildDir $targetRepoMapFile)
|
||||||
|
|
||||||
Remove-Item -Recurse -Force "target"
|
Remove-Item -Recurse -Force "target"
|
||||||
}
|
}
|
||||||
|
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -39,6 +39,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
"tree-sitter-c",
|
"tree-sitter-c",
|
||||||
|
"tree-sitter-c-sharp",
|
||||||
"tree-sitter-cpp",
|
"tree-sitter-cpp",
|
||||||
"tree-sitter-elixir",
|
"tree-sitter-elixir",
|
||||||
"tree-sitter-go",
|
"tree-sitter-go",
|
||||||
@ -1331,6 +1332,16 @@ dependencies = [
|
|||||||
"tree-sitter-language",
|
"tree-sitter-language",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-c-sharp"
|
||||||
|
version = "0.23.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67f06accca7b45351758663b8215089e643d53bd9a660ce0349314263737fcb0"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"tree-sitter-language",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tree-sitter-cpp"
|
name = "tree-sitter-cpp"
|
||||||
version = "0.23.1"
|
version = "0.23.1"
|
||||||
|
@ -29,6 +29,7 @@ tree-sitter-ruby = "0.23"
|
|||||||
tree-sitter-zig = "1.0.2"
|
tree-sitter-zig = "1.0.2"
|
||||||
tree-sitter-scala = "0.23"
|
tree-sitter-scala = "0.23"
|
||||||
tree-sitter-elixir = "0.3.1"
|
tree-sitter-elixir = "0.3.1"
|
||||||
|
tree-sitter-c-sharp = "0.23"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
25
crates/avante-repo-map/queries/tree-sitter-c-sharp-defs.scm
Normal file
25
crates/avante-repo-map/queries/tree-sitter-c-sharp-defs.scm
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
(class_declaration
|
||||||
|
name: (identifier) @class
|
||||||
|
(parameter_list)? @method) ;; Primary constructor
|
||||||
|
|
||||||
|
(record_declaration
|
||||||
|
name: (identifier) @class
|
||||||
|
(parameter_list)? @method) ;; Primary constructor
|
||||||
|
|
||||||
|
(interface_declaration
|
||||||
|
name: (identifier) @class)
|
||||||
|
|
||||||
|
(method_declaration) @method
|
||||||
|
|
||||||
|
(constructor_declaration) @method
|
||||||
|
|
||||||
|
(property_declaration) @class_variable
|
||||||
|
|
||||||
|
(field_declaration
|
||||||
|
(variable_declaration
|
||||||
|
(variable_declarator))) @class_variable
|
||||||
|
|
||||||
|
(enum_declaration
|
||||||
|
body: (enum_member_declaration_list
|
||||||
|
(enum_member_declaration) @enum_item))
|
||||||
|
|
@ -63,6 +63,7 @@ fn get_ts_language(language: &str) -> Option<LanguageFn> {
|
|||||||
"zig" => Some(tree_sitter_zig::LANGUAGE),
|
"zig" => Some(tree_sitter_zig::LANGUAGE),
|
||||||
"scala" => Some(tree_sitter_scala::LANGUAGE),
|
"scala" => Some(tree_sitter_scala::LANGUAGE),
|
||||||
"elixir" => Some(tree_sitter_elixir::LANGUAGE),
|
"elixir" => Some(tree_sitter_elixir::LANGUAGE),
|
||||||
|
"csharp" => Some(tree_sitter_c_sharp::LANGUAGE),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,6 +80,7 @@ const TYPESCRIPT_QUERY: &str = include_str!("../queries/tree-sitter-typescript-d
|
|||||||
const RUBY_QUERY: &str = include_str!("../queries/tree-sitter-ruby-defs.scm");
|
const RUBY_QUERY: &str = include_str!("../queries/tree-sitter-ruby-defs.scm");
|
||||||
const SCALA_QUERY: &str = include_str!("../queries/tree-sitter-scala-defs.scm");
|
const SCALA_QUERY: &str = include_str!("../queries/tree-sitter-scala-defs.scm");
|
||||||
const ELIXIR_QUERY: &str = include_str!("../queries/tree-sitter-elixir-defs.scm");
|
const ELIXIR_QUERY: &str = include_str!("../queries/tree-sitter-elixir-defs.scm");
|
||||||
|
const CSHARP_QUERY: &str = include_str!("../queries/tree-sitter-c-sharp-defs.scm");
|
||||||
|
|
||||||
fn get_definitions_query(language: &str) -> Result<Query, String> {
|
fn get_definitions_query(language: &str) -> Result<Query, String> {
|
||||||
let ts_language = get_ts_language(language);
|
let ts_language = get_ts_language(language);
|
||||||
@ -99,6 +101,7 @@ fn get_definitions_query(language: &str) -> Result<Query, String> {
|
|||||||
"ruby" => RUBY_QUERY,
|
"ruby" => RUBY_QUERY,
|
||||||
"scala" => SCALA_QUERY,
|
"scala" => SCALA_QUERY,
|
||||||
"elixir" => ELIXIR_QUERY,
|
"elixir" => ELIXIR_QUERY,
|
||||||
|
"csharp" => CSHARP_QUERY,
|
||||||
_ => return Err(format!("Unsupported language: {language}")),
|
_ => return Err(format!("Unsupported language: {language}")),
|
||||||
};
|
};
|
||||||
let query = Query::new(&ts_language.into(), contents)
|
let query = Query::new(&ts_language.into(), contents)
|
||||||
@ -129,6 +132,20 @@ fn find_ancestor_by_type<'a>(node: &'a Node, parent_type: &str) -> Option<Node<'
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_first_ancestor_by_types<'a>(
|
||||||
|
node: &'a Node,
|
||||||
|
possible_parent_types: &[&str],
|
||||||
|
) -> Option<Node<'a>> {
|
||||||
|
let mut parent = node.parent();
|
||||||
|
while let Some(parent_node) = parent {
|
||||||
|
if possible_parent_types.contains(&parent_node.kind()) {
|
||||||
|
return Some(parent_node);
|
||||||
|
}
|
||||||
|
parent = parent_node.parent();
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn find_descendant_by_type<'a>(node: &'a Node, child_type: &str) -> Option<Node<'a>> {
|
fn find_descendant_by_type<'a>(node: &'a Node, child_type: &str) -> Option<Node<'a>> {
|
||||||
let mut cursor = node.walk();
|
let mut cursor = node.walk();
|
||||||
for i in 0..node.descendant_count() {
|
for i in 0..node.descendant_count() {
|
||||||
@ -189,6 +206,17 @@ fn zig_find_type_in_parent<'a>(node: &'a Node, source: &'a [u8]) -> Option<Strin
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn csharp_is_primary_constructor(node: &Node) -> bool {
|
||||||
|
node.kind() == "parameter_list"
|
||||||
|
&& node.parent().map_or(false, |n| {
|
||||||
|
n.kind() == "class_declaration" || n.kind() == "record_declaration"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn csharp_find_parent_type_node<'a>(node: &'a Node) -> Option<Node<'a>> {
|
||||||
|
find_first_ancestor_by_types(node, &["class_declaration", "record_declaration"])
|
||||||
|
}
|
||||||
|
|
||||||
fn ex_find_parent_module_declaration_name<'a>(node: &'a Node, source: &'a [u8]) -> Option<String> {
|
fn ex_find_parent_module_declaration_name<'a>(node: &'a Node, source: &'a [u8]) -> Option<String> {
|
||||||
let mut parent = node.parent();
|
let mut parent = node.parent();
|
||||||
while let Some(parent_node) = parent {
|
while let Some(parent_node) = parent {
|
||||||
@ -351,6 +379,22 @@ fn extract_definitions(language: &str, source: &str) -> Result<Vec<Definition>,
|
|||||||
.map(|n| n.utf8_text(source.as_bytes()).unwrap())
|
.map(|n| n.utf8_text(source.as_bytes()).unwrap())
|
||||||
.unwrap_or(node_text)
|
.unwrap_or(node_text)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
"csharp" => {
|
||||||
|
let mut identifier = node;
|
||||||
|
// Handle primary constructors (they are direct children of *_declaration)
|
||||||
|
if *capture_name == "method" && csharp_is_primary_constructor(&node) {
|
||||||
|
identifier = node.parent().unwrap_or(node);
|
||||||
|
} else if *capture_name == "class_variable" {
|
||||||
|
identifier =
|
||||||
|
find_descendant_by_type(&node, "variable_declarator").unwrap_or(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
identifier
|
||||||
|
.child_by_field_name("name")
|
||||||
|
.map(|n| n.utf8_text(source.as_bytes()).unwrap())
|
||||||
|
.unwrap_or(node_text)
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
_ => node
|
_ => node
|
||||||
.child_by_field_name("name")
|
.child_by_field_name("name")
|
||||||
.map(|n| n.utf8_text(source.as_bytes()).unwrap())
|
.map(|n| n.utf8_text(source.as_bytes()).unwrap())
|
||||||
@ -472,6 +516,23 @@ fn extract_definitions(language: &str, source: &str) -> Result<Vec<Definition>,
|
|||||||
if !name.is_empty() && language == "go" && !is_first_letter_uppercase(&name) {
|
if !name.is_empty() && language == "go" && !is_first_letter_uppercase(&name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if language == "csharp" {
|
||||||
|
let csharp_visibility = find_descendant_by_type(&node, "modifier");
|
||||||
|
if csharp_visibility.is_none() && !csharp_is_primary_constructor(&node) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if csharp_visibility.is_some() {
|
||||||
|
let csharp_visibility_text = csharp_visibility
|
||||||
|
.unwrap()
|
||||||
|
.utf8_text(source.as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
if csharp_visibility_text == "private" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut params_node = node
|
let mut params_node = node
|
||||||
.child_by_field_name("parameters")
|
.child_by_field_name("parameters")
|
||||||
.or_else(|| find_descendant_by_type(&node, "parameter_list"));
|
.or_else(|| find_descendant_by_type(&node, "parameter_list"));
|
||||||
@ -494,6 +555,7 @@ fn extract_definitions(language: &str, source: &str) -> Result<Vec<Definition>,
|
|||||||
.unwrap_or("()");
|
.unwrap_or("()");
|
||||||
let mut return_type_node = match language {
|
let mut return_type_node = match language {
|
||||||
"cpp" => node.child_by_field_name("type"),
|
"cpp" => node.child_by_field_name("type"),
|
||||||
|
"csharp" => node.child_by_field_name("returns"),
|
||||||
_ => node.child_by_field_name("return_type"),
|
_ => node.child_by_field_name("return_type"),
|
||||||
};
|
};
|
||||||
if language == "cpp" {
|
if language == "cpp" {
|
||||||
@ -509,6 +571,19 @@ fn extract_definitions(language: &str, source: &str) -> Result<Vec<Definition>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if language == "csharp" {
|
||||||
|
let type_specifier_node = csharp_find_parent_type_node(&node);
|
||||||
|
let type_identifier_node =
|
||||||
|
type_specifier_node.and_then(|n| n.child_by_field_name("name"));
|
||||||
|
|
||||||
|
if let Some(type_identifier_node) = type_identifier_node {
|
||||||
|
let type_identifier_text =
|
||||||
|
type_identifier_node.utf8_text(source.as_bytes()).unwrap();
|
||||||
|
if name == type_identifier_text {
|
||||||
|
return_type_node = Some(type_identifier_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if return_type_node.is_none() {
|
if return_type_node.is_none() {
|
||||||
return_type_node = node.child_by_field_name("result");
|
return_type_node = node.child_by_field_name("result");
|
||||||
}
|
}
|
||||||
@ -542,6 +617,12 @@ fn extract_definitions(language: &str, source: &str) -> Result<Vec<Definition>,
|
|||||||
.and_then(|n| n.utf8_text(source.as_bytes()).ok())
|
.and_then(|n| n.utf8_text(source.as_bytes()).ok())
|
||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
.to_string()
|
.to_string()
|
||||||
|
} else if language == "csharp" {
|
||||||
|
csharp_find_parent_type_node(&node)
|
||||||
|
.and_then(|n| n.child_by_field_name("name"))
|
||||||
|
.and_then(|n| n.utf8_text(source.as_bytes()).ok())
|
||||||
|
.unwrap_or("")
|
||||||
|
.to_string()
|
||||||
} else if let Some(impl_item) = impl_item_node {
|
} else if let Some(impl_item) = impl_item_node {
|
||||||
let impl_type_node = impl_item.child_by_field_name("type");
|
let impl_type_node = impl_item.child_by_field_name("type");
|
||||||
impl_type_node
|
impl_type_node
|
||||||
@ -645,6 +726,21 @@ fn extract_definitions(language: &str, source: &str) -> Result<Vec<Definition>,
|
|||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
.to_string();
|
.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if language == "csharp" {
|
||||||
|
let csharp_visibility = find_descendant_by_type(&node, "modifier");
|
||||||
|
if csharp_visibility.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let csharp_visibility_text = csharp_visibility
|
||||||
|
.unwrap()
|
||||||
|
.utf8_text(source.as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
if csharp_visibility_text == "private" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if language == "zig" {
|
if language == "zig" {
|
||||||
class_name =
|
class_name =
|
||||||
zig_find_parent_variable_declaration_name(&node, source.as_bytes())
|
zig_find_parent_variable_declaration_name(&node, source.as_bytes())
|
||||||
@ -1507,6 +1603,60 @@ mod tests {
|
|||||||
assert_eq!(stringified, expected);
|
assert_eq!(stringified, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_csharp() {
|
||||||
|
let source = r#"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TestNamespace;
|
||||||
|
|
||||||
|
public class TestClass(TestDependency m)
|
||||||
|
{
|
||||||
|
|
||||||
|
private int PrivateTestProperty { get; set; }
|
||||||
|
|
||||||
|
private int _privateTestField;
|
||||||
|
|
||||||
|
public int TestProperty { get; set; }
|
||||||
|
|
||||||
|
public string TestField;
|
||||||
|
|
||||||
|
public TestClass()
|
||||||
|
{
|
||||||
|
TestProperty = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void TestMethod(int a, int b)
|
||||||
|
{
|
||||||
|
var innerVarInMethod = 1;
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TestMethod(int a, int b, int c) => a + b + c;
|
||||||
|
|
||||||
|
private void PrivateMethod()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyInnerClass(InnerClassDependency m) {}
|
||||||
|
|
||||||
|
public record MyInnerRecord(int a);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record TestRecord(int a, int b);
|
||||||
|
|
||||||
|
public enum TestEnum { Value1, Value2 }
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let definitions = extract_definitions("csharp", source).unwrap();
|
||||||
|
let stringified = stringify_definitions(&definitions);
|
||||||
|
println!("{stringified}");
|
||||||
|
let expected = "class MyInnerClass{func MyInnerClass(InnerClassDependency m) -> MyInnerClass;};class MyInnerRecord{func MyInnerRecord(int a) -> MyInnerRecord;};class TestClass{func TestClass(TestDependency m) -> TestClass;func TestClass() -> TestClass;func TestMethod(int a, int b) -> void;func TestMethod(int a, int b, int c) -> int;var TestProperty:int;var TestField:string;};class TestRecord{func TestRecord(int a, int b) -> TestRecord;};enum TestEnum{Value1;Value2;};";
|
||||||
|
assert_eq!(stringified, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unsupported_language() {
|
fn test_unsupported_language() {
|
||||||
let source = "print('Hello, world!')";
|
let source = "print('Hello, world!')";
|
||||||
|
@ -6,6 +6,7 @@ local Config = require("avante.config")
|
|||||||
local filetype_map = {
|
local filetype_map = {
|
||||||
["javascriptreact"] = "javascript",
|
["javascriptreact"] = "javascript",
|
||||||
["typescriptreact"] = "typescript",
|
["typescriptreact"] = "typescript",
|
||||||
|
["cs"] = "csharp",
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class AvanteRepoMap
|
---@class AvanteRepoMap
|
||||||
|
Loading…
x
Reference in New Issue
Block a user