From be9e7f09ec85c5ef83c17b36bb1cbfc88dfbc241 Mon Sep 17 00:00:00 2001 From: Mark Tareshawty Date: Mon, 24 Aug 2026 10:40:40 -0400 Subject: [PATCH] Cover argument forms and shadowing locals for visibility methods Follow-up to #8, which changed `private`, `protected` and `public` from `@keyword` to `@function.method.builtin` but only for the bare form. A bare `private` parses as an identifier, but `private :foo` and `private def bar; end` parse as calls and are claimed by the general call pattern, so they kept the ordinary `@function.method` capture. Add a call pattern after that one, using `!receiver` so an unrelated method with a colliding name stays an ordinary call. Guard the identifier pattern with `#is-not? local`, matching the general identifier pattern above it, so a local variable shadowing one of these names is highlighted as a variable rather than as a method call. That case was already wrong before #8, as a keyword. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ac21705f-befb-4606-9c83-657e3e9ea0dc --- queries/highlights.scm | 15 +++++++++++++-- test/highlight/classes.rb | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/queries/highlights.scm b/queries/highlights.scm index 3f308db0..61d889f8 100644 --- a/queries/highlights.scm +++ b/queries/highlights.scm @@ -34,9 +34,11 @@ ] @keyword ; `private`, `protected` and `public` are `Module` instance methods, not -; keywords: they can be called with arguments, passed a `def`, or shadowed. +; keywords. Bare, they parse as a plain identifier; `#is-not? local` keeps a +; local variable that shadows one of them from being highlighted as a call. ((identifier) @function.method.builtin - (#match? @function.method.builtin "^(private|protected|public)$")) + (#match? @function.method.builtin "^(private|protected|public)$") + (#is-not? local)) (constant) @constructor @@ -50,6 +52,15 @@ ((identifier) @function.method.builtin (#eq? @function.method.builtin "require")) +; The argument forms, such as `private :foo` and `private def bar; end`, have to +; come after the general call pattern above to take precedence over it. +; `!receiver` keeps an unrelated method that happens to share the name, as in +; `acl.public`, highlighted as an ordinary call. +((call + !receiver + method: (identifier) @function.method.builtin) + (#match? @function.method.builtin "^(private|protected|public)$")) + ; Function definitions (alias (identifier) @function.method) diff --git a/test/highlight/classes.rb b/test/highlight/classes.rb index 9139d903..ca34f0fd 100644 --- a/test/highlight/classes.rb +++ b/test/highlight/classes.rb @@ -29,5 +29,24 @@ def init(id) protected # ^ function.method.builtin + + private :with_symbol + # <- function.method.builtin + + private def with_def; end + # <- function.method.builtin + + def shadowed + private = 1 + # <- variable + private + # <- variable + private + end + + def unrelated(acl) + acl.public + # ^ function.method + end end # <- keyword