From eb15fc829f2d3fd3d5ec1726731433b298c6f6be Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 7 Sep 2026 22:31:58 +1200 Subject: [PATCH] Fix Ruby predicate and bang method highlighting Assisted-By: devx/908b949a-46bc-4163-a012-6ecd9d79be49 --- Syntax/Language/ruby.js | 12 ++++++++---- test/Syntax/Language/ruby.js | 27 ++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Syntax/Language/ruby.js b/Syntax/Language/ruby.js index d21c2ae..54d9f86 100644 --- a/Syntax/Language/ruby.js +++ b/Syntax/Language/ruby.js @@ -4,11 +4,15 @@ import {Match} from '../Match.js'; const language = new Language('ruby'); -// Ruby-style function definitions and method calls (def foo, .bar) -// Method names can end with ? or ! +// Ruby-style function definitions and method calls (def foo, .bar). +// Predicate and bang methods are also unambiguous without a receiver or arguments. const rubyStyleFunction = { - pattern: /(?:def\s+|\.)([a-z_][a-z0-9_]*[?!]?)/i, - matches: Rule.extractMatches({type: 'function'}) + pattern: + /(?:def\s+|\.)([a-z_][a-z0-9_]*[?!]?)|(^|[^\w.:])([a-z_][a-z0-9_]*[?!])(?!:)/i, + matches: Rule.extractMatches( + {index: 1, type: 'function'}, + {index: 3, type: 'function'} + ) }; // Emulate negative lookbehind to avoid matching ::symbol (only match :symbol not ::symbol) diff --git a/test/Syntax/Language/ruby.js b/test/Syntax/Language/ruby.js index 1a81e83..48ee388 100644 --- a/test/Syntax/Language/ruby.js +++ b/test/Syntax/Language/ruby.js @@ -1,6 +1,6 @@ import Syntax from '../../../Syntax.js'; import registerRuby from '../../../Syntax/Language/ruby.js'; -import {strictEqual} from 'node:assert'; +import {deepStrictEqual, strictEqual} from 'node:assert'; import test from 'node:test'; async function getTypesFor(code) { @@ -68,3 +68,28 @@ test('Ruby: function detection', async () => { const types = await getTypesFor('def compute\nend\nobject.method'); strictEqual(types.includes('function'), true); }); + +test('Ruby: predicate and bang method detection', async () => { + const syntax = new Syntax(); + registerRuby(syntax); + const language = await syntax.getLanguage('ruby'); + const matches = await language.getMatches( + syntax, + 'def valid?\nend\nobject.save!\nvalid?(value)\nsave! value\nready?' + ); + const functions = matches + .filter(match => match.expression.type === 'function') + .map(match => match.value); + + deepStrictEqual(functions, ['valid?', 'save!', 'valid?', 'save!', 'ready?']); +}); + +test('Ruby: predicate and bang symbols are not methods', async () => { + const syntax = new Syntax(); + registerRuby(syntax); + const language = await syntax.getLanguage('ruby'); + const matches = await language.getMatches(syntax, ':valid?\n:save!\nvalid?: true'); + const functions = matches.filter(match => match.expression.type === 'function'); + + deepStrictEqual(functions, []); +});