From 67644b9d7fb9909b44dcfb28b46a44201780d9ca Mon Sep 17 00:00:00 2001
From: jimtng <2554958+jimtng@users.noreply.github.com>
Date: Tue, 25 Feb 2025 23:36:55 +1000
Subject: [PATCH 1/9] fix(ruby) symbols, string interpolation, class names with
underscores
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
---
CHANGES.md | 7 ++
src/languages/ruby.js | 153 +++++++++++++++++++---------
test/markup/ruby/classes.expect.txt | 7 ++
test/markup/ruby/classes.txt | 7 ++
test/markup/ruby/strings.expect.txt | 26 ++++-
test/markup/ruby/strings.txt | 26 ++++-
test/markup/ruby/symbols.expect.txt | 22 ++++
test/markup/ruby/symbols.txt | 22 ++++
8 files changed, 220 insertions(+), 50 deletions(-)
create mode 100644 test/markup/ruby/classes.expect.txt
create mode 100644 test/markup/ruby/classes.txt
create mode 100644 test/markup/ruby/symbols.expect.txt
create mode 100644 test/markup/ruby/symbols.txt
diff --git a/CHANGES.md b/CHANGES.md
index 76fa83e573..62cc1846d4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,10 @@
+## Version 11.11.2
+
+Core Grammars:
+
+- fix(ruby) symbols, string interpolation, class names with underscores
+
+
## Version 11.11.1
- Fixes regression with Rust grammar.
diff --git a/src/languages/ruby.js b/src/languages/ruby.js
index 91b2cb527d..2ae6896fcf 100644
--- a/src/languages/ruby.js
+++ b/src/languages/ruby.js
@@ -11,13 +11,7 @@ export default function(hljs) {
const regex = hljs.regex;
const RUBY_METHOD_RE = '([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)';
// TODO: move concepts like CAMEL_CASE into `modes.js`
- const CLASS_NAME_RE = regex.either(
- /\b([A-Z]+[a-z0-9]+)+/,
- // ends in caps
- /\b([A-Z]+[a-z0-9]+)+[A-Z]+/,
- )
- ;
- const CLASS_NAME_WITH_NAMESPACE_RE = regex.concat(CLASS_NAME_RE, /(::\w+)*/)
+ const CLASS_NAME_RE = /\b([A-Z]+[a-z0-9_]+)+[A-Z]*/;
// very popular ruby built-ins that one might even assume
// are actual keywords (despite that not being the case)
const PSEUDO_KWS = [
@@ -120,19 +114,16 @@ export default function(hljs) {
className: 'subst',
begin: /#\{/,
end: /\}/,
- keywords: RUBY_KEYWORDS
+ keywords: RUBY_KEYWORDS,
+ relevance: 10
};
- const STRING = {
+ const STRING_INTERPOLABLE = {
className: 'string',
contains: [
hljs.BACKSLASH_ESCAPE,
SUBST
],
variants: [
- {
- begin: /'/,
- end: /'/
- },
{
begin: /"/,
end: /"/
@@ -142,45 +133,45 @@ export default function(hljs) {
end: /`/
},
{
- begin: /%[qQwWx]?\(/,
- end: /\)/
+ begin: /%[QWx]?\(/,
+ end: /\)/,
+ relevance: 2
},
{
- begin: /%[qQwWx]?\[/,
- end: /\]/
+ begin: /%[QWx]?\[/,
+ end: /\]/,
+ relevance: 2
},
{
- begin: /%[qQwWx]?\{/,
- end: /\}/
+ begin: /%[QWx]?\{/,
+ end: /\}/,
+ relevance: 2
},
{
- begin: /%[qQwWx]?,
- end: />/
+ begin: /%[QWx]?,
+ end: />/,
+ relevance: 2
},
{
- begin: /%[qQwWx]?\//,
- end: /\//
+ begin: /%[QWx]?\//,
+ end: /\//,
+ relevance: 2
},
{
- begin: /%[qQwWx]?%/,
- end: /%/
+ begin: /%[QWx]?%/,
+ end: /%/,
+ relevance: 2
},
{
- begin: /%[qQwWx]?-/,
- end: /-/
+ begin: /%[QWx]?-/,
+ end: /-/,
+ relevance: 2
},
{
- begin: /%[qQwWx]?\|/,
- end: /\|/
+ begin: /%[QWx]?\|/,
+ end: /\|/,
+ relevance: 2
},
- // in the following expressions, \B in the beginning suppresses recognition of ?-sequences
- // where ? is the last character of a preceding identifier, as in: `func?4`
- { begin: /\B\?(\\\d{1,3})/ },
- { begin: /\B\?(\\x[A-Fa-f0-9]{1,2})/ },
- { begin: /\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/ },
- { begin: /\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/ },
- { begin: /\B\?\\(c|C-)[\x20-\x7e]/ },
- { begin: /\B\?\\?\S/ },
// heredocs
{
// this guard makes sure that we have an entire heredoc and not a false
@@ -202,6 +193,63 @@ export default function(hljs) {
}
]
};
+ const STRING_NONINTERPOLABLE = {
+ className: 'string',
+ variants: [
+ {
+ begin: /'/,
+ end: /'/
+ },
+ {
+ begin: /%[qw]?\(/,
+ end: /\)/,
+ relevance: 2
+ },
+ {
+ begin: /%[qw]?\[/,
+ end: /\]/,
+ relevance: 2
+ },
+ {
+ begin: /%[qw]?\{/,
+ end: /\}/,
+ relevance: 2
+ },
+ {
+ begin: /%[qw]?,
+ end: />/,
+ relevance: 2
+ },
+ {
+ begin: /%[qw]?\//,
+ end: /\//,
+ relevance: 2
+ },
+ {
+ begin: /%[qw]?%/,
+ end: /%/,
+ relevance: 2
+ },
+ {
+ begin: /%[qw]?-/,
+ end: /-/,
+ relevance: 2
+ },
+ {
+ begin: /%[qw]?\|/,
+ end: /\|/,
+ relevance: 2
+ },
+ // in the following expressions, \B in the beginning suppresses recognition of ?-sequences
+ // where ? is the last character of a preceding identifier, as in: `func?4`
+ { begin: /\B\?(\\\d{1,3})/ },
+ { begin: /\B\?(\\x[A-Fa-f0-9]{1,2})/ },
+ { begin: /\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/ },
+ { begin: /\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/ },
+ { begin: /\B\?\\(c|C-)[\x20-\x7e]/ },
+ { begin: /\B\?\\?\S/ }
+ ]
+ };
// Ruby syntax is underdocumented, but this grammar seems to be accurate
// as of version 2.7.2 (confirmed with (irb and `Ripper.sexp(...)`)
@@ -246,7 +294,7 @@ export default function(hljs) {
const INCLUDE_EXTEND = {
match: [
/(include|extend)\s+/,
- CLASS_NAME_WITH_NAMESPACE_RE
+ CLASS_NAME_RE
],
scope: {
2: "title.class"
@@ -259,15 +307,15 @@ export default function(hljs) {
{
match: [
/class\s+/,
- CLASS_NAME_WITH_NAMESPACE_RE,
+ CLASS_NAME_RE,
/\s+<\s+/,
- CLASS_NAME_WITH_NAMESPACE_RE
+ CLASS_NAME_RE
]
},
{
match: [
/\b(class|module)\s+/,
- CLASS_NAME_WITH_NAMESPACE_RE
+ CLASS_NAME_RE
]
}
],
@@ -301,7 +349,7 @@ export default function(hljs) {
const OBJECT_CREATION = {
relevance: 0,
match: [
- CLASS_NAME_WITH_NAMESPACE_RE,
+ CLASS_NAME_RE,
/\.new[. (]/
],
scope: {
@@ -317,7 +365,8 @@ export default function(hljs) {
};
const RUBY_DEFAULT_CONTAINS = [
- STRING,
+ STRING_INTERPOLABLE,
+ STRING_NONINTERPOLABLE,
CLASS_DEFINITION,
INCLUDE_EXTEND,
OBJECT_CREATION,
@@ -326,7 +375,8 @@ export default function(hljs) {
METHOD_DEFINITION,
{
// swallow namespace qualifiers before symbols
- begin: hljs.IDENT_RE + '::' },
+ begin: hljs.IDENT_RE + '::'
+ },
{
className: 'symbol',
begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
@@ -334,12 +384,19 @@ export default function(hljs) {
},
{
className: 'symbol',
- begin: ':(?!\\s)',
+ begin: '(?Class
+ClassName
+Class_Name
+ClassNAME
+ClassName::With::Namespace
+ClassName::With.method
+::TopLevel::Class
\ No newline at end of file
diff --git a/test/markup/ruby/classes.txt b/test/markup/ruby/classes.txt
new file mode 100644
index 0000000000..ae2c069837
--- /dev/null
+++ b/test/markup/ruby/classes.txt
@@ -0,0 +1,7 @@
+Class
+ClassName
+Class_Name
+ClassNAME
+ClassName::With::Namespace
+ClassName::With.method
+::TopLevel::Class
\ No newline at end of file
diff --git a/test/markup/ruby/strings.expect.txt b/test/markup/ruby/strings.expect.txt
index 07b8c51197..68a351a3e8 100644
--- a/test/markup/ruby/strings.expect.txt
+++ b/test/markup/ruby/strings.expect.txt
@@ -27,4 +27,28 @@ c = ?\u{00AF09}
c = ?\u{0AF09}
c = ?\u{AF9}
c = ?\u{F9}
-c = ?\u{F}
\ No newline at end of file
+c = ?\u{F}
+
+
+"string"
+"string #{var}"
+`string`
+`string #{var}`
+%W[foo bar]
+%W[foo bar #{var}]
+%Q[foo bar]
+%Q[foo bar #{var}]
+%x[foo]
+%x[foo #{var}]
+<<~DOC
+ Multiline heredoc
+ Text #{var}
+DOC
+
+
+'string'
+'string #{var}'
+%q[foo]
+%q[foo #{var}]
+%w[foo]
+%w[foo #{var}]
\ No newline at end of file
diff --git a/test/markup/ruby/strings.txt b/test/markup/ruby/strings.txt
index 43d35d656b..2ec98d2f0c 100644
--- a/test/markup/ruby/strings.txt
+++ b/test/markup/ruby/strings.txt
@@ -27,4 +27,28 @@ c = ?\u{00AF09}
c = ?\u{0AF09}
c = ?\u{AF9}
c = ?\u{F9}
-c = ?\u{F}
\ No newline at end of file
+c = ?\u{F}
+
+# Interpolable Strings
+"string"
+"string #{var}"
+`string`
+`string #{var}`
+%W[foo bar]
+%W[foo bar #{var}]
+%Q[foo bar]
+%Q[foo bar #{var}]
+%x[foo]
+%x[foo #{var}]
+<<~DOC
+ Multiline heredoc
+ Text #{var}
+DOC
+
+# Non-interpolable Strings
+'string'
+'string #{var}'
+%q[foo]
+%q[foo #{var}]
+%w[foo]
+%w[foo #{var}]
diff --git a/test/markup/ruby/symbols.expect.txt b/test/markup/ruby/symbols.expect.txt
new file mode 100644
index 0000000000..df75d4fbcc
--- /dev/null
+++ b/test/markup/ruby/symbols.expect.txt
@@ -0,0 +1,22 @@
+:symbol
+:Symbol
+:_leading
+:trailing_
+:contains_underscore
+:symbol_CAPS
+:"string symbol"
+:"interpolated #{test}"
+:'string symbol'
+:'not interpolated #{test}'
+method :symbol
+method(:symbol)
+method(&:symbol)
+assign=:symbol
+assign = :symbol
+:symbol, others
+:1notasymbol
+:%q[notasymbol]
+
+::notsymbol
+
+hash_symbol: value
\ No newline at end of file
diff --git a/test/markup/ruby/symbols.txt b/test/markup/ruby/symbols.txt
new file mode 100644
index 0000000000..b11c5c04ba
--- /dev/null
+++ b/test/markup/ruby/symbols.txt
@@ -0,0 +1,22 @@
+:symbol
+:Symbol
+:_leading
+:trailing_
+:contains_underscore
+:symbol_CAPS
+:"string symbol"
+:"interpolated #{test}"
+:'string symbol'
+:'not interpolated #{test}'
+method :symbol
+method(:symbol)
+method(&:symbol)
+assign=:symbol
+assign = :symbol
+:symbol, others
+:1notasymbol
+:%q[notasymbol]
+
+::notsymbol
+
+hash_symbol: value
\ No newline at end of file
From 1ce45f356095fa1eeae63196aa3032c26127adb7 Mon Sep 17 00:00:00 2001
From: jimtng <2554958+jimtng@users.noreply.github.com>
Date: Wed, 5 Mar 2025 14:40:46 +1000
Subject: [PATCH 2/9] remove negative lookbehind
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
---
src/languages/ruby.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/languages/ruby.js b/src/languages/ruby.js
index 2ae6896fcf..c2336b26cc 100644
--- a/src/languages/ruby.js
+++ b/src/languages/ruby.js
@@ -375,7 +375,7 @@ export default function(hljs) {
METHOD_DEFINITION,
{
// swallow namespace qualifiers before symbols
- begin: hljs.IDENT_RE + '::'
+ begin: '::'
},
{
className: 'symbol',
@@ -384,7 +384,7 @@ export default function(hljs) {
},
{
className: 'symbol',
- begin: '(?
Date: Wed, 5 Mar 2025 14:41:30 +1000
Subject: [PATCH 3/9] reduce relevance of string interpolation
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
---
src/languages/ruby.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/languages/ruby.js b/src/languages/ruby.js
index c2336b26cc..ad2bec8416 100644
--- a/src/languages/ruby.js
+++ b/src/languages/ruby.js
@@ -115,7 +115,7 @@ export default function(hljs) {
begin: /#\{/,
end: /\}/,
keywords: RUBY_KEYWORDS,
- relevance: 10
+ relevance: 2
};
const STRING_INTERPOLABLE = {
className: 'string',
From f53b568ff64716ffb4af1bd4b084a3a66bf482aa Mon Sep 17 00:00:00 2001
From: jimtng <2554958+jimtng@users.noreply.github.com>
Date: Wed, 5 Mar 2025 15:29:42 +1000
Subject: [PATCH 4/9] remove relevance
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
---
src/languages/ruby.js | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/src/languages/ruby.js b/src/languages/ruby.js
index ad2bec8416..6d9feca8aa 100644
--- a/src/languages/ruby.js
+++ b/src/languages/ruby.js
@@ -115,7 +115,6 @@ export default function(hljs) {
begin: /#\{/,
end: /\}/,
keywords: RUBY_KEYWORDS,
- relevance: 2
};
const STRING_INTERPOLABLE = {
className: 'string',
@@ -135,42 +134,34 @@ export default function(hljs) {
{
begin: /%[QWx]?\(/,
end: /\)/,
- relevance: 2
},
{
begin: /%[QWx]?\[/,
end: /\]/,
- relevance: 2
},
{
begin: /%[QWx]?\{/,
end: /\}/,
- relevance: 2
},
{
begin: /%[QWx]?,
end: />/,
- relevance: 2
},
{
begin: /%[QWx]?\//,
end: /\//,
- relevance: 2
},
{
begin: /%[QWx]?%/,
end: /%/,
- relevance: 2
},
{
begin: /%[QWx]?-/,
end: /-/,
- relevance: 2
},
{
begin: /%[QWx]?\|/,
end: /\|/,
- relevance: 2
},
// heredocs
{
@@ -203,42 +194,34 @@ export default function(hljs) {
{
begin: /%[qw]?\(/,
end: /\)/,
- relevance: 2
},
{
begin: /%[qw]?\[/,
end: /\]/,
- relevance: 2
},
{
begin: /%[qw]?\{/,
end: /\}/,
- relevance: 2
},
{
begin: /%[qw]?,
end: />/,
- relevance: 2
},
{
begin: /%[qw]?\//,
end: /\//,
- relevance: 2
},
{
begin: /%[qw]?%/,
end: /%/,
- relevance: 2
},
{
begin: /%[qw]?-/,
end: /-/,
- relevance: 2
},
{
begin: /%[qw]?\|/,
end: /\|/,
- relevance: 2
},
// in the following expressions, \B in the beginning suppresses recognition of ?-sequences
// where ? is the last character of a preceding identifier, as in: `func?4`
From 094e626c568edea55e71899c482cf3d8a49c9eb1 Mon Sep 17 00:00:00 2001
From: jimtng <2554958+jimtng@users.noreply.github.com>
Date: Wed, 5 Mar 2025 15:37:47 +1000
Subject: [PATCH 5/9] match single-letter class name
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
---
src/languages/ruby.js | 4 ++--
test/markup/ruby/classes.expect.txt | 8 +++++++-
test/markup/ruby/classes.txt | 8 +++++++-
3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/src/languages/ruby.js b/src/languages/ruby.js
index 6d9feca8aa..2e852693a3 100644
--- a/src/languages/ruby.js
+++ b/src/languages/ruby.js
@@ -11,7 +11,7 @@ export default function(hljs) {
const regex = hljs.regex;
const RUBY_METHOD_RE = '([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)';
// TODO: move concepts like CAMEL_CASE into `modes.js`
- const CLASS_NAME_RE = /\b([A-Z]+[a-z0-9_]+)+[A-Z]*/;
+ const CLASS_NAME_RE = /\b([A-Z]+[a-z0-9_]*)+[A-Z]*/;
// very popular ruby built-ins that one might even assume
// are actual keywords (despite that not being the case)
const PSEUDO_KWS = [
@@ -362,7 +362,7 @@ export default function(hljs) {
},
{
className: 'symbol',
- begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
+ begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:(?!:)',
relevance: 0
},
{
diff --git a/test/markup/ruby/classes.expect.txt b/test/markup/ruby/classes.expect.txt
index 68419cfa01..d3e4580891 100644
--- a/test/markup/ruby/classes.expect.txt
+++ b/test/markup/ruby/classes.expect.txt
@@ -4,4 +4,10 @@
ClassNAME
ClassName::With::Namespace
ClassName::With.method
-::TopLevel::Class
\ No newline at end of file
+::TopLevel::Class
+
+class Class::Name < With::Inheritance
+end
+
+class A::B < C::D
+end
\ No newline at end of file
diff --git a/test/markup/ruby/classes.txt b/test/markup/ruby/classes.txt
index ae2c069837..c5ba1a2373 100644
--- a/test/markup/ruby/classes.txt
+++ b/test/markup/ruby/classes.txt
@@ -4,4 +4,10 @@ Class_Name
ClassNAME
ClassName::With::Namespace
ClassName::With.method
-::TopLevel::Class
\ No newline at end of file
+::TopLevel::Class
+
+class Class::Name < With::Inheritance
+end
+
+class A::B < C::D
+end
From 2017467a4acfc92a94a9aa4be924239fe7f29f90 Mon Sep 17 00:00:00 2001
From: jimtng <2554958+jimtng@users.noreply.github.com>
Date: Thu, 6 Mar 2025 19:47:32 +1000
Subject: [PATCH 6/9] match single char constant
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
---
src/languages/ruby.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/languages/ruby.js b/src/languages/ruby.js
index 2e852693a3..5bad7e5f13 100644
--- a/src/languages/ruby.js
+++ b/src/languages/ruby.js
@@ -311,7 +311,7 @@ export default function(hljs) {
const UPPER_CASE_CONSTANT = {
relevance: 0,
- match: /\b[A-Z][A-Z_0-9]+\b/,
+ match: /\b[A-Z][A-Z_0-9]*\b/,
className: "variable.constant"
};
From 34ee845581a494d4216de6bbe43a94d85f819669 Mon Sep 17 00:00:00 2001
From: jimtng <2554958+jimtng@users.noreply.github.com>
Date: Thu, 6 Mar 2025 19:48:09 +1000
Subject: [PATCH 7/9] match class inheritance
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
---
src/languages/ruby.js | 53 ++++-------------------------
test/markup/ruby/classes.expect.txt | 4 +--
test/markup/ruby/classes.txt | 4 +--
3 files changed, 10 insertions(+), 51 deletions(-)
diff --git a/src/languages/ruby.js b/src/languages/ruby.js
index 5bad7e5f13..e50858f23c 100644
--- a/src/languages/ruby.js
+++ b/src/languages/ruby.js
@@ -10,8 +10,9 @@ Category: common, scripting
export default function(hljs) {
const regex = hljs.regex;
const RUBY_METHOD_RE = '([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)';
+ const CLASS_NAME_PATTERN = '([A-Z]+[a-z0-9_]+)+[A-Z]*'
// TODO: move concepts like CAMEL_CASE into `modes.js`
- const CLASS_NAME_RE = /\b([A-Z]+[a-z0-9_]*)+[A-Z]*/;
+ const CLASS_NAME_RE = new RegExp(`\\b${CLASS_NAME_PATTERN}`);
// very popular ruby built-ins that one might even assume
// are actual keywords (despite that not being the case)
const PSEUDO_KWS = [
@@ -274,41 +275,6 @@ export default function(hljs) {
]
};
- const INCLUDE_EXTEND = {
- match: [
- /(include|extend)\s+/,
- CLASS_NAME_RE
- ],
- scope: {
- 2: "title.class"
- },
- keywords: RUBY_KEYWORDS
- };
-
- const CLASS_DEFINITION = {
- variants: [
- {
- match: [
- /class\s+/,
- CLASS_NAME_RE,
- /\s+<\s+/,
- CLASS_NAME_RE
- ]
- },
- {
- match: [
- /\b(class|module)\s+/,
- CLASS_NAME_RE
- ]
- }
- ],
- scope: {
- 2: "title.class",
- 4: "title.class.inherited"
- },
- keywords: RUBY_KEYWORDS
- };
-
const UPPER_CASE_CONSTANT = {
relevance: 0,
match: /\b[A-Z][A-Z_0-9]*\b/,
@@ -329,15 +295,10 @@ export default function(hljs) {
]
};
- const OBJECT_CREATION = {
+ const CLASS_INHERITANCE = {
relevance: 0,
- match: [
- CLASS_NAME_RE,
- /\.new[. (]/
- ],
- scope: {
- 1: "title.class"
- }
+ match: new RegExp(`(?<=\\s*class\\s+(${CLASS_NAME_PATTERN}::)*${CLASS_NAME_PATTERN}\\s*<\\s*(${CLASS_NAME_PATTERN}::)*)${CLASS_NAME_PATTERN}`),
+ className: "title.class.inherited"
};
// CamelCase
@@ -350,10 +311,8 @@ export default function(hljs) {
const RUBY_DEFAULT_CONTAINS = [
STRING_INTERPOLABLE,
STRING_NONINTERPOLABLE,
- CLASS_DEFINITION,
- INCLUDE_EXTEND,
- OBJECT_CREATION,
UPPER_CASE_CONSTANT,
+ CLASS_INHERITANCE,
CLASS_REFERENCE,
METHOD_DEFINITION,
{
diff --git a/test/markup/ruby/classes.expect.txt b/test/markup/ruby/classes.expect.txt
index d3e4580891..fdfe4b5cba 100644
--- a/test/markup/ruby/classes.expect.txt
+++ b/test/markup/ruby/classes.expect.txt
@@ -6,8 +6,8 @@
ClassName::With.method
::TopLevel::Class
-class Class::Name < With::Inheritance
+class Foo::Bar::Baz < Qux::Quux::Corge
end
-class A::B < C::D
+class Child < Parent
end
\ No newline at end of file
diff --git a/test/markup/ruby/classes.txt b/test/markup/ruby/classes.txt
index c5ba1a2373..8ee77737cc 100644
--- a/test/markup/ruby/classes.txt
+++ b/test/markup/ruby/classes.txt
@@ -6,8 +6,8 @@ ClassName::With::Namespace
ClassName::With.method
::TopLevel::Class
-class Class::Name < With::Inheritance
+class Foo::Bar::Baz < Qux::Quux::Corge
end
-class A::B < C::D
+class Child < Parent
end
From 1e751a93cec6152d4e7c8bda251bb30ef7d1ab63 Mon Sep 17 00:00:00 2001
From: jimtng <2554958+jimtng@users.noreply.github.com>
Date: Thu, 6 Mar 2025 20:23:07 +1000
Subject: [PATCH 8/9] clean up last commas
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
---
src/languages/ruby.js | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/languages/ruby.js b/src/languages/ruby.js
index e50858f23c..907fb60abb 100644
--- a/src/languages/ruby.js
+++ b/src/languages/ruby.js
@@ -115,7 +115,7 @@ export default function(hljs) {
className: 'subst',
begin: /#\{/,
end: /\}/,
- keywords: RUBY_KEYWORDS,
+ keywords: RUBY_KEYWORDS
};
const STRING_INTERPOLABLE = {
className: 'string',
@@ -134,35 +134,35 @@ export default function(hljs) {
},
{
begin: /%[QWx]?\(/,
- end: /\)/,
+ end: /\)/
},
{
begin: /%[QWx]?\[/,
- end: /\]/,
+ end: /\]/
},
{
begin: /%[QWx]?\{/,
- end: /\}/,
+ end: /\}/
},
{
begin: /%[QWx]?,
- end: />/,
+ end: />/
},
{
begin: /%[QWx]?\//,
- end: /\//,
+ end: /\//
},
{
begin: /%[QWx]?%/,
- end: /%/,
+ end: /%/
},
{
begin: /%[QWx]?-/,
- end: /-/,
+ end: /-/
},
{
begin: /%[QWx]?\|/,
- end: /\|/,
+ end: /\|/
},
// heredocs
{
@@ -194,35 +194,35 @@ export default function(hljs) {
},
{
begin: /%[qw]?\(/,
- end: /\)/,
+ end: /\)/
},
{
begin: /%[qw]?\[/,
- end: /\]/,
+ end: /\]/
},
{
begin: /%[qw]?\{/,
- end: /\}/,
+ end: /\}/
},
{
begin: /%[qw]?,
- end: />/,
+ end: />/
},
{
begin: /%[qw]?\//,
- end: /\//,
+ end: /\//
},
{
begin: /%[qw]?%/,
- end: /%/,
+ end: /%/
},
{
begin: /%[qw]?-/,
- end: /-/,
+ end: /-/
},
{
begin: /%[qw]?\|/,
- end: /\|/,
+ end: /\|/
},
// in the following expressions, \B in the beginning suppresses recognition of ?-sequences
// where ? is the last character of a preceding identifier, as in: `func?4`
From eace348e91567fef49df24f2ec4355abb54d4b7b Mon Sep 17 00:00:00 2001
From: jimtng <2554958+jimtng@users.noreply.github.com>
Date: Fri, 7 Mar 2025 17:37:06 +1000
Subject: [PATCH 9/9] remove the use of lookbehind
Signed-off-by: jimtng <2554958+jimtng@users.noreply.github.com>
---
src/languages/ruby.js | 50 +++++++++++++++++++++++++----
test/markup/ruby/classes.expect.txt | 13 +++++---
test/markup/ruby/classes.txt | 11 ++++---
3 files changed, 58 insertions(+), 16 deletions(-)
diff --git a/src/languages/ruby.js b/src/languages/ruby.js
index 907fb60abb..6121071b22 100644
--- a/src/languages/ruby.js
+++ b/src/languages/ruby.js
@@ -10,9 +10,8 @@ Category: common, scripting
export default function(hljs) {
const regex = hljs.regex;
const RUBY_METHOD_RE = '([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)';
- const CLASS_NAME_PATTERN = '([A-Z]+[a-z0-9_]+)+[A-Z]*'
// TODO: move concepts like CAMEL_CASE into `modes.js`
- const CLASS_NAME_RE = new RegExp(`\\b${CLASS_NAME_PATTERN}`);
+ const CLASS_NAME_RE = /\b([A-Z]+[a-z0-9_]+)+[A-Z]*/;
// very popular ruby built-ins that one might even assume
// are actual keywords (despite that not being the case)
const PSEUDO_KWS = [
@@ -295,10 +294,9 @@ export default function(hljs) {
]
};
- const CLASS_INHERITANCE = {
- relevance: 0,
- match: new RegExp(`(?<=\\s*class\\s+(${CLASS_NAME_PATTERN}::)*${CLASS_NAME_PATTERN}\\s*<\\s*(${CLASS_NAME_PATTERN}::)*)${CLASS_NAME_PATTERN}`),
- className: "title.class.inherited"
+ const CLASS_ANCESTOR = {
+ className: "title.class.inherited",
+ match: CLASS_NAME_RE
};
// CamelCase
@@ -308,12 +306,50 @@ export default function(hljs) {
scope: "title.class"
};
+ const CLASS_SEPARATOR = {
+ match: /::/
+ };
+
+ const CLASS_KEYWORD = {
+ match: /class/,
+ scope: "keyword"
+ };
+
+ const CLASS_INHERITANCE = {
+ begin: /\s*class\b/,
+ excludeBegin: true,
+ variants: [
+ {
+ contains: [
+ CLASS_KEYWORD,
+ {
+ begin: /\s/,
+ contains: [ CLASS_REFERENCE, CLASS_SEPARATOR ],
+ },
+ {
+ begin: /<\s*/,
+ contains: [ CLASS_ANCESTOR, CLASS_SEPARATOR ]
+ }
+ ]
+ },
+ {
+ contains: [
+ CLASS_KEYWORD,
+ {
+ begin: /\s/,
+ contains: [ CLASS_REFERENCE, CLASS_SEPARATOR ]
+ },
+ ]
+ }
+ ]
+ };
+
const RUBY_DEFAULT_CONTAINS = [
STRING_INTERPOLABLE,
STRING_NONINTERPOLABLE,
UPPER_CASE_CONSTANT,
- CLASS_INHERITANCE,
CLASS_REFERENCE,
+ CLASS_INHERITANCE,
METHOD_DEFINITION,
{
// swallow namespace qualifiers before symbols
diff --git a/test/markup/ruby/classes.expect.txt b/test/markup/ruby/classes.expect.txt
index fdfe4b5cba..1b59166e66 100644
--- a/test/markup/ruby/classes.expect.txt
+++ b/test/markup/ruby/classes.expect.txt
@@ -6,8 +6,11 @@
ClassName::With.method
::TopLevel::Class
-class Foo::Bar::Baz < Qux::Quux::Corge
-end
-
-class Child < Parent
-end
\ No newline at end of file
+class Foo::Bar::Baz
+class Foo < Qux
+class Foo < ::Qux
+class Foo<Qux
+class Foo::Bar < Qux
+class Foo < Qux::Quux
+class Foo::Bar::Baz < Qux::Quux
+class Foo::Bar::Baz < Qux::Quux::Corge
\ No newline at end of file
diff --git a/test/markup/ruby/classes.txt b/test/markup/ruby/classes.txt
index 8ee77737cc..91cbca4f7f 100644
--- a/test/markup/ruby/classes.txt
+++ b/test/markup/ruby/classes.txt
@@ -6,8 +6,11 @@ ClassName::With::Namespace
ClassName::With.method
::TopLevel::Class
+class Foo::Bar::Baz
+class Foo < Qux
+class Foo < ::Qux
+class Foo