Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enh(php) Switch highlighter to partially case-insensitive #3426

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.md
Expand Up @@ -13,6 +13,10 @@ These changes should be for the better and should not be super noticeable but if

Grammars:

- enh(php) support First-class Callable Syntax (#3427) [Wojciech Kania][]
- enh(php) support class constructor call (#3427) [Wojciech Kania][]
- enh(php) support function invoke (#3427) [Wojciech Kania][]
- enh(php) Switch highlighter to partially case-insensitive (#3427) [Wojciech Kania][]
- enh(php) improve `namespace` and `use` highlighting (#3427) [Josh Goebel][]
- enh(php) `$this` is a `variable.language` now (#3427) [Josh Goebel][]
- enh(php) add `__COMPILER_HALT_OFFSET__` (#3427) [Josh Goebel][]
Expand Down Expand Up @@ -46,6 +50,7 @@ Themes:

- Modified background color in css for Gradient Light and Gradient Dark themes [Samia Ali][]

[Wojciech Kania]: https://github.com/wkania
[Jeylani B]: https://github.com/jeyllani
[Richard Gibson]: https://github.com/gibson042
[Bradley Mackey]: https://github.com/bradleymackey
Expand Down
2 changes: 2 additions & 0 deletions docs/css-classes-reference.rst
Expand Up @@ -70,6 +70,8 @@ in mind so a better choice (for best theme support) might possibly be ``string``
+--------------------------+-------------------------------------------------------------+
| title.function | name of a function |
+--------------------------+-------------------------------------------------------------+
| title.function.invoke | name of a function (when being invoked) |
+--------------------------+-------------------------------------------------------------+
| params | block of function arguments (parameters) at the |
| | place of declaration |
+--------------------------+-------------------------------------------------------------+
Expand Down
91 changes: 82 additions & 9 deletions src/languages/php.js
Expand Up @@ -11,6 +11,7 @@ Category: common
* @returns {LanguageDetail}
* */
export default function(hljs) {
const regex = hljs.regex;
const VARIABLE = {
className: 'variable',
begin: '\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' +
Expand Down Expand Up @@ -45,6 +46,8 @@ 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
const WHITESPACE = '[ \t\n]';
const STRING = {
className: 'string',
variants: [
Expand All @@ -56,11 +59,11 @@ export default function(hljs) {
const NUMBER = {
className: 'number',
variants: [
{ begin: `\\b0b[01]+(?:_[01]+)*\\b` }, // Binary w/ underscore support
{ begin: `\\b0o[0-7]+(?:_[0-7]+)*\\b` }, // Octals w/ underscore support
{ begin: `\\b0x[\\da-f]+(?:_[\\da-f]+)*\\b` }, // Hex w/ underscore support
{ begin: `\\b0[bB][01]+(?:_[01]+)*\\b` }, // Binary w/ underscore support
{ begin: `\\b0[oO][0-7]+(?:_[0-7]+)*\\b` }, // Octals w/ underscore support
{ begin: `\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b` }, // Hex w/ underscore support
// Decimals w/ underscore support, with optional fragments and scientific exponent (e) suffix.
{ begin: `(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:e[+-]?\\d+)?` }
{ begin: `(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?` }
],
relevance: 0
};
Expand Down Expand Up @@ -263,13 +266,76 @@ export default function(hljs) {
"stdClass"
];

/** Dual-case keywords
*
* ["then","FILE"] =>
* ["then", "THEN", "FILE", "file"]
*
* @param {string[]} items */
const dualCase = (items) => {
/** @type string[] */
const result = [];
items.forEach(item => {
result.push(item);
if (item.toLowerCase() === item) {
result.push(item.toUpperCase());
} else {
result.push(item.toLowerCase());
}
});
return result;
};

const KEYWORDS = {
keyword: KWS,
literal: LITERALS,
built_in: BUILT_INS
literal: dualCase(LITERALS),
built_in: BUILT_INS,
};

/**
* @param {string[]} items */
const normalizeKeywords = (items) => {
return items.map(item => {
return item.replace(/\|\d+$/, "");
});
};

const CONSTRUCTOR_CALL = {
variants: [
{
match: [
/new/,
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(WHITESPACE, "*\\("),
],
scope: {
1: "keyword",
4: "title.class",
},
}
]
};

const FUNCTION_INVOKE = {
relevance: 0,
match: [
/\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(WHITESPACE, "*"),
regex.lookahead(/(?=\()/)
],
scope: {
3: "title.function.invoke",
}
};

return {
case_insensitive: true,
case_insensitive: false,
keywords: KEYWORDS,
contains: [
hljs.HASH_COMMENT_MODE,
Expand Down Expand Up @@ -307,10 +373,17 @@ export default function(hljs) {
begin: /\$this\b/
},
VARIABLE,
FUNCTION_INVOKE,
{
// swallow composed identifiers to avoid parsing them as keywords
begin: /(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/
match: regex.concat(
/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,
regex.concat("(?!", WHITESPACE, "*\\()"),
/(?![a-zA-Z0-9_\x7f-\xff])/
),
// scope:"wrong"
},
CONSTRUCTOR_CALL,
{
className: 'function',
relevance: 0,
Expand Down Expand Up @@ -357,7 +430,7 @@ export default function(hljs) {
},
// both use and namespace still use "old style" rules (vs multi-match)
// because the namespace name can include `\` and we still want each
// element to be treated as it's own *individual* title
// element to be treated as its own *individual* title
{
beginKeywords: 'namespace',
relevance: 0,
Expand Down
8 changes: 8 additions & 0 deletions test/markup/php/case.expect.txt
@@ -0,0 +1,8 @@
<span class="hljs-variable">$test</span> = <span class="hljs-literal">true</span>
<span class="hljs-variable">$test</span> = <span class="hljs-literal">TRUE</span>

<span class="hljs-variable">$a</span> = <span class="hljs-literal">false</span>
<span class="hljs-variable">$a</span> = <span class="hljs-literal">FALSE</span>

<span class="hljs-variable">$b</span> = <span class="hljs-literal">null</span>
<span class="hljs-variable">$b</span> = <span class="hljs-literal">NULL</span>
8 changes: 8 additions & 0 deletions test/markup/php/case.txt
@@ -0,0 +1,8 @@
$test = true
$test = TRUE

$a = false
$a = FALSE

$b = null
$b = NULL
35 changes: 35 additions & 0 deletions test/markup/php/functions.expect.txt
Expand Up @@ -6,3 +6,38 @@
<span class="hljs-variable">$fn2</span> = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-variable">$x</span></span>) <span class="hljs-keyword">use</span> (<span class="hljs-params"><span class="hljs-variable">$y</span></span>) </span>{
<span class="hljs-keyword">return</span> <span class="hljs-variable">$x</span> + <span class="hljs-variable">$y</span>;
};

<span class="hljs-comment">/**
* Function invoke
*/</span>
<span class="hljs-variable">$date</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">DateTimeImmutable</span> ();
<span class="hljs-variable">$date</span>-&gt;<span class="hljs-title function_ invoke__">format</span>(<span class="hljs-string">&#x27;Y-m-d&#x27;</span>);

DateTimeImmutable::<span class="hljs-title function_ invoke__">createFromMutable</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">\DateTime</span>(<span class="hljs-string">&#x27;now&#x27;</span>));

<span class="hljs-title function_ invoke__">str_contains</span> (\<span class="hljs-title function_ invoke__">strtoupper</span>(<span class="hljs-title function_ invoke__">substr</span>(<span class="hljs-string">&#x27;abcdef&#x27;</span>, -<span class="hljs-number">2</span>), <span class="hljs-string">&#x27;f&#x27;</span>));

<span class="hljs-comment">/**
* Function declaration
*/</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testMe</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>|<span class="hljs-keyword">int</span> <span class="hljs-variable">$name</span></span>): <span class="hljs-title">int</span>
</span>{
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">empty</span>(<span class="hljs-variable">$name</span>)) {
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
} <span class="hljs-keyword">elseif</span> (<span class="hljs-variable">$name</span> === <span class="hljs-number">1</span>) {
<span class="hljs-keyword">return</span> (<span class="hljs-keyword">int</span>) <span class="hljs-variable">$name</span>;
}

<span class="hljs-keyword">switch</span>(<span class="hljs-variable">$name</span>) {
<span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;2&#x27;</span>:
<span class="hljs-keyword">return</span> <span class="hljs-number">2</span>;
<span class="hljs-keyword">default</span>:
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">\Exception</span>(<span class="hljs-string">&#x27;error&#x27;</span>);
}
}

<span class="hljs-comment">/**
* First-class Callable Syntax
*/</span>
<span class="hljs-variable">$fun</span> = <span class="hljs-title function_ invoke__">mb_strlen</span>();
<span class="hljs-variable">$fun</span>();
35 changes: 35 additions & 0 deletions test/markup/php/functions.txt
Expand Up @@ -6,3 +6,38 @@ $fn1 = fn($x) => $x + $y;
$fn2 = function ($x) use ($y) {
return $x + $y;
};

/**
* Function invoke
*/
$date = new DateTimeImmutable ();
$date->format('Y-m-d');

DateTimeImmutable::createFromMutable(new \DateTime('now'));

str_contains (\strtoupper(substr('abcdef', -2), 'f'));

/**
* Function declaration
*/
function testMe(string|int $name): int
{
if (empty($name)) {
return 0;
} elseif ($name === 1) {
return (int) $name;
}

switch($name) {
case '2':
return 2;
default:
throw new \Exception('error');
}
}

/**
* First-class Callable Syntax
*/
$fun = mb_strlen();
$fun();
4 changes: 2 additions & 2 deletions test/markup/php/strings.expect.txt
Expand Up @@ -12,12 +12,12 @@ MSG</span>);

<span class="hljs-comment">// heredoc syntax</span>

var_dump(<span class="hljs-string">&lt;&lt;&lt;SQL
<span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-string">&lt;&lt;&lt;SQL
SELECT *
FROM table
SQL</span>);

var_dump(<span class="hljs-string">&lt;&lt;&lt;SQL
<span class="hljs-title function_ invoke__">var_dump</span>(<span class="hljs-string">&lt;&lt;&lt;SQL
SELECT *
FROM table
SQL</span>);