Skip to content

Commit

Permalink
Fix the PHP language
Browse files Browse the repository at this point in the history
This patch is an attempt to fix the PHP language, when it is combined
with markup.

The problem is, that markup has a higher priority than all other tokens.
This leads to weird errors, where HTML tags are highlighted inside of
comments PrismJS#197. One solution to this was to set the `greedy` flag for the
comment token, but this leads to far worse errors like PrismJS#1097.

This patch should fix both issues PrismJS#197 and PrismJS#1097, by switching the
grammar to markup on the fly. One potential problem is, that it relies
on the `<?php` tag to detect if markup is present. So if a PHP file
contains only markup and no PHP code at all, the result will look
broken.
  • Loading branch information
zeitgeist87 committed Mar 10, 2017
1 parent 1f699e7 commit 76698cd
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 69 deletions.
45 changes: 20 additions & 25 deletions components/prism-php.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ Prism.languages.php = Prism.languages.extend('clike', {
'constant': /\b[A-Z0-9_]{2,}\b/,
'comment': {
pattern: /(^|[^\\])(?:\/\*[\w\W]*?\*\/|\/\/.*)/,
lookbehind: true,
greedy: true
lookbehind: true
}
});

Expand All @@ -32,7 +31,10 @@ Prism.languages.insertBefore('php', 'class-name', {
});

Prism.languages.insertBefore('php', 'keyword', {
'delimiter': /\?>|<\?(?:php)?/i,
'delimiter': {
pattern: /\?>|<\?(?:php)?/i,
alias: 'important'
},
'variable': /\$\w+\b/i,
'package': {
pattern: /(\\|namespace\s+|use\s+)[\w\\]+/,
Expand All @@ -51,61 +53,54 @@ Prism.languages.insertBefore('php', 'operator', {
}
});

// Add HTML support of the markup language exists
// Add HTML support if the markup language exists
if (Prism.languages.markup) {

// Tokenize all inline PHP blocks that are wrapped in <?php ?>
// This allows for easy PHP + markup highlighting
Prism.hooks.add('before-highlight', function(env) {
if (env.language !== 'php') {
if (env.language !== 'php' || !/(?:<\?php|<\?)/ig.test(env.code)) {
return;
}

env.tokenStack = [];

env.backupCode = env.code;
env.code = env.code.replace(/(?:<\?php|<\?)[\w\W]*?(?:\?>)/ig, function(match) {
env.code = env.code.replace(/(?:<\?php|<\?)[\w\W]*?(?:\?>|$)/ig, function(match) {
env.tokenStack.push(match);

return '{{{PHP' + env.tokenStack.length + '}}}';
});

// Switch the grammar to markup
env.grammar = Prism.languages.markup;
});

// Restore env.code for other plugins (e.g. line-numbers)
Prism.hooks.add('before-insert', function(env) {
if (env.language === 'php') {
if (env.language === 'php' && env.backupCode) {
env.code = env.backupCode;
delete env.backupCode;
}
});

// Re-insert the tokens after highlighting
Prism.hooks.add('after-highlight', function(env) {
if (env.language !== 'php') {
if (env.language !== 'php' || !env.tokenStack) {
return;
}

// Switch the grammar back
env.grammar = Prism.languages.php;

for (var i = 0, t; t = env.tokenStack[i]; i++) {
// The replace prevents $$, $&, $`, $', $n, $nn from being interpreted as special patterns
env.highlightedCode = env.highlightedCode.replace('{{{PHP' + (i + 1) + '}}}', Prism.highlight(t, env.grammar, 'php').replace(/\$/g, '$$$$'));
env.highlightedCode = env.highlightedCode.replace('{{{PHP' + (i + 1) + '}}}',
"<span class=\"token php\">" +
Prism.highlight(t, env.grammar, 'php').replace(/\$/g, '$$$$') +
"</span>");
}

env.element.innerHTML = env.highlightedCode;
});

// Wrap tokens in classes that are missing them
Prism.hooks.add('wrap', function(env) {
if (env.language === 'php' && env.type === 'markup') {
env.content = env.content.replace(/(\{\{\{PHP[0-9]+\}\}\})/g, "<span class=\"token php\">$1</span>");
}
});

// Add the rules before all others
Prism.languages.insertBefore('php', 'comment', {
'markup': {
pattern: /<[^?]\/?(.*?)>/,
inside: Prism.languages.markup
},
'php': /\{\{\{PHP[0-9]+\}\}\}/
});
}
2 changes: 1 addition & 1 deletion components/prism-php.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/helper/test-case.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ module.exports = {
code: code
};
Prism.hooks.run('before-highlight', env);
env.highlightedCode = Prism.highlight(env.code, Prism.languages[usedLanguages.mainLanguage], usedLanguages.mainLanguage);
env.highlightedCode = Prism.highlight(env.code, env.grammar, env.language);
Prism.hooks.run('before-insert', env);
env.element.innerHTML = env.highlightedCode;
Prism.hooks.run('after-highlight', env);
Expand Down
37 changes: 0 additions & 37 deletions tests/languages/markup+php/markup_feature.test

This file was deleted.

8 changes: 4 additions & 4 deletions tests/languages/markup+php/php_in_markup_feature.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion tests/languages/php/comment_feature.test
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
/**/
/* foo
bar */
/* <me@example.com> */

----------------------------------------------------

[
["comment", "//"],
["comment", "// foobar"],
["comment", "/**/"],
["comment", "/* foo\r\nbar */"]
["comment", "/* foo\r\nbar */"],
["comment", "/* <me@example.com> */"]
]

----------------------------------------------------
Expand Down
13 changes: 13 additions & 0 deletions tests/languages/php/string_feature.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"https://example.com"
" /* not a comment */ "

----------------------------------------------------

[
["string", "\"https://example.com\""],
["string", "\" /* not a comment */ \""]
]

----------------------------------------------------

Checks for strings with comments.

0 comments on commit 76698cd

Please sign in to comment.