diff --git a/Cargo.lock b/Cargo.lock index 88db657..7f01149 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -782,6 +782,17 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae62f7eae5eb549c71b76658648b72cc6111f2d87d24a1e31fa907f4943e3ce" +[[package]] +name = "tree-sitter-rbs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "737ba7f61f95da9cbf3af40ef4167cc500fd2b2f9be7030d67e7ec43a84ae079" +dependencies = [ + "cc", + "tree-sitter", + "tree-sitter-language", +] + [[package]] name = "tree-sitter-ruby" version = "0.23.1" @@ -1056,6 +1067,7 @@ dependencies = [ "serde_json", "tree-sitter", "tree-sitter-embedded-template", + "tree-sitter-rbs", "tree-sitter-ruby", "zed_extension_api", ] diff --git a/Cargo.toml b/Cargo.toml index 2a59901..93d9bc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,5 @@ anyhow = "1.0.89" tree-sitter = "0.26" tree-sitter-ruby = "0.23.1" tree-sitter-embedded-template = "0.25.0" +tree-sitter-rbs = "0.2.3" insta = { version = "1.46", features = ["yaml"] } diff --git a/languages/rbs/highlights.scm b/languages/rbs/highlights.scm index 5789d65..5757ae6 100644 --- a/languages/rbs/highlights.scm +++ b/languages/rbs/highlights.scm @@ -98,7 +98,7 @@ (type_variable) @constant (namespace - (constant) @module) + (constant) @type) (builtin_type) @type.builtin diff --git a/languages/rbs/indents.scm b/languages/rbs/indents.scm index 8d53ab1..130fac3 100644 --- a/languages/rbs/indents.scm +++ b/languages/rbs/indents.scm @@ -5,7 +5,7 @@ (parameters) (tuple_type) (record_type) -] @indent.begin +] @indent (_ "[" @@ -18,5 +18,3 @@ (_ "(" ")" @end) @indent - -(comment) @indent.ignore diff --git a/tests/languages/rbs/highlights.rbs b/tests/languages/rbs/highlights.rbs new file mode 100644 index 0000000..a296bef --- /dev/null +++ b/tests/languages/rbs/highlights.rbs @@ -0,0 +1,57 @@ +use Billing::Invoice as Bill + +$billing_enabled: bool +LATEST_INVOICE: Billing::Invoice[String]? + +module Billing + DEFAULT_CURRENCY: String + + type status = :draft | :paid | :void + type metadata = { id: Integer, label: String?, active: bool } + type callback = ^(Integer) -> String + type mode = "fast" | "slow\n" | 1 | true | false + + interface _Printable + def render: () -> String + end + + interface _Source[out A] + def get: () -> A + end + + module Trackable + @updated_at: Time? + def touch: () -> void + end + + class Invoice[A] < Object + include Trackable + + @memo: String? + @@count: Integer + + attr_reader items: Array[A] + attr_writer memo: String? + attr_accessor owner: String + + def self.build: [T] (T value) -> Invoice[T] + def initialize: (Array[A] items, owner: String) -> void + def render: () -> String + alias formatted render + + def ready?: () -> bool + def reset: () -> void + def []: (Integer index) -> A? + def amount=: (Integer value) -> Integer + def +: (Invoice[A] other) -> Invoice[A] + def each: () { (A) -> void } -> self + + def charge: (Integer amount, ?String note, *String tags, + currency: String, ?retry: bool, **untyped options) + ?{ (Integer) -> void } -> bool + + private + + def normalize: (untyped value) -> String + end +end diff --git a/tests/languages/rbs/snapshots/highlights.snap b/tests/languages/rbs/snapshots/highlights.snap new file mode 100644 index 0000000..78289cd --- /dev/null +++ b/tests/languages/rbs/snapshots/highlights.snap @@ -0,0 +1,1140 @@ +--- +source: tests/support/mod.rs +expression: captures +--- +- name: keyword + line: 1 + column: 1 + text: use +- name: type + line: 1 + column: 5 + text: "Billing::Invoice" +- name: type + line: 1 + column: 5 + text: Billing +- name: keyword + line: 1 + column: 22 + text: as +- name: type + line: 1 + column: 25 + text: Bill +- name: variable + line: 3 + column: 1 + text: $billing_enabled +- name: type.builtin + line: 3 + column: 19 + text: bool +- name: constant + line: 4 + column: 1 + text: LATEST_INVOICE +- name: type + line: 4 + column: 17 + text: Billing +- name: type + line: 4 + column: 26 + text: Invoice +- name: punctuation.bracket + line: 4 + column: 33 + text: "[" +- name: type + line: 4 + column: 34 + text: String +- name: punctuation.bracket + line: 4 + column: 40 + text: "]" +- name: operator + line: 4 + column: 41 + text: "?" +- name: keyword + line: 6 + column: 1 + text: module +- name: type + line: 6 + column: 8 + text: Billing +- name: constant + line: 7 + column: 3 + text: DEFAULT_CURRENCY +- name: type + line: 7 + column: 21 + text: String +- name: keyword.type + line: 9 + column: 3 + text: type +- name: type + line: 9 + column: 8 + text: status +- name: operator + line: 9 + column: 15 + text: "=" +- name: string.special.symbol + line: 9 + column: 17 + text: ":draft" +- name: operator + line: 9 + column: 24 + text: "|" +- name: string.special.symbol + line: 9 + column: 26 + text: ":paid" +- name: operator + line: 9 + column: 32 + text: "|" +- name: string.special.symbol + line: 9 + column: 34 + text: ":void" +- name: keyword.type + line: 10 + column: 3 + text: type +- name: type + line: 10 + column: 8 + text: metadata +- name: operator + line: 10 + column: 17 + text: "=" +- name: punctuation.bracket + line: 10 + column: 19 + text: "{" +- name: string.special.symbol + line: 10 + column: 21 + text: id +- name: type + line: 10 + column: 25 + text: Integer +- name: punctuation.delimiter + line: 10 + column: 32 + text: "," +- name: string.special.symbol + line: 10 + column: 34 + text: label +- name: type + line: 10 + column: 41 + text: String +- name: operator + line: 10 + column: 47 + text: "?" +- name: punctuation.delimiter + line: 10 + column: 48 + text: "," +- name: string.special.symbol + line: 10 + column: 50 + text: active +- name: type.builtin + line: 10 + column: 58 + text: bool +- name: punctuation.bracket + line: 10 + column: 63 + text: "}" +- name: keyword.type + line: 11 + column: 3 + text: type +- name: type + line: 11 + column: 8 + text: callback +- name: operator + line: 11 + column: 17 + text: "=" +- name: operator + line: 11 + column: 19 + text: ^ +- name: punctuation.bracket + line: 11 + column: 20 + text: ( +- name: type + line: 11 + column: 21 + text: Integer +- name: punctuation.bracket + line: 11 + column: 28 + text: ) +- name: operator + line: 11 + column: 30 + text: "->" +- name: type + line: 11 + column: 33 + text: String +- name: keyword.type + line: 12 + column: 3 + text: type +- name: type + line: 12 + column: 8 + text: mode +- name: operator + line: 12 + column: 13 + text: "=" +- name: string + line: 12 + column: 15 + text: "\"fast\"" +- name: operator + line: 12 + column: 22 + text: "|" +- name: string + line: 12 + column: 24 + text: "\"slow\\n\"" +- name: string.escape + line: 12 + column: 29 + text: "\\n" +- name: operator + line: 12 + column: 33 + text: "|" +- name: number + line: 12 + column: 35 + text: "1" +- name: operator + line: 12 + column: 37 + text: "|" +- name: boolean + line: 12 + column: 39 + text: "true" +- name: operator + line: 12 + column: 44 + text: "|" +- name: boolean + line: 12 + column: 46 + text: "false" +- name: keyword.type + line: 14 + column: 3 + text: interface +- name: type + line: 14 + column: 13 + text: _Printable +- name: keyword + line: 15 + column: 5 + text: def +- name: keyword.function + line: 15 + column: 5 + text: def +- name: function.method + line: 15 + column: 9 + text: render +- name: punctuation.bracket + line: 15 + column: 17 + text: ( +- name: punctuation.bracket + line: 15 + column: 18 + text: ) +- name: operator + line: 15 + column: 20 + text: "->" +- name: type + line: 15 + column: 23 + text: String +- name: keyword.type + line: 16 + column: 3 + text: end +- name: keyword + line: 16 + column: 3 + text: end +- name: keyword.type + line: 18 + column: 3 + text: interface +- name: type + line: 18 + column: 13 + text: _Source +- name: punctuation.bracket + line: 18 + column: 20 + text: "[" +- name: keyword + line: 18 + column: 21 + text: out +- name: constant + line: 18 + column: 25 + text: A +- name: punctuation.bracket + line: 18 + column: 26 + text: "]" +- name: keyword + line: 19 + column: 5 + text: def +- name: keyword.function + line: 19 + column: 5 + text: def +- name: function.method + line: 19 + column: 9 + text: get +- name: punctuation.bracket + line: 19 + column: 14 + text: ( +- name: punctuation.bracket + line: 19 + column: 15 + text: ) +- name: operator + line: 19 + column: 17 + text: "->" +- name: type + line: 19 + column: 20 + text: A +- name: keyword.type + line: 20 + column: 3 + text: end +- name: keyword + line: 20 + column: 3 + text: end +- name: keyword + line: 22 + column: 3 + text: module +- name: type + line: 22 + column: 10 + text: Trackable +- name: variable.member + line: 23 + column: 5 + text: "@updated_at" +- name: type + line: 23 + column: 18 + text: Time +- name: operator + line: 23 + column: 22 + text: "?" +- name: keyword + line: 24 + column: 5 + text: def +- name: keyword.function + line: 24 + column: 5 + text: def +- name: function.method + line: 24 + column: 9 + text: touch +- name: punctuation.bracket + line: 24 + column: 16 + text: ( +- name: punctuation.bracket + line: 24 + column: 17 + text: ) +- name: operator + line: 24 + column: 19 + text: "->" +- name: type.builtin + line: 24 + column: 22 + text: void +- name: keyword + line: 25 + column: 3 + text: end +- name: keyword.type + line: 27 + column: 3 + text: class +- name: type + line: 27 + column: 9 + text: Invoice +- name: punctuation.bracket + line: 27 + column: 16 + text: "[" +- name: constant + line: 27 + column: 17 + text: A +- name: punctuation.bracket + line: 27 + column: 18 + text: "]" +- name: operator + line: 27 + column: 20 + text: "<" +- name: type + line: 27 + column: 22 + text: Object +- name: function.method + line: 28 + column: 5 + text: include +- name: type + line: 28 + column: 13 + text: Trackable +- name: variable.member + line: 30 + column: 5 + text: "@memo" +- name: type + line: 30 + column: 12 + text: String +- name: operator + line: 30 + column: 18 + text: "?" +- name: variable.member + line: 31 + column: 5 + text: "@@count" +- name: type + line: 31 + column: 14 + text: Integer +- name: keyword + line: 33 + column: 5 + text: attr_reader +- name: function.method + line: 33 + column: 17 + text: items +- name: type + line: 33 + column: 24 + text: Array +- name: punctuation.bracket + line: 33 + column: 29 + text: "[" +- name: type + line: 33 + column: 30 + text: A +- name: punctuation.bracket + line: 33 + column: 31 + text: "]" +- name: keyword + line: 34 + column: 5 + text: attr_writer +- name: function.method + line: 34 + column: 17 + text: memo +- name: type + line: 34 + column: 23 + text: String +- name: operator + line: 34 + column: 29 + text: "?" +- name: keyword + line: 35 + column: 5 + text: attr_accessor +- name: function.method + line: 35 + column: 19 + text: owner +- name: type + line: 35 + column: 26 + text: String +- name: keyword + line: 37 + column: 5 + text: def +- name: keyword.function + line: 37 + column: 5 + text: def +- name: variable.builtin + line: 37 + column: 9 + text: self +- name: punctuation.delimiter + line: 37 + column: 13 + text: "." +- name: function.method + line: 37 + column: 14 + text: build +- name: punctuation.bracket + line: 37 + column: 21 + text: "[" +- name: constant + line: 37 + column: 22 + text: T +- name: punctuation.bracket + line: 37 + column: 23 + text: "]" +- name: punctuation.bracket + line: 37 + column: 25 + text: ( +- name: type + line: 37 + column: 26 + text: T +- name: variable.parameter + line: 37 + column: 28 + text: value +- name: punctuation.bracket + line: 37 + column: 33 + text: ) +- name: operator + line: 37 + column: 35 + text: "->" +- name: type + line: 37 + column: 38 + text: Invoice +- name: punctuation.bracket + line: 37 + column: 45 + text: "[" +- name: type + line: 37 + column: 46 + text: T +- name: punctuation.bracket + line: 37 + column: 47 + text: "]" +- name: keyword + line: 38 + column: 5 + text: def +- name: keyword.function + line: 38 + column: 5 + text: def +- name: function.method + line: 38 + column: 9 + text: initialize +- name: punctuation.bracket + line: 38 + column: 21 + text: ( +- name: type + line: 38 + column: 22 + text: Array +- name: punctuation.bracket + line: 38 + column: 27 + text: "[" +- name: type + line: 38 + column: 28 + text: A +- name: punctuation.bracket + line: 38 + column: 29 + text: "]" +- name: variable.parameter + line: 38 + column: 31 + text: items +- name: punctuation.delimiter + line: 38 + column: 36 + text: "," +- name: variable.parameter + line: 38 + column: 38 + text: owner +- name: type + line: 38 + column: 45 + text: String +- name: punctuation.bracket + line: 38 + column: 51 + text: ) +- name: operator + line: 38 + column: 53 + text: "->" +- name: type.builtin + line: 38 + column: 56 + text: void +- name: keyword + line: 39 + column: 5 + text: def +- name: keyword.function + line: 39 + column: 5 + text: def +- name: function.method + line: 39 + column: 9 + text: render +- name: punctuation.bracket + line: 39 + column: 17 + text: ( +- name: punctuation.bracket + line: 39 + column: 18 + text: ) +- name: operator + line: 39 + column: 20 + text: "->" +- name: type + line: 39 + column: 23 + text: String +- name: keyword + line: 40 + column: 5 + text: alias +- name: function + line: 40 + column: 11 + text: formatted +- name: function + line: 40 + column: 21 + text: render +- name: keyword + line: 42 + column: 5 + text: def +- name: keyword.function + line: 42 + column: 5 + text: def +- name: function.method + line: 42 + column: 9 + text: ready? +- name: operator + line: 42 + column: 14 + text: "?" +- name: punctuation.bracket + line: 42 + column: 17 + text: ( +- name: punctuation.bracket + line: 42 + column: 18 + text: ) +- name: operator + line: 42 + column: 20 + text: "->" +- name: type.builtin + line: 42 + column: 23 + text: bool +- name: keyword + line: 43 + column: 5 + text: def +- name: keyword.function + line: 43 + column: 5 + text: def +- name: function.method + line: 43 + column: 9 + text: reset +- name: punctuation.bracket + line: 43 + column: 16 + text: ( +- name: punctuation.bracket + line: 43 + column: 17 + text: ) +- name: operator + line: 43 + column: 19 + text: "->" +- name: type.builtin + line: 43 + column: 22 + text: void +- name: keyword + line: 44 + column: 5 + text: def +- name: keyword.function + line: 44 + column: 5 + text: def +- name: function.method + line: 44 + column: 9 + text: "[]" +- name: punctuation.bracket + line: 44 + column: 13 + text: ( +- name: type + line: 44 + column: 14 + text: Integer +- name: variable.parameter + line: 44 + column: 22 + text: index +- name: punctuation.bracket + line: 44 + column: 27 + text: ) +- name: operator + line: 44 + column: 29 + text: "->" +- name: type + line: 44 + column: 32 + text: A +- name: operator + line: 44 + column: 33 + text: "?" +- name: keyword + line: 45 + column: 5 + text: def +- name: keyword.function + line: 45 + column: 5 + text: def +- name: function.method + line: 45 + column: 9 + text: amount= +- name: operator + line: 45 + column: 15 + text: "=" +- name: punctuation.bracket + line: 45 + column: 18 + text: ( +- name: type + line: 45 + column: 19 + text: Integer +- name: variable.parameter + line: 45 + column: 27 + text: value +- name: punctuation.bracket + line: 45 + column: 32 + text: ) +- name: operator + line: 45 + column: 34 + text: "->" +- name: type + line: 45 + column: 37 + text: Integer +- name: keyword + line: 46 + column: 5 + text: def +- name: keyword.function + line: 46 + column: 5 + text: def +- name: function.method + line: 46 + column: 9 + text: + +- name: punctuation.bracket + line: 46 + column: 12 + text: ( +- name: type + line: 46 + column: 13 + text: Invoice +- name: punctuation.bracket + line: 46 + column: 20 + text: "[" +- name: type + line: 46 + column: 21 + text: A +- name: punctuation.bracket + line: 46 + column: 22 + text: "]" +- name: variable.parameter + line: 46 + column: 24 + text: other +- name: punctuation.bracket + line: 46 + column: 29 + text: ) +- name: operator + line: 46 + column: 31 + text: "->" +- name: type + line: 46 + column: 34 + text: Invoice +- name: punctuation.bracket + line: 46 + column: 41 + text: "[" +- name: type + line: 46 + column: 42 + text: A +- name: punctuation.bracket + line: 46 + column: 43 + text: "]" +- name: keyword + line: 47 + column: 5 + text: def +- name: keyword.function + line: 47 + column: 5 + text: def +- name: function.method + line: 47 + column: 9 + text: each +- name: punctuation.bracket + line: 47 + column: 15 + text: ( +- name: punctuation.bracket + line: 47 + column: 16 + text: ) +- name: punctuation.bracket + line: 47 + column: 18 + text: "{" +- name: punctuation.bracket + line: 47 + column: 20 + text: ( +- name: type + line: 47 + column: 21 + text: A +- name: punctuation.bracket + line: 47 + column: 22 + text: ) +- name: operator + line: 47 + column: 24 + text: "->" +- name: type.builtin + line: 47 + column: 27 + text: void +- name: punctuation.bracket + line: 47 + column: 32 + text: "}" +- name: operator + line: 47 + column: 34 + text: "->" +- name: type.builtin + line: 47 + column: 37 + text: self +- name: variable.builtin + line: 47 + column: 37 + text: self +- name: keyword + line: 49 + column: 5 + text: def +- name: keyword.function + line: 49 + column: 5 + text: def +- name: function.method + line: 49 + column: 9 + text: charge +- name: punctuation.bracket + line: 49 + column: 17 + text: ( +- name: type + line: 49 + column: 18 + text: Integer +- name: variable.parameter + line: 49 + column: 26 + text: amount +- name: punctuation.delimiter + line: 49 + column: 32 + text: "," +- name: operator + line: 49 + column: 34 + text: "?" +- name: type + line: 49 + column: 35 + text: String +- name: variable.parameter + line: 49 + column: 42 + text: note +- name: punctuation.delimiter + line: 49 + column: 46 + text: "," +- name: operator + line: 49 + column: 48 + text: "*" +- name: type + line: 49 + column: 49 + text: String +- name: variable.parameter + line: 49 + column: 56 + text: tags +- name: punctuation.delimiter + line: 49 + column: 60 + text: "," +- name: variable.parameter + line: 50 + column: 18 + text: currency +- name: type + line: 50 + column: 28 + text: String +- name: punctuation.delimiter + line: 50 + column: 34 + text: "," +- name: operator + line: 50 + column: 36 + text: "?" +- name: variable.parameter + line: 50 + column: 37 + text: retry +- name: type.builtin + line: 50 + column: 44 + text: bool +- name: punctuation.delimiter + line: 50 + column: 48 + text: "," +- name: operator + line: 50 + column: 50 + text: "**" +- name: type.builtin + line: 50 + column: 52 + text: untyped +- name: variable.parameter + line: 50 + column: 60 + text: options +- name: punctuation.bracket + line: 50 + column: 67 + text: ) +- name: operator + line: 51 + column: 18 + text: "?" +- name: punctuation.bracket + line: 51 + column: 19 + text: "{" +- name: punctuation.bracket + line: 51 + column: 21 + text: ( +- name: type + line: 51 + column: 22 + text: Integer +- name: punctuation.bracket + line: 51 + column: 29 + text: ) +- name: operator + line: 51 + column: 31 + text: "->" +- name: type.builtin + line: 51 + column: 34 + text: void +- name: punctuation.bracket + line: 51 + column: 39 + text: "}" +- name: operator + line: 51 + column: 41 + text: "->" +- name: type.builtin + line: 51 + column: 44 + text: bool +- name: keyword.modifier + line: 53 + column: 5 + text: private +- name: keyword + line: 55 + column: 5 + text: def +- name: keyword.function + line: 55 + column: 5 + text: def +- name: function.method + line: 55 + column: 9 + text: normalize +- name: punctuation.bracket + line: 55 + column: 20 + text: ( +- name: type.builtin + line: 55 + column: 21 + text: untyped +- name: variable.parameter + line: 55 + column: 29 + text: value +- name: punctuation.bracket + line: 55 + column: 34 + text: ) +- name: operator + line: 55 + column: 36 + text: "->" +- name: type + line: 55 + column: 39 + text: String +- name: keyword.type + line: 56 + column: 3 + text: end +- name: keyword + line: 56 + column: 3 + text: end +- name: keyword + line: 57 + column: 1 + text: end diff --git a/tests/rbs.rs b/tests/rbs.rs new file mode 100644 index 0000000..2c9cf3e --- /dev/null +++ b/tests/rbs.rs @@ -0,0 +1,14 @@ +mod support; + +// ============================================================================ +// Highlights Tests +// ============================================================================ + +#[test] +fn highlights() { + support::assert_query_snapshot( + "highlights", + "tests/languages/rbs/highlights.rbs", + "languages/rbs/highlights.scm", + ); +} diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 3dbfaf0..6e8eb1c 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -19,6 +19,7 @@ fn language_for_id(language_id: &str) -> tree_sitter::Language { match language_id { "ruby" => tree_sitter_ruby::LANGUAGE.into(), "erb" => tree_sitter_embedded_template::LANGUAGE.into(), + "rbs" => tree_sitter_rbs::LANGUAGE.into(), _ => panic!("Unsupported language id for query tests: {language_id}"), } }