diff --git a/doc/manual.html b/doc/manual.html index fd6729770030c8c53d836a3b34226ff9126c91e0..3f591492b6d84c41b23ca01e746f6371cfd4aec7 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -387,9 +387,21 @@ <dd>When highlighting long lines, in order to stay responsive, the editor will give up and simply style the rest of the line as plain text when it reaches a certain position. The default is - 10000. You can set this to <code>Infinity</code> to turn off + 10 000. You can set this to <code>Infinity</code> to turn off this behavior.</dd> + <dt id="option_crudeMeasuringFrom"><code><strong>crudeMeasuringFrom</strong>: number</code></dt> + <dd>When measuring the character positions in long lines, any + line longer than this number (default is 10 000), + when <a href="#option_lineWrapping">line wrapping</a> + is <strong>off</strong>, will simply be assumed to consist of + same-sized characters. This means that, on the one hand, + measuring will be inaccurate when characters of varying size, + right-to-left text, markers, or other irregular elements are + present. On the other hand, it means that having such a line + won't freeze the user interface because of the expensiveness of + the measurements.</dd> + <dt id="option_viewportMargin"><code><strong>viewportMargin</strong>: integer</code></dt> <dd>Specifies the amount of lines that are rendered above and below the part of the document that's currently scrolled into diff --git a/lib/codemirror.js b/lib/codemirror.js index eb393c29b2c3d6869cabfa850a5c652f9ed1e667..f7e4a0e69b1029bf71a6dd0da678a7b532eb4cd7 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -972,6 +972,10 @@ window.CodeMirror = (function() { function measureChar(cm, line, ch, data, bias) { var dir = -1; data = data || measureLine(cm, line); + if (data.crude) { + var left = data.left + ch * data.width; + return {left: left, right: left + data.width, top: data.top, bottom: data.bottom}; + } for (var pos = ch;; pos += dir) { var r = data[pos]; @@ -1020,6 +1024,9 @@ window.CodeMirror = (function() { } function measureLineInner(cm, line) { + if (!cm.options.lineWrapping && line.text.length >= cm.options.crudeMeasuringFrom) + return crudelyMeasureLine(cm, line); + var display = cm.display, measure = emptyArray(line.text.length); var pre = lineContent(cm, line, measure, true); @@ -1106,6 +1113,15 @@ window.CodeMirror = (function() { return data; } + function crudelyMeasureLine(cm, line) { + var copy = new Line(line.text.slice(0, 100), null); + if (line.textClass) copy.textClass = line.textClass; + var measure = measureLineInner(cm, copy); + var left = measureChar(cm, copy, 0, measure, "left"); + var right = measureChar(cm, copy, 99, measure, "right"); + return {crude: true, top: left.top, left: left.left, bottom: left.bottom, width: (right.right - left.left) / 100}; + } + function measureLineWidth(cm, line) { var hasBadSpan = false; if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) { @@ -1113,7 +1129,8 @@ window.CodeMirror = (function() { if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true; } var cached = !hasBadSpan && findCachedMeasurement(cm, line); - if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right; + if (cached || line.text.length >= cm.options.crudeMeasuringFrom) + return measureChar(cm, line, line.text.length, cached && cached.measure, "right").right; var pre = lineContent(cm, line, null, true); var end = pre.appendChild(zeroWidthElement(cm.display.measure)); @@ -3273,6 +3290,7 @@ window.CodeMirror = (function() { option("historyEventDelay", 500); option("viewportMargin", 10, function(cm){cm.refresh();}, true); option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true); + option("crudeMeasuringFrom", 10000); option("moveInputWithCursor", true, function(cm, val) { if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0; });