Skip to content

Commit

Permalink
Merge branch 'skip'
Browse files Browse the repository at this point in the history
  • Loading branch information
isagalaev committed Feb 2, 2016
2 parents e672c7d + e5d2123 commit a3c5aba
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 85 deletions.
26 changes: 26 additions & 0 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -296,3 +296,29 @@ The value of the attribute controls which language or languages will be used for
* language name: explicit highlighting with the specified language
* empty array: auto detection with all the languages available
* array of language names: auto detection constrained to the specified set

skip
^^^^

**type**: boolean

Skips any markup processing for the mode ensuring that it remains a part of its
parent buffer along with the starting and the ending lexemes. This works in
conjunction with the parent's :ref:`subLanguage` when it requires complex
parsing.

Consider parsing PHP inside HTML::

<p><? echo 'PHP'; /* ?> */ ?></p>

The ``?>`` inside the comment should **not** end the PHP part, so we have to
handle pairs of ``/* .. */`` to correctly find the ending ``?>``::

{
begin: /<\?/, end: /\?>/,
subLanguage: 'php',
contains: [{begin: '/\\*', end: '\\*/', skip: true}]
}

Without ``skip: true`` every comment would cause the parser to drop out back
into the HTML mode.
49 changes: 28 additions & 21 deletions src/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,57 +373,64 @@ https://highlightjs.org/
}

function processBuffer() {
return top.subLanguage !== undefined ? processSubLanguage() : processKeywords();
result += (top.subLanguage !== undefined ? processSubLanguage() : processKeywords());
mode_buffer = '';
}

function startNewMode(mode, lexeme) {
var markup = mode.className? buildSpan(mode.className, '', true): '';
if (mode.returnBegin) {
result += markup;
mode_buffer = '';
} else if (mode.excludeBegin) {
result += escape(lexeme) + markup;
mode_buffer = '';
} else {
result += markup;
mode_buffer = lexeme;
}
result += mode.className? buildSpan(mode.className, '', true): '';
top = Object.create(mode, {parent: {value: top}});
}

function processLexeme(buffer, lexeme) {

mode_buffer += buffer;

if (lexeme === undefined) {
result += processBuffer();
processBuffer();
return 0;
}

var new_mode = subMode(lexeme, top);
if (new_mode) {
result += processBuffer();
if (new_mode.skip) {
mode_buffer += lexeme;
} else {
if (new_mode.excludeBegin) {
mode_buffer += lexeme;
}
processBuffer();
if (!new_mode.returnBegin && !new_mode.excludeBegin) {
mode_buffer = lexeme;
}
}
startNewMode(new_mode, lexeme);
return new_mode.returnBegin ? 0 : lexeme.length;
}

var end_mode = endOfMode(top, lexeme);
if (end_mode) {
var origin = top;
if (!(origin.returnEnd || origin.excludeEnd)) {
if (origin.skip) {
mode_buffer += lexeme;
} else {
if (!(origin.returnEnd || origin.excludeEnd)) {
mode_buffer += lexeme;
}
processBuffer();
if (origin.excludeEnd) {
mode_buffer = lexeme;
}
}
result += processBuffer();
do {
if (top.className) {
result += '</span>';
}
relevance += top.relevance;
if (!top.skip) {
relevance += top.relevance;
}
top = top.parent;
} while (top != end_mode.parent);
if (origin.excludeEnd) {
result += escape(lexeme);
}
mode_buffer = '';
if (end_mode.starts) {
startNewMode(end_mode.starts, '');
}
Expand Down
9 changes: 6 additions & 3 deletions src/languages/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,12 @@ function(hljs) {
hljs.C_BLOCK_COMMENT_MODE,
hljs.REGEXP_MODE,
{ // E4X / JSX
begin: /</, end: />\s*[);\]]/,
relevance: 0,
subLanguage: 'xml'
begin: /</, end: /(\/\w+|\w+\/)>/,
subLanguage: 'xml',
contains: [
{begin: /<\w+\/>/, skip: true},
{begin: /<\w+/, end: /(\/\w+|\w+\/)>/, skip: true, contains: ['self']}
]
}
],
relevance: 0
Expand Down
18 changes: 18 additions & 0 deletions src/languages/php-html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Language: PHP in HTML
Requires: php.js, xml.js
Category: common
*/

function(hljs) {
return {
subLanguage: 'xml',
contains: [
{
begin: /<\?(php)?/, end: /\?>/,
subLanguage: 'php',
contains: [{begin: '/\\*', end: '\\*/', skip: true}]
}
]
}
}
5 changes: 2 additions & 3 deletions src/languages/php.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ function(hljs) {
'trait goto instanceof insteadof __DIR__ __NAMESPACE__ ' +
'yield finally',
contains: [
hljs.C_LINE_COMMENT_MODE,
hljs.HASH_COMMENT_MODE,
hljs.COMMENT('//', '$', {contains: [PREPROCESSOR]}),
hljs.COMMENT(
'/\\*',
'\\*/',
Expand All @@ -50,8 +50,7 @@ function(hljs) {
{
className: 'doctag',
begin: '@[A-Za-z]+'
},
PREPROCESSOR
}
]
}
),
Expand Down
13 changes: 4 additions & 9 deletions src/languages/xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,11 @@ Category: common

function(hljs) {
var XML_IDENT_RE = '[A-Za-z0-9\\._:-]+';
var PHP = {
begin: /<\?(php)?(?!\w)/, end: /\?>/,
subLanguage: 'php'
};
var TAG_INTERNALS = {
endsWithParent: true,
illegal: /</,
relevance: 0,
contains: [
PHP,
{
className: 'attr',
begin: XML_IDENT_RE,
Expand All @@ -26,7 +21,6 @@ function(hljs) {
contains: [
{
className: 'string',
contains: [PHP],
variants: [
{begin: /"/, end: /"/},
{begin: /'/, end: /'/},
Expand Down Expand Up @@ -85,11 +79,12 @@ function(hljs) {
subLanguage: ['actionscript', 'javascript', 'handlebars', 'xml']
}
},
PHP,
{
className: 'meta',
begin: /<\?\w+/, end: /\?>/,
relevance: 10
variants: [
{begin: /<\?xml/, end: /\?>/, relevance: 10},
{begin: /<\?\w+/, end: /\?>/}
]
},
{
className: 'tag',
Expand Down
3 changes: 3 additions & 0 deletions test/detect/php-html/default.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<p>HTML</p>
<?php echo 'PHP' ?>
<hr>
6 changes: 3 additions & 3 deletions test/fixtures/expect/sublanguages.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<span class="php"><span class="hljs-meta">&lt;?</span> <span class="hljs-keyword">echo</span> <span class="hljs-string">'php'</span>; <span class="hljs-comment">/* <span class="hljs-meta">?&gt;</span></span></span>
<span class="xml"></span><span class="php"><span class="hljs-meta">&lt;?</span> <span class="hljs-keyword">echo</span> <span class="hljs-string">'php'</span>; <span class="hljs-comment">/* ?&gt; */</span> <span class="hljs-meta">?&gt;</span></span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="actionscript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tf</span> <span class="hljs-params">(x)</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-keyword">typeof</span> x; }</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"><span class="hljs-built_in">document</span>.write(<span class="hljs-string">'Legacy code'</span>);</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="php"><span class="hljs-comment"><span class="hljs-meta">&lt;?</span> end of comment */</span> <span class="hljs-meta">?&gt;</span></span>
</span>
5 changes: 2 additions & 3 deletions test/fixtures/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,10 @@
</div>

<!-- sub-languages -->
<pre><code id="sublanguages" class="xml">&lt;? echo 'php'; /* ?&gt;
<pre><code id="sublanguages" class="php-html">&lt;? echo 'php'; /* ?&gt; */ ?&gt;
&lt;body&gt;
&lt;script&gt;function tf (x) { return typeof x; }&lt;/script&gt;
&lt;script&gt;document.write('Legacy code');&lt;/script&gt;
&lt;/body&gt;
&lt;? end of comment */ ?&gt;
</code></pre>

<div id="build-classname">
Expand Down
26 changes: 5 additions & 21 deletions test/markup/javascript/jsx.expect.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
<span class="hljs-keyword">var</span> X = React.createClass({
render: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">var</span> c = <span class="hljs-keyword">this</span>.map(c =&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Comment</span> <span class="hljs-attr">comment</span>=<span class="hljs-string">{c}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{c.id}</span> /&gt;</span>)</span>;
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'comments'</span>&gt;</span>
{c}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
)</span>;
},
foo: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{}
});

<span class="hljs-keyword">var</span> Comment = React.createClass({
render: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">var</span> comment = <span class="hljs-keyword">this</span>.props.comment;
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>{comment}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
)</span>;
}
foo: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{}
});
<span class="hljs-keyword">var</span> jsx = <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">node</span>/&gt;</span></span>;
<span class="hljs-keyword">var</span> jsx = <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">node</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">child</span>/&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">node</span>&gt;</span></span>;
<span class="hljs-keyword">var</span> jsx = <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">node</span>&gt;</span>...<span class="hljs-tag">&lt;<span class="hljs-name">child</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">child</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">node</span>&gt;</span></span>;
<span class="hljs-keyword">var</span> x = <span class="hljs-number">5</span>;
<span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">node</span> <span class="hljs-attr">attr</span>=<span class="hljs-string">"value"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">node</span>&gt;</span></span>);
26 changes: 5 additions & 21 deletions test/markup/javascript/jsx.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
var X = React.createClass({
render: function() {
var c = this.map(c => <Comment comment={c} key={c.id} />);
return (
<div className='comments'>
{c}
</div>
);
},
foo: function() {}
});

var Comment = React.createClass({
render: function() {
var comment = this.props.comment;
return (
<li>{comment}</li>
);
}
foo: function() {}
});
var jsx = <node/>;
var jsx = <node><child/></node>;
var jsx = <node>...<child>...</child></node>;
var x = 5;
return (<node attr="value"></node>);
2 changes: 1 addition & 1 deletion test/utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ exports.numberToString = _.method('toString');

exports.expectedFile = function(filename, encoding, actual) {
return readFile(filename, encoding)
.then(expected => actual.should.equal(expected));
.then(expected => actual.trim().should.equal(expected.trim()));
};

exports.setupFile = function(filename, encoding, that, testHTML) {
Expand Down

0 comments on commit a3c5aba

Please sign in to comment.