Skip to content
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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Dev Improvements:

Parser:

- keywords now have a maximum # of times they provide relevance (#3129) [Josh Goebel][]
- enh(api) add `unregisterLanguage` method (#3009) [Antoine du Hamel][]
- enh: Make alias registration case insensitive (#3026) [David Ostrovsky][]
- fix(parser) `highlightAll()` now works if the library is lazy loaded [Josh Goebel][]
Expand Down
16 changes: 10 additions & 6 deletions src/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import * as logger from "./lib/logger.js";
const escape = utils.escapeHTML;
const inherit = utils.inherit;
const NO_MATCH = Symbol("nomatch");
const MAX_KEYWORD_HITS = 7;

/**
* @param {any} hljs - object that is extended (legacy)
Expand Down Expand Up @@ -162,15 +163,16 @@ const HLJS = function(hljs) {
* @returns {HighlightResult} - result of the highlight operation
*/
function _highlight(languageName, codeToHighlight, ignoreIllegals, continuation) {
const keywordHits = Object.create(null);

/**
* Return keyword data if a match is a keyword
* @param {CompiledMode} mode - current mode
* @param {RegExpMatchArray} match - regexp match data
* @param {string} matchText - the textual match
* @returns {KeywordData | false}
*/
function keywordData(mode, match) {
const matchText = language.case_insensitive ? match[0].toLowerCase() : match[0];
return Object.prototype.hasOwnProperty.call(mode.keywords, matchText) && mode.keywords[matchText];
function keywordData(mode, matchText) {
return mode.keywords[matchText];
}

function processKeywords() {
Expand All @@ -186,13 +188,15 @@ const HLJS = function(hljs) {

while (match) {
buf += modeBuffer.substring(lastIndex, match.index);
const data = keywordData(top, match);
const word = language.case_insensitive ? match[0].toLowerCase() : match[0];
const data = keywordData(top, word);
if (data) {
const [kind, keywordRelevance] = data;
emitter.addText(buf);
buf = "";

relevance += keywordRelevance;
keywordHits[word] = (keywordHits[word] || 0) + 1;
if (keywordHits[word] <= MAX_KEYWORD_HITS) relevance += keywordRelevance;
if (kind.startsWith("_")) {
// _ implied for relevance only, do not highlight
// by applying a class name
Expand Down
2 changes: 1 addition & 1 deletion src/lib/compile_keywords.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const DEFAULT_KEYWORD_CLASSNAME = "keyword";
*/
export function compileKeywords(rawKeywords, caseInsensitive, className = DEFAULT_KEYWORD_CLASSNAME) {
/** @type KeywordDict */
const compiledKeywords = {};
const compiledKeywords = Object.create(null);

// input can be a string of keywords, an array of keywords, or a object with
// named keys representing className (which can then point to a string or array)
Expand Down
1 change: 1 addition & 0 deletions test/parser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ describe('hljs', function() {
require('./reuse-endsWithParent');
require('./should-not-destroyData');
require('./compiler-extensions');
require('./max_keyword_hits');
});
19 changes: 19 additions & 0 deletions test/parser/max_keyword_hits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const hljs = require('../../build');

describe("max keyword hits", function() {
it("should count a keyword 7 times for relevance, no more", () => {
hljs.registerLanguage('test-language', (hljs) => {
return {
keywords: "bob suzy|2"
};
});

let result = hljs.highlight('bob bob bob bob bob bob bob bob bob bob bob bob bob', { language: 'test-language' });
result.relevance.should.equal(7);

result = hljs.highlight('suzy suzy suzy suzy suzy suzy suzy suzy suzy suzy suzy suzy suzy', { language: 'test-language' });
result.relevance.should.equal(14);

hljs.unregisterLanguage("test-language");
});
});