From c90d127e459652b6a20cb61e1aa6a0861bc3be6f Mon Sep 17 00:00:00 2001 From: Wojciech Kania Date: Sun, 19 Dec 2021 23:20:36 +0100 Subject: [PATCH 1/6] (chore) add php IDENT_RE --- src/languages/php.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/languages/php.js b/src/languages/php.js index 728e744720..51fc8e685a 100644 --- a/src/languages/php.js +++ b/src/languages/php.js @@ -12,12 +12,13 @@ Category: common * */ export default function(hljs) { const regex = hljs.regex; + const IDENT_RE = '([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' + + // negative look-ahead tries to avoid matching patterns that are not + // Perl at all like $ident$, @ident@, etc. + '(?![A-Za-z0-9])(?![$]))'; const VARIABLE = { scope: 'variable', - match: '\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' + - // negative look-ahead tries to avoid matching patterns that are not - // Perl at all like $ident$, @ident@, etc. - `(?![A-Za-z0-9])(?![$])` + match: '\\$+' + IDENT_RE, }; const PREPROCESSOR = { scope: 'meta', @@ -313,7 +314,7 @@ export default function(hljs) { regex.concat(WHITESPACE, "+"), // to prevent built ins from being confused as the class constructor call regex.concat("(?!", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"), - /\\?\w+/, + regex.concat("\\\\?", IDENT_RE, "+"), regex.concat(WHITESPACE, "*\\("), ], scope: { @@ -330,7 +331,7 @@ export default function(hljs) { /\b/, // to prevent keywords from being confused as the function title regex.concat("(?!fn\\b|function\\b|", normalizeKeywords(KWS).join("\\b|"), "|", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"), - /\w+/, + regex.concat(IDENT_RE, "+"), regex.concat(WHITESPACE, "*"), regex.lookahead(/(?=\()/) ], @@ -351,8 +352,8 @@ export default function(hljs) { { contains: [ { - className: 'doctag', - begin: '@[A-Za-z]+' + scope: 'doctag', + match: '@[A-Za-z]+' } ] } @@ -382,9 +383,9 @@ export default function(hljs) { { // swallow composed identifiers to avoid parsing them as keywords match: regex.concat( - /(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/, - regex.concat("(?!", WHITESPACE, "*\\()"), - /(?![a-zA-Z0-9_\x7f-\xff])/ + /(::|->)+/, + IDENT_RE, + regex.concat("(?!", WHITESPACE, "*\\()") ), // scope:"wrong" }, From 0e3000617a67db9e65fdaa4094e0c5187b15db5c Mon Sep 17 00:00:00 2001 From: Wojciech Kania Date: Sun, 19 Dec 2021 23:22:15 +0100 Subject: [PATCH 2/6] enh(php) add PHP constants --- src/languages/php.js | 12 ++++++++++++ test/markup/php/titles.expect.txt | 22 ++++++++++++++++++++++ test/markup/php/titles.txt | 22 ++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 test/markup/php/titles.expect.txt create mode 100644 test/markup/php/titles.txt diff --git a/src/languages/php.js b/src/languages/php.js index 51fc8e685a..a23b6543c2 100644 --- a/src/languages/php.js +++ b/src/languages/php.js @@ -380,6 +380,18 @@ export default function(hljs) { }, VARIABLE, FUNCTION_INVOKE, + { + match: [ + /const/, + regex.concat(WHITESPACE, "+"), + IDENT_RE, + regex.concat(WHITESPACE, "*="), + ], + scope: { + 1: "keyword", + 3: "variable", + }, + }, { // swallow composed identifiers to avoid parsing them as keywords match: regex.concat( diff --git a/test/markup/php/titles.expect.txt b/test/markup/php/titles.expect.txt new file mode 100644 index 0000000000..03a9daaf19 --- /dev/null +++ b/test/markup/php/titles.expect.txt @@ -0,0 +1,22 @@ +final class Example { + const FOO='foo'; + + public function __construct( + public readonly string $name = self::FOO + ) {} + + public function getClass(): string { + return DateTimeImmutable::class; + } + + public function getClassFromSelf(): string { + return self::class; + } + + public static function getClassFromStatic(): string { + return static::class; + } +} + +$date = DateTimeImmutable::createFromMutable(new \DateTime()); +echo $date::class; diff --git a/test/markup/php/titles.txt b/test/markup/php/titles.txt new file mode 100644 index 0000000000..ffa405fb72 --- /dev/null +++ b/test/markup/php/titles.txt @@ -0,0 +1,22 @@ +final class Example { + const FOO='foo'; + + public function __construct( + public readonly string $name = self::FOO + ) {} + + public function getClass(): string { + return DateTimeImmutable::class; + } + + public function getClassFromSelf(): string { + return self::class; + } + + public static function getClassFromStatic(): string { + return static::class; + } +} + +$date = DateTimeImmutable::createFromMutable(new \DateTime()); +echo $date::class; From 1d9cdf217283a5fe29d23ab3b5716385d429225e Mon Sep 17 00:00:00 2001 From: Wojciech Kania Date: Sun, 19 Dec 2021 23:29:39 +0100 Subject: [PATCH 3/6] enh(php) Left and right-side of double colon --- CHANGES.md | 2 + src/languages/php.js | 70 +++++++++++++++++++++++++--- test/markup/php/functions.expect.txt | 2 +- test/markup/php/titles.expect.txt | 12 +++-- test/markup/php/titles.txt | 6 ++- 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0e6f4b1bdf..92f6d7b2f5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,8 @@ These changes should be for the better and should not be super noticeable but if Grammars: +- enh(php) Left and right-side of double colon [Wojciech Kania][] +- enh(php) add PHP constants [Wojciech Kania][] - enh(php) add PHP 8.1 keywords [Wojciech Kania][] - fix(cpp) fix `vector<<` template false positive (#3437) [Josh Goebel][] - enh(php) support First-class Callable Syntax (#3427) [Wojciech Kania][] diff --git a/src/languages/php.js b/src/languages/php.js index a23b6543c2..435ff8ab00 100644 --- a/src/languages/php.js +++ b/src/languages/php.js @@ -12,10 +12,13 @@ Category: common * */ export default function(hljs) { const regex = hljs.regex; - const IDENT_RE = '([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' + + const IDENT_RE_CORE = '[a-zA-Z0-9_\x7f-\xff]*' + // negative look-ahead tries to avoid matching patterns that are not // Perl at all like $ident$, @ident@, etc. '(?![A-Za-z0-9])(?![$]))'; + const IDENT_RE = regex.concat("([a-zA-Z_\\x7f-\\xff]", IDENT_RE_CORE); + // Will not detect camelCase classes + const PASCAL_CASE_CLASS_NAME_RE = regex.concat("([A-Z]", IDENT_RE_CORE); const VARIABLE = { scope: 'variable', match: '\\$+' + IDENT_RE, @@ -47,7 +50,7 @@ export default function(hljs) { end: /[ \t]*(\w+)\b/, contains: hljs.QUOTE_STRING_MODE.contains.concat(SUBST), }); - // list of valid whitespaces because non-breaking space might be part of a name + // list of valid whitespaces because non-breaking space might be part of a IDENT_RE const WHITESPACE = '[ \t\n]'; const STRING = { scope: 'string', @@ -314,7 +317,7 @@ export default function(hljs) { regex.concat(WHITESPACE, "+"), // to prevent built ins from being confused as the class constructor call regex.concat("(?!", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"), - regex.concat("\\\\?", IDENT_RE, "+"), + regex.concat("\\\\?", IDENT_RE), regex.concat(WHITESPACE, "*\\("), ], scope: { @@ -331,7 +334,7 @@ export default function(hljs) { /\b/, // to prevent keywords from being confused as the function title regex.concat("(?!fn\\b|function\\b|", normalizeKeywords(KWS).join("\\b|"), "|", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"), - regex.concat(IDENT_RE, "+"), + IDENT_RE, regex.concat(WHITESPACE, "*"), regex.lookahead(/(?=\()/) ], @@ -340,6 +343,57 @@ export default function(hljs) { } }; + const CONSTANT_REFERENCE = regex.concat(IDENT_RE, "\\b(?!\\()"); + + const LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON = { + variants: [ + { + match: [ + regex.concat( + /::/, + regex.lookahead(/(?!class\b)/) + ), + CONSTANT_REFERENCE, + ], + scope: { + 2: "variable.constant", + }, + }, + { + match: [ + /::/, + /class/, + ], + scope: { + 2: "variable.language", + }, + }, + { + match: [ + PASCAL_CASE_CLASS_NAME_RE, + regex.concat( + "::", + regex.lookahead(/(?!class\b)/) + ), + ], + scope: { + 1: "title.class", + }, + }, + { + match: [ + PASCAL_CASE_CLASS_NAME_RE, + /::/, + /class/, + ], + scope: { + 1: "title.class", + 3: "variable.language", + }, + } + ] + }; + return { case_insensitive: false, keywords: KEYWORDS, @@ -380,16 +434,17 @@ export default function(hljs) { }, VARIABLE, FUNCTION_INVOKE, + LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON, { match: [ /const/, - regex.concat(WHITESPACE, "+"), + /\s/, IDENT_RE, - regex.concat(WHITESPACE, "*="), + /\s*=/, ], scope: { 1: "keyword", - 3: "variable", + 3: "variable.constant", }, }, { @@ -425,6 +480,7 @@ export default function(hljs) { contains: [ 'self', VARIABLE, + LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON, hljs.C_BLOCK_COMMENT_MODE, STRING, NUMBER diff --git a/test/markup/php/functions.expect.txt b/test/markup/php/functions.expect.txt index f6bc43f49a..88c5d94ebe 100644 --- a/test/markup/php/functions.expect.txt +++ b/test/markup/php/functions.expect.txt @@ -13,7 +13,7 @@ $date = new DateTimeImmutable (); $date->format('Y-m-d'); -DateTimeImmutable::createFromMutable(new \DateTime('now')); +DateTimeImmutable::createFromMutable(new \DateTime('now')); str_contains (\strtoupper(substr('abcdef', -2), 'f')); diff --git a/test/markup/php/titles.expect.txt b/test/markup/php/titles.expect.txt index 03a9daaf19..05763d78a2 100644 --- a/test/markup/php/titles.expect.txt +++ b/test/markup/php/titles.expect.txt @@ -1,8 +1,8 @@ -final class Example { - const FOO='foo'; +final class Example extends Foo { + const FOO='foo'; public function __construct( - public readonly string $name = self::FOO + public readonly string $name = self::FOO ) {} public function getClass(): string { @@ -16,7 +16,11 @@ public static function getClassFromStatic(): string { return static::class; } + + public static function getParentClass(): string { + return parent::class; + } } -$date = DateTimeImmutable::createFromMutable(new \DateTime()); +$date = DateTimeImmutable::createFromMutable(new \DateTime()); echo $date::class; diff --git a/test/markup/php/titles.txt b/test/markup/php/titles.txt index ffa405fb72..6ff19f2fc6 100644 --- a/test/markup/php/titles.txt +++ b/test/markup/php/titles.txt @@ -1,4 +1,4 @@ -final class Example { +final class Example extends Foo { const FOO='foo'; public function __construct( @@ -16,6 +16,10 @@ final class Example { public static function getClassFromStatic(): string { return static::class; } + + public static function getParentClass(): string { + return parent::class; + } } $date = DateTimeImmutable::createFromMutable(new \DateTime()); From 2c995631091ea940675f793ca7218046391ab3c4 Mon Sep 17 00:00:00 2001 From: wkania Date: Tue, 4 Jan 2022 20:46:59 +0100 Subject: [PATCH 4/6] Update src/languages/php.js Co-authored-by: Josh Goebel --- src/languages/php.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/php.js b/src/languages/php.js index 435ff8ab00..dfc246c8f0 100644 --- a/src/languages/php.js +++ b/src/languages/php.js @@ -317,8 +317,8 @@ export default function(hljs) { regex.concat(WHITESPACE, "+"), // to prevent built ins from being confused as the class constructor call regex.concat("(?!", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"), - regex.concat("\\\\?", IDENT_RE), - regex.concat(WHITESPACE, "*\\("), + regex.concat(/\\?/, IDENT_RE), + regex.concat(WHITESPACE, "*", /\(/), ], scope: { 1: "keyword", From b13a109295b93c79d690f2f9e42cd3bd5d1b8383 Mon Sep 17 00:00:00 2001 From: wkania Date: Tue, 4 Jan 2022 21:29:55 +0100 Subject: [PATCH 5/6] Update src/languages/php.js --- src/languages/php.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/php.js b/src/languages/php.js index dfc246c8f0..b05dfe0263 100644 --- a/src/languages/php.js +++ b/src/languages/php.js @@ -450,7 +450,7 @@ export default function(hljs) { { // swallow composed identifiers to avoid parsing them as keywords match: regex.concat( - /(::|->)+/, + /->+/, IDENT_RE, regex.concat("(?!", WHITESPACE, "*\\()") ), From be9c02088f4470541fd9cc1a01fd41f4750207b8 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Tue, 4 Jan 2022 17:48:31 -0500 Subject: [PATCH 6/6] maint: remove dead rule --- src/languages/php.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/languages/php.js b/src/languages/php.js index b05dfe0263..7f0be203ae 100644 --- a/src/languages/php.js +++ b/src/languages/php.js @@ -447,15 +447,6 @@ export default function(hljs) { 3: "variable.constant", }, }, - { - // swallow composed identifiers to avoid parsing them as keywords - match: regex.concat( - /->+/, - IDENT_RE, - regex.concat("(?!", WHITESPACE, "*\\()") - ), - // scope:"wrong" - }, CONSTRUCTOR_CALL, { scope: 'function',