Skip to content

Commit e63058f

Browse files
VitaliyRGolmote
authored andcommitted
Fixes to compatibility of line number and line higlight plugins (#1194)
* Fix line breaks issues in line numbers plugin used with soft wraps option * Fix line highlight plugin compatibility issues with line number plugin * Revert rename data attribute for line numbers plugin * Fix compatibility issues with line highlight and line numbers
1 parent ac219d7 commit e63058f

File tree

3 files changed

+100
-21
lines changed

3 files changed

+100
-21
lines changed

plugins/line-highlight/prism-line-highlight.css

+5
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,8 @@ pre[data-line] {
4242
top: auto;
4343
bottom: .4em;
4444
}
45+
46+
.line-numbers .line-highlight:before,
47+
.line-numbers .line-highlight:after {
48+
content: none;
49+
}

plugins/line-highlight/prism-line-highlight.js

+41-12
Original file line numberDiff line numberDiff line change
@@ -36,37 +36,53 @@ var isLineHeightRounded = (function() {
3636
}());
3737

3838
function highlightLines(pre, lines, classes) {
39+
lines = typeof lines === 'string' ? lines : pre.getAttribute('data-line');
40+
3941
var ranges = lines.replace(/\s+/g, '').split(','),
4042
offset = +pre.getAttribute('data-line-offset') || 0;
4143

4244
var parseMethod = isLineHeightRounded() ? parseInt : parseFloat;
4345
var lineHeight = parseMethod(getComputedStyle(pre).lineHeight);
46+
var hasLineNumbers = hasClass(pre, 'line-numbers');
4447

45-
for (var i=0, range; range = ranges[i++];) {
46-
range = range.split('-');
48+
for (var i=0, currentRange; currentRange = ranges[i++];) {
49+
var range = currentRange.split('-');
4750

4851
var start = +range[0],
4952
end = +range[1] || start;
5053

51-
var line = document.createElement('div');
54+
var line = pre.querySelector('.line-highlight[data-range="' + currentRange + '"]') || document.createElement('div');
5255

53-
line.textContent = Array(end - start + 2).join(' \n');
5456
line.setAttribute('aria-hidden', 'true');
57+
line.setAttribute('data-range', currentRange);
5558
line.className = (classes || '') + ' line-highlight';
5659

5760
//if the line-numbers plugin is enabled, then there is no reason for this plugin to display the line numbers
58-
if(!hasClass(pre, 'line-numbers')) {
61+
if(hasLineNumbers && Prism.plugins.lineNumbers) {
62+
var startNode = Prism.plugins.lineNumbers.getLine(pre, start);
63+
var endNode = Prism.plugins.lineNumbers.getLine(pre, end);
64+
65+
if (startNode) {
66+
line.style.top = startNode.offsetTop + 'px';
67+
}
68+
69+
if (endNode) {
70+
line.style.height = (endNode.offsetTop - startNode.offsetTop) + endNode.offsetHeight + 'px';
71+
}
72+
} else {
5973
line.setAttribute('data-start', start);
6074

6175
if(end > start) {
6276
line.setAttribute('data-end', end);
6377
}
64-
}
78+
79+
line.style.top = (start - offset - 1) * lineHeight + 'px';
6580

66-
line.style.top = (start - offset - 1) * lineHeight + 'px';
81+
line.textContent = new Array(end - start + 2).join(' \n');
82+
}
6783

6884
//allow this to play nicely with the line-numbers plugin
69-
if(hasClass(pre, 'line-numbers')) {
85+
if(hasLineNumbers) {
7086
//need to attack to pre as when line-numbers is enabled, the code tag is relatively which screws up the positioning
7187
pre.appendChild(line);
7288
} else {
@@ -133,7 +149,7 @@ Prism.hooks.add('before-sanity-check', function(env) {
133149
}
134150
});
135151

136-
Prism.hooks.add('complete', function(env) {
152+
Prism.hooks.add('complete', function completeHook(env) {
137153
var pre = env.element.parentNode;
138154
var lines = pre && pre.getAttribute('data-line');
139155

@@ -142,11 +158,24 @@ Prism.hooks.add('complete', function(env) {
142158
}
143159

144160
clearTimeout(fakeTimer);
145-
highlightLines(pre, lines);
146161

147-
fakeTimer = setTimeout(applyHash, 1);
162+
var hasLineNumbers = Prism.plugins.lineNumbers;
163+
var isLineNumbersLoaded = env.plugins && env.plugins.lineNumbers;
164+
165+
if (hasLineNumbers && !isLineNumbersLoaded) {
166+
Prism.hooks.add('line-numbers', completeHook);
167+
} else {
168+
highlightLines(pre, lines);
169+
fakeTimer = setTimeout(applyHash, 1);
170+
}
148171
});
149172

150173
window.addEventListener('hashchange', applyHash);
174+
window.addEventListener('resize', function () {
175+
var preElements = document.querySelectorAll('pre[data-line]');
176+
Array.prototype.forEach.call(preElements, function (pre) {
177+
highlightLines(pre);
178+
});
179+
});
151180

152-
})();
181+
})();

plugins/line-numbers/prism-line-numbers.js

+54-9
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@
55
}
66

77
/**
8-
* Class name for <pre> which is activating the plugin
8+
* Plugin name which is used as a class name for <pre> which is activating the plugin
99
* @type {String}
1010
*/
11-
var PLUGIN_CLASS = 'line-numbers';
11+
var PLUGIN_NAME = 'line-numbers';
12+
13+
/**
14+
* Regular expression used for determining line breaks
15+
* @type {RegExp}
16+
*/
17+
var NEW_LINE_EXP = /\n(?!$)/g;
1218

1319
/**
1420
* Resizes line numbers spans according to height of line of code
15-
* @param {Element} element <pre> element
21+
* @param {Element} element <pre> element
1622
*/
1723
var _resizeElement = function (element) {
1824
var codeStyles = getStyles(element);
@@ -22,7 +28,7 @@
2228
var codeElement = element.querySelector('code');
2329
var lineNumbersWrapper = element.querySelector('.line-numbers-rows');
2430
var lineNumberSizer = element.querySelector('.line-numbers-sizer');
25-
var codeLines = element.textContent.split('\n');
31+
var codeLines = codeElement.textContent.split(NEW_LINE_EXP);
2632

2733
if (!lineNumberSizer) {
2834
lineNumberSizer = document.createElement('span');
@@ -57,7 +63,7 @@
5763
};
5864

5965
window.addEventListener('resize', function () {
60-
Array.prototype.forEach.call(document.querySelectorAll('pre.' + PLUGIN_CLASS), _resizeElement);
66+
Array.prototype.forEach.call(document.querySelectorAll('pre.' + PLUGIN_NAME), _resizeElement);
6167
});
6268

6369
Prism.hooks.add('complete', function (env) {
@@ -76,21 +82,21 @@
7682
return;
7783
}
7884

79-
if (env.element.querySelector(".line-numbers-rows")) {
85+
if (env.element.querySelector('.line-numbers-rows')) {
8086
// Abort if line numbers already exists
8187
return;
8288
}
8389

8490
if (clsReg.test(env.element.className)) {
85-
// Remove the class "line-numbers" from the <code>
91+
// Remove the class 'line-numbers' from the <code>
8692
env.element.className = env.element.className.replace(clsReg, ' ');
8793
}
8894
if (!clsReg.test(pre.className)) {
89-
// Add the class "line-numbers" to the <pre>
95+
// Add the class 'line-numbers' to the <pre>
9096
pre.className += ' line-numbers';
9197
}
9298

93-
var match = env.code.match(/\n(?!$)/g);
99+
var match = env.code.match(NEW_LINE_EXP);
94100
var linesNum = match ? match.length + 1 : 1;
95101
var lineNumbersWrapper;
96102

@@ -109,6 +115,45 @@
109115
env.element.appendChild(lineNumbersWrapper);
110116

111117
_resizeElement(pre);
118+
119+
Prism.hooks.run('line-numbers', env);
120+
});
121+
122+
Prism.hooks.add('line-numbers', function (env) {
123+
env.plugins = env.plugins || {};
124+
env.plugins.lineNumbers = true;
112125
});
126+
127+
/**
128+
* Global exports
129+
*/
130+
Prism.plugins.lineNumbers = {
131+
/**
132+
* Get node for provided line number
133+
* @param {Element} element pre element
134+
* @param {Number} number line number
135+
* @return {Element|undefined}
136+
*/
137+
getLine: function (element, number) {
138+
if (element.tagName !== 'PRE' || !element.classList.contains(PLUGIN_NAME)) {
139+
return;
140+
}
141+
142+
var lineNumberRows = element.querySelector('.line-numbers-rows');
143+
var lineNumberStart = parseInt(element.getAttribute('data-start'), 10) || 1;
144+
var lineNumberEnd = lineNumberStart + (lineNumberRows.children.length - 1);
145+
146+
if (number < lineNumberStart) {
147+
number = lineNumberStart;
148+
}
149+
if (number > lineNumberEnd) {
150+
number = lineNumberEnd;
151+
}
152+
153+
var lineIndex = number - lineNumberStart;
154+
155+
return lineNumberRows.children[lineIndex];
156+
}
157+
};
113158

114159
}());

0 commit comments

Comments
 (0)