diff --git a/build.rs b/build.rs index 45562709b..6ee33eab7 100644 --- a/build.rs +++ b/build.rs @@ -316,6 +316,11 @@ fn main() { src_dir: "vendored_parsers/tree-sitter-sql-src", extra_files: vec!["scanner.cc"], }, + TreeSitterParser { + name: "tree-sitter-sfapex", + src_dir: "vendored_parsers/tree-sitter-sfapex-src", + extra_files: vec![], + }, TreeSitterParser { name: "tree-sitter-swift", src_dir: "vendored_parsers/tree-sitter-swift-src", diff --git a/manual/src/languages_supported.md b/manual/src/languages_supported.md index f2d07cfd2..ef9c88a37 100644 --- a/manual/src/languages_supported.md +++ b/manual/src/languages_supported.md @@ -9,6 +9,7 @@ with `difft --list-languages`. | Language | Parser Used | |-----------------|---------------------------------------------------------------------------------------------| | Ada | [briot/tree-sitter-ada](https://github.com/briot/tree-sitter-ada) | +| Apex | [aheber/tree-sitter-sfapex](https://github.com/aheber/tree-sitter-sfapex) | | Bash | [tree-sitter/tree-sitter-bash](https://github.com/tree-sitter/tree-sitter-bash) | | C | [tree-sitter/tree-sitter-c](https://github.com/tree-sitter/tree-sitter-c) | | C++ | [tree-sitter/tree-sitter-cpp](https://github.com/tree-sitter/tree-sitter-cpp) | diff --git a/sample_files/apex_after.cls b/sample_files/apex_after.cls new file mode 100644 index 000000000..9e85867d5 --- /dev/null +++ b/sample_files/apex_after.cls @@ -0,0 +1,10 @@ +/** + * @description diffstatic test file + */ +public with sharing class MyClass extends OtherClass implements MyInterface { + private static final Integer A_CONSTANT = 0; + + public void doSomething(Object param1, Object param2) { + System.debug('Hello world!'); + } +} diff --git a/sample_files/apex_before.cls b/sample_files/apex_before.cls new file mode 100644 index 000000000..afb5dae92 --- /dev/null +++ b/sample_files/apex_before.cls @@ -0,0 +1,14 @@ +/** + * @description diffstatic test file + */ +public with sharing class MyClass extends OtherClass + implements MyInterface { + + private static final Integer A_CONSTANT = 0; + + public void doSomething(Object param1, + Object param2) { + + System.debug('Hello world!'); + } +} diff --git a/sample_files/compare.expected b/sample_files/compare.expected index 8ea086494..e77e2c7ab 100644 --- a/sample_files/compare.expected +++ b/sample_files/compare.expected @@ -7,6 +7,9 @@ sample_files/ada_before.adb sample_files/ada_after.adb sample_files/added_line_before.txt sample_files/added_line_after.txt 5996d2f9cc7c1e3acfdeafcf0f5e43c1 - +sample_files/apex_before.cls sample_files/apex_after.cls +fde5c2cb739fb8bb42a592f64190137e - + sample_files/b2_math_before.h sample_files/b2_math_after.h e1391702d8059d127110017bf6e9ef85 - diff --git a/src/parse/guess_language.rs b/src/parse/guess_language.rs index 470dc1dad..cb003774c 100644 --- a/src/parse/guess_language.rs +++ b/src/parse/guess_language.rs @@ -19,6 +19,7 @@ use strum::{EnumIter, IntoEnumIterator}; #[derive(Debug, Clone, Copy, PartialEq, Eq, EnumIter)] pub enum Language { Ada, + Apex, Bash, C, Clojure, @@ -104,6 +105,7 @@ pub fn language_override_from_name(name: &str) -> Option { pub fn language_name(language: Language) -> &'static str { match language { Ada => "Ada", + Apex => "Apex", Bash => "Bash", C => "C", Clojure => "Clojure", @@ -215,6 +217,7 @@ pub fn language_globs(language: Language) -> Vec { "zshenv", "zshrc", ], + Apex => &["*.cls", "*.apexc", "*.trigger"], C => &["*.c"], Clojure => &[ "*.bb", "*.boot", "*.clj", "*.cljc", "*.clje", "*.cljs", "*.cljx", "*.edn", "*.joke", diff --git a/src/parse/tree_sitter_parser.rs b/src/parse/tree_sitter_parser.rs index 699abb798..f37893db0 100644 --- a/src/parse/tree_sitter_parser.rs +++ b/src/parse/tree_sitter_parser.rs @@ -61,6 +61,7 @@ pub struct TreeSitterConfig { extern "C" { fn tree_sitter_ada() -> ts::Language; + fn tree_sitter_apex() -> ts::Language; fn tree_sitter_bash() -> ts::Language; fn tree_sitter_c() -> ts::Language; fn tree_sitter_c_sharp() -> ts::Language; @@ -145,6 +146,30 @@ pub fn from_language(language: guess::Language) -> TreeSitterConfig { sub_languages: vec![], } } + Apex => { + let language = unsafe { tree_sitter_apex() }; + TreeSitterConfig { + language, + atom_nodes: vec![ + "string_literal", + "null_literal", + "boolean", + "int", + "decimal_floating_point_literal", + "date_literal", + "currency_literal", + ] + .into_iter() + .collect(), + delimiter_tokens: vec![("[", "]"), ("(", ")"), ("{", "}")], + highlight_query: ts::Query::new( + language, + include_str!("../../vendored_parsers/highlights/apex.scm"), + ) + .unwrap(), + sub_languages: vec![], + } + } Bash => { let language = unsafe { tree_sitter_bash() }; TreeSitterConfig { diff --git a/translation/zh-CN/manual-zh-CN/src/languages_supported.md b/translation/zh-CN/manual-zh-CN/src/languages_supported.md index be7eb2fa2..4edef2dcd 100644 --- a/translation/zh-CN/manual-zh-CN/src/languages_supported.md +++ b/translation/zh-CN/manual-zh-CN/src/languages_supported.md @@ -7,6 +7,7 @@ | 语言 | 使用的解析器 | |-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| | Bash | [tree-sitter/tree-sitter-bash](https://github.com/tree-sitter/tree-sitter-bash) | +| Apex | [aheber/tree-sitter-sfapex](https://github.com/aheber/tree-sitter-sfapex) | | C | [tree-sitter/tree-sitter-c](https://github.com/tree-sitter/tree-sitter-c) | | C++ | [tree-sitter/tree-sitter-cpp](https://github.com/tree-sitter/tree-sitter-cpp) | | C# | [tree-sitter/tree-sitter-c-sharp](https://github.com/tree-sitter/tree-sitter-c-sharp) | diff --git a/vendored_parsers/highlights/apex.scm b/vendored_parsers/highlights/apex.scm new file mode 100644 index 000000000..4049ab3ed --- /dev/null +++ b/vendored_parsers/highlights/apex.scm @@ -0,0 +1,302 @@ +;; attempting to match concepts represented here: +;; https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide + +[ + "[" + "]" + "{" + "}" + "?" + ";" +] @punctuation + +;; Methods + +(method_declaration + name: (identifier) @method) +(method_declaration + type: (type_identifier) @type) + +(method_invocation + name: (identifier) @method) +(argument_list + (identifier) @variable) +(super) @function.defaultLibrary + +(explicit_constructor_invocation + arguments: (argument_list + (identifier) @variable )) + +;; Annotations + +(annotation + name: (identifier) @decorator) + +"@" @operator + +(annotation_key_value + (identifier) @variable) + + +;; Types + +(interface_declaration + name: (identifier) @interface) +(class_declaration + name: (identifier) @class) +(class_declaration + (superclass) @class) +(enum_declaration + name: (identifier) @enum) +(enum_constant + name: (identifier) @enumMember) + +(interfaces + (type_list + (type_identifier) @interface )) + +(local_variable_declaration + (type_identifier) @type ) + +( expression_statement (_ (identifier)) @variable) + +(type_arguments "<" @punctuation) +(type_arguments ">" @punctuation) + +; (identifier) @variable + +((field_access + object: (identifier) @type)) ;; don't know what type of thing it is + +(generic_type + (type_identifier) @type) +(type_arguments (type_identifier) @type) + +(field_access + field: (identifier) @property) + +((scoped_identifier + scope: (identifier) @type) + (#match? @type "^[A-Z]")) +((method_invocation + object: (identifier) @type) + (#match? @type "^[A-Z]")) + + +(field_declaration + type: (type_identifier) @type) + +(method_declaration + (formal_parameters + (formal_parameter + name: (identifier) @parameter))) + +(formal_parameter + type: (type_identifier) @type + (identifier) @variable) + +(enhanced_for_statement + type: (type_identifier) @type + name: (identifier) @variable ) + +(enhanced_for_statement + value: (identifier) @variable) + +(enhanced_for_statement + name: (identifier) @variable) + +(object_creation_expression + type: (type_identifier) @type) + +(array_creation_expression + type: (type_identifier) @type) + +(array_type + element: (type_identifier) @type) + +(catch_formal_parameter + (type_identifier) @type + name: (identifier) @variable) + +(return_statement + (identifier) @variable) + +(local_variable_declaration + (variable_declarator + name: (identifier) @variable )) + +(for_statement + condition: (binary_expression + (identifier) @variable)) + +(for_statement + update: (update_expression + (identifier) @variable)) + +(constructor_declaration + name: (identifier) @class) + +(dml_type) @function.defaultLibrary + +(bound_apex_expression + (identifier) @variable) + +(assignment_operator) @operator + +(update_expression ["++" "--"] @operator) + +(instanceof_expression + left: (identifier) @variable + right: (type_identifier) @type ) + +(cast_expression + type: (type_identifier) @type + value: (identifier) @variable) + +(switch_expression + condition: (identifier) @variable) + +(switch_label + (type_identifier) @type + (identifier) @variable ) + +(switch_rule + (switch_label + (identifier) @enumMember )) + +(trigger_declaration + name: (identifier) @type + object: (identifier) @type + (trigger_event) @keyword + ("," (trigger_event) @keyword)*) + +(binary_expression + operator: [ + ">" + "<" + ">=" + "<=" + "==" + "===" + "!=" + "!==" + "&&" + "||" + "+" + "-" + "*" + "/" + "&" + "|" + "^" + "%" + "<<" + ">>" + ">>>"] @operator) + +(binary_expression + (identifier) @variable) + +(unary_expression + operator: [ + "+" + "-" + "!" + "~" + ]) @operator + +(map_initializer "=>" @operator) + +[ + (boolean_type) + (void_type) +] @type.defaultLibrary + +; Variables + +(field_declaration + (modifiers (modifier ["final" "static"])(modifier ["final" "static"])) + (variable_declarator + name: (identifier) @variable.readonly)) + +(variable_declarator + (identifier) @property) + +;; because itendifying it when declared doesn't carry to use +;; leans on the convention that "screaming snake case" is a const +((identifier) @variable.readonly + (#match? @variable.readonly "^_*[A-Z][A-Z\\d_]+$")) + + +(this) @variable.defaultLibrary + +; Literals + +[ + (int) +] @number + +[ + (string_literal) +] @string + +[ + (line_comment) + (block_comment) +] @comment + +;; ;; Keywords + +[ + "abstract" + "break" + "catch" + "class" + "continue" + "default" + "do" + "else" + "enum" + "extends" + "final" + "finally" + "for" + "get" + "global" + "if" + "implements" + "instanceof" + "interface" + "new" + "on" + "private" + "protected" + "public" + "return" + "set" + "static" + "switch" + "testMethod" + "throw" + "transient" + "try" + "trigger" + "virtual" + "when" + "while" + "with_sharing" + "without_sharing" + "inherited_sharing" +] @keyword + +(assignment_expression + left: (identifier) @variable) + +; (type_identifier) @type ;; not respecting precedence... +;; I don't love this but couldn't break them up right now +;; can't figure out how to let that be special without conflicting +;; in the grammar +"System.runAs" @method.defaultLibrary + +(scoped_type_identifier + (type_identifier) @type) \ No newline at end of file diff --git a/vendored_parsers/tree-sitter-sfapex-src b/vendored_parsers/tree-sitter-sfapex-src new file mode 120000 index 000000000..8542f2433 --- /dev/null +++ b/vendored_parsers/tree-sitter-sfapex-src @@ -0,0 +1 @@ +tree-sitter-sfapex/apex/src \ No newline at end of file