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 | ||||
|     $targetTokenizerFile = "avante_tokenizers.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_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" | ||||
| } | ||||
|  | ||||
							
								
								
									
										11
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -39,6 +39,7 @@ dependencies = [ | ||||
|  "serde", | ||||
|  "tree-sitter", | ||||
|  "tree-sitter-c", | ||||
|  "tree-sitter-c-sharp", | ||||
|  "tree-sitter-cpp", | ||||
|  "tree-sitter-elixir", | ||||
|  "tree-sitter-go", | ||||
| @ -1331,6 +1332,16 @@ dependencies = [ | ||||
|  "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]] | ||||
| name = "tree-sitter-cpp" | ||||
| version = "0.23.1" | ||||
|  | ||||
| @ -29,6 +29,7 @@ tree-sitter-ruby = "0.23" | ||||
| tree-sitter-zig = "1.0.2" | ||||
| tree-sitter-scala = "0.23" | ||||
| tree-sitter-elixir = "0.3.1" | ||||
| tree-sitter-c-sharp = "0.23" | ||||
| 
 | ||||
| [lints] | ||||
| 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), | ||||
|         "scala" => Some(tree_sitter_scala::LANGUAGE), | ||||
|         "elixir" => Some(tree_sitter_elixir::LANGUAGE), | ||||
|         "csharp" => Some(tree_sitter_c_sharp::LANGUAGE), | ||||
|         _ => 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 SCALA_QUERY: &str = include_str!("../queries/tree-sitter-scala-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> { | ||||
|     let ts_language = get_ts_language(language); | ||||
| @ -99,6 +101,7 @@ fn get_definitions_query(language: &str) -> Result<Query, String> { | ||||
|         "ruby" => RUBY_QUERY, | ||||
|         "scala" => SCALA_QUERY, | ||||
|         "elixir" => ELIXIR_QUERY, | ||||
|         "csharp" => CSHARP_QUERY, | ||||
|         _ => return Err(format!("Unsupported language: {language}")), | ||||
|     }; | ||||
|     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 | ||||
| } | ||||
| 
 | ||||
| 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>> { | ||||
|     let mut cursor = node.walk(); | ||||
|     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 | ||||
| } | ||||
| 
 | ||||
| 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> { | ||||
|     let mut 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()) | ||||
|                     .unwrap_or(node_text) | ||||
|                     .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 | ||||
|                     .child_by_field_name("name") | ||||
|                     .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) { | ||||
|                         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 | ||||
|                         .child_by_field_name("parameters") | ||||
|                         .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("()"); | ||||
|                     let mut return_type_node = match language { | ||||
|                         "cpp" => node.child_by_field_name("type"), | ||||
|                         "csharp" => node.child_by_field_name("returns"), | ||||
|                         _ => node.child_by_field_name("return_type"), | ||||
|                     }; | ||||
|                     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() { | ||||
|                         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()) | ||||
|                             .unwrap_or("") | ||||
|                             .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 { | ||||
|                         let impl_type_node = impl_item.child_by_field_name("type"); | ||||
|                         impl_type_node | ||||
| @ -645,6 +726,21 @@ fn extract_definitions(language: &str, source: &str) -> Result<Vec<Definition>, | ||||
|                             .unwrap_or("") | ||||
|                             .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" { | ||||
|                         class_name = | ||||
|                             zig_find_parent_variable_declaration_name(&node, source.as_bytes()) | ||||
| @ -1507,6 +1603,60 @@ mod tests { | ||||
|         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] | ||||
|     fn test_unsupported_language() { | ||||
|         let source = "print('Hello, world!')"; | ||||
|  | ||||
| @ -6,6 +6,7 @@ local Config = require("avante.config") | ||||
| local filetype_map = { | ||||
|   ["javascriptreact"] = "javascript", | ||||
|   ["typescriptreact"] = "typescript", | ||||
|   ["cs"] = "csharp", | ||||
| } | ||||
| 
 | ||||
| ---@class AvanteRepoMap | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Kostiantyn Sharovarskyi
						Kostiantyn Sharovarskyi