Skip to content

(fix) When ignoring a potential match highlighting can terminate early #2650

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

Merged
merged 4 commits into from
Sep 5, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Version 10.2.0 (next up)

Parser Engine:

- (fix) When ignoring a potential match highlighting can terminate early (#2649) [Josh Goebel][]

New themes:

- *Gradient Light* by [Samia Ali]()
Expand Down
25 changes: 21 additions & 4 deletions src/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,14 @@ const HLJS = function(hljs) {
}
}

/**
* Advance a single character
*/
function advanceOne() {
mode_buffer += codeToHighlight[index];
index += 1;
}

/**
* Handle matching but then ignoring a sequence of text
*
Expand All @@ -275,7 +283,7 @@ const HLJS = function(hljs) {
} else {
// no need to move the cursor, we still have additional regexes to try and
// match at this very spot
continueScanAtSamePosition = true;
resumeScanAtSamePosition = true;
return 0;
}
}
Expand Down Expand Up @@ -478,23 +486,32 @@ const HLJS = function(hljs) {
var relevance = 0;
var index = 0;
var iterations = 0;
var continueScanAtSamePosition = false;
var resumeScanAtSamePosition = false;

try {
top.matcher.considerAll();

for (;;) {
iterations++;
if (continueScanAtSamePosition) {
if (resumeScanAtSamePosition) {
// only regexes not matched previously will now be
// considered for a potential match
continueScanAtSamePosition = false;
resumeScanAtSamePosition = false;
} else {
top.matcher.lastIndex = index;
top.matcher.considerAll();
}

const match = top.matcher.exec(codeToHighlight);
// console.log("match", match[0], match.rule && match.rule.begin)

// if our failure to match was the result of a "resumed scan" then we
// need to advance one position and revert to full scanning before we
// decide there are truly no more matches at all to be had
if (!match && top.matcher.resumingScanAtSamePosition()) {
advanceOne();
continue;
}
if (!match) break;

const beforeMatch = codeToHighlight.substring(index, match.index);
Expand Down
4 changes: 4 additions & 0 deletions src/lib/mode_compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ export function compileLanguage(language) {
return matcher;
}

resumingScanAtSamePosition() {
return this.regexIndex != 0;
}

considerAll() {
this.regexIndex = 0;
}
Expand Down
4 changes: 3 additions & 1 deletion test/parser/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
'use strict';

describe('hljs', function() {
require('./look-ahead-end-matchers');
require('./resume-scan');
require('./reuse-endsWithParent');
require('./should-not-destroyData');
require('./look-ahead-end-matchers');

});
13 changes: 13 additions & 0 deletions test/parser/resume-scan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const hljs = require('../../build');

describe("bugs", function () {

describe("resume scan when a match is ignored", () => {
it("should continue to highlight later matches", () => {
hljs.highlight('java', 'ImmutablePair.of(Stuff.class, "bar")').value
.should.equal(
'ImmutablePair.of(Stuff.class, <span class="hljs-string">&quot;bar&quot;</span>)'
)
})
})
})