diff --git a/Cargo.toml b/Cargo.toml index f036f25..57d4f6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,4 @@ path = "src/yls.rs" crate-type = ["cdylib"] [dependencies] -zed_extension_api = "0.3.0" +zed_extension_api = "0.7.0" diff --git a/README.md b/README.md index f686002..12dca64 100644 --- a/README.md +++ b/README.md @@ -4,5 +4,12 @@ Yara language extension for Zed. Example (using the `Ayu Mirage` theme): ![Yara syntax highlighting in Zed](example.png) -Before installing this extension, install the [Yara Language -Server](https://avast.github.io/yls/) and ensure that it is on your PATH. +Before installing this extension, install a YARA Language Server and ensure that it is on your PATH. + +This extension supports: +1. **Official YARA-X LSP (Recommended):** Install `yara-x-ls` from the [VirusTotal/yara-x](https://github.com/VirusTotal/yara-x) repository: + ```bash + cargo install --git https://github.com/VirusTotal/yara-x yara-x-ls --locked + ``` + This installs the `yr-ls` binary. +2. **YLS (Legacy):** Install the [Yara Language Server](https://avast.github.io/yls/) (`yls` binary). diff --git a/extension.toml b/extension.toml index 22b04d2..5657462 100644 --- a/extension.toml +++ b/extension.toml @@ -7,9 +7,9 @@ description = "Yara syntax highlighting for Zed." repository = "https://github.com/egibs/yara.zed" [grammars.yara] -repository = "https://github.com/egibs/tree-sitter-yara.git" -commit = "eb3ede203275c38000177f72ec0f9965312806ef" +repository = "https://github.com/celstur/tree-sitter-yara.git" +commit = "b49d980b2e111e0b09a6a8bae18b3fa2b6cf4797" -[language_servers.yls] -name = "yls" +[language_servers.yr-ls] +name = "yr-ls" languages = ["Yara"] diff --git a/languages/yara/config.toml b/languages/yara/config.toml index 72be442..b78b275 100644 --- a/languages/yara/config.toml +++ b/languages/yara/config.toml @@ -2,10 +2,15 @@ name = "Yara" grammar = "yara" path_suffixes = ["yar", "yara"] line_comments = ["//"] +block_comment = ["/*", "*/"] brackets = [ { start = "{", end = "}", close = true, newline = true }, { start = "[", end = "]", close = true, newline = true }, + { start = "(", end = ")", close = true, newline = true }, { start = "\"", end = "\"", close = true, newline = false, not_in = [ "string", ] }, + { start = "'", end = "'", close = true, newline = false, not_in = [ + "string", + ] }, ] diff --git a/languages/yara/folds.scm b/languages/yara/folds.scm new file mode 100644 index 0000000..64c8fec --- /dev/null +++ b/languages/yara/folds.scm @@ -0,0 +1,5 @@ +(rule_definition) @fold +(meta_section) @fold +(strings_section) @fold +(condition_section) @fold +(comment) @fold diff --git a/languages/yara/highlights.scm b/languages/yara/highlights.scm index 54d14f7..03f8f1e 100644 --- a/languages/yara/highlights.scm +++ b/languages/yara/highlights.scm @@ -1,114 +1,140 @@ ; Comments (comment) @comment -; Keywords +; Preprocessor / Top-level Keywords [ - "global" "import" - "private" -] @constant.builtin + "include" +] @keyword.control [ - "rule" -] @function + "private" + "global" +] @keyword.modifier + +"rule" @keyword +; Sections (Emacs warning face) [ "meta" "strings" "condition" -] @property +] @keyword.control -; Operators -[ - "matches" - "contains" - "icontains" - "imatches" - "startswith" - "istartswith" - "endswith" - "iendswith" - "and" - "or" - "not" - "==" - "!=" - "<" - ">" - ">=" - "<=" - "of" - "for" - "all" - "any" - "none" - "in" -] @string.special +; Rule details +(rule_definition + name: (identifier) @function) -; String modifiers -[ - "wide" - "ascii" - "nocase" - "fullword" - "xor" - "base64" - "base64wide" -] @keyword.modifier +(tag) @tag -; Numbers and sizes -(integer_literal) @constant.numeric -(size_unit) @constant.numeric +; Meta definition keys +(meta_definition + key: (identifier) @property) -; Strings +; Variables and String Identifiers +(string_identifier) @variable + +; Literals +(integer_decimal_positive) @constant.numeric +(integer_zero) @constant.numeric +(integer_hexadecimal) @constant.numeric +(size_unit) @constant.numeric (double_quoted_string) @string (single_quoted_string) @string (escape_sequence) @string.escape -(text_string) @text.literal +(text_string) @string -; Hex strings +[ + "true" + "false" +] @constant.boolean + +; Hex Strings (hex_string) @string.special +(hex_seq) @string.special (hex_byte) @constant.numeric -(hex_wildcard) @constant.builtin (hex_jump) @constant.numeric ; Regular expressions (regex_string) @string.regexp -(pattern) @string.regexp +(regular_expression) @string.regexp -; Boolean literals +; Control structures and keywords [ - "true" - "false" -] @constant.boolean + "for" + "in" + "of" +] @keyword.control -; Keywords and special identifiers [ - "them" "all" "any" "none" -] @keyword.operator - + "them" +] @keyword.control -; String identifiers -"$" @string.special.symbol -(identifier) @string -(string_identifier) @string.special.symbol +; String modifiers +[ + "wide" + "ascii" + "nocase" + "fullword" + "xor" + "base64" + "base64wide" +] @keyword.modifier ; Built-ins [ (filesize_keyword) - (entrypoint_keyword) ] @constant.builtin -; Tags -(tag_list - [(identifier) (tag)] @tag) +; Modules and Functions (e.g. pe.import_rva) +(module_identifier) @type +(module_var_or_func name: (identifier) @function) + +; Built-in functions & types matching Emacs +((identifier) @function.builtin + (#match? @function.builtin "^(int(8|16|32)(be)?|uint(8|16|32)(be)?|math\\.[a-z_]+)$")) + +; General identifiers (fallback) +(identifier) @variable + +; Operators +[ + "and" + "or" + "not" + "defined" + "matches" + "contains" + "icontains" + "startswith" + "istartswith" + "endswith" + "iendswith" + "iequals" +] @keyword.operator -; Punctuation and delimiters [ "=" + "==" + "!=" + "<" + ">" + ">=" + "<=" + "+" + "-" + "*" + "\\" + "%" + ".." + "|" +] @operator + +; Punctuation & Delimiters +[ ":" "{" "}" @@ -116,22 +142,5 @@ "]" "(" ")" - "#" - "@" - ".." - "|" "," - "!" - "/" - "\"" - "'" - "*" -] @string.special.symbol - -; Rule names -(rule_definition - name: (identifier) @string.special) - -; Meta definitions -(meta_definition - key: (identifier) @string.special) +] @punctuation.delimiter diff --git a/languages/yara/indents.scm b/languages/yara/indents.scm new file mode 100644 index 0000000..0d8ba1a --- /dev/null +++ b/languages/yara/indents.scm @@ -0,0 +1,4 @@ +(rule_body) @indent +(meta_section) @indent +(strings_section) @indent +(condition_section) @indent diff --git a/languages/yara/outline.scm b/languages/yara/outline.scm new file mode 100644 index 0000000..f2d7d36 --- /dev/null +++ b/languages/yara/outline.scm @@ -0,0 +1,16 @@ +(rule_definition + name: (identifier) @name) @item + +(meta_section + "meta" @name) @item + +(meta_definition + key: (identifier) @name) @item + +(strings_section + "strings" @name) @item + +(string_identifier) @name @item + +(condition_section + "condition" @name) @item diff --git a/src/yls.rs b/src/yls.rs index 9dcaa69..84a2708 100644 --- a/src/yls.rs +++ b/src/yls.rs @@ -12,14 +12,26 @@ impl Extension for YaraExtension { _language_server_id: &zed_extension_api::LanguageServerId, _worktree: &zed_extension_api::Worktree, ) -> zed_extension_api::Result { - match _worktree.which("yls") { - Some(path) => Ok(zed_extension_api::process::Command { - command: path, - args: vec!["-v".into()], - env: vec![], - }), - None => Err("Unable to find yls from worktree.".into()), + let server_name = _language_server_id.as_ref(); + if server_name == "yr-ls" { + if let Some(path) = _worktree.which("yr-ls") { + return Ok(zed_extension_api::process::Command { + command: path, + args: vec![], + env: vec![], + }); + } + if let Some(path) = _worktree.which("yls") { + return Ok(zed_extension_api::process::Command { + command: path, + args: vec!["-v".into()], + env: vec![], + }); + } + return Err("Unable to find yr-ls or yls from worktree.".into()); } + + Err(format!("Unknown language server: {}", server_name).into()) } }