diff --git a/src/autocomplete_test.js b/src/autocomplete_test.js index 8352f52f7e5..97251398ffb 100644 --- a/src/autocomplete_test.js +++ b/src/autocomplete_test.js @@ -26,16 +26,16 @@ module.exports = { editor.renderer.$themeId = "./theme/textmate"; editor.execCommand("insertstring", "a"); - checkInnerHTML('arraysortlocalalooooooooooooooooooooooooooooong_wordlocal', function() { + checkInnerHTML('arraysortlocalalooooooooooooooooooooooooooooong_wordlocal', function() { editor.execCommand("insertstring", "rr"); - checkInnerHTML('arraysortlocal', function() { + checkInnerHTML('arraysortlocal', function() { editor.execCommand("insertstring", "r"); - checkInnerHTML('arraysortlocal', function() { + checkInnerHTML('arraysortlocal', function() { editor.onCommandKey(null, 0, 13); assert.equal(editor.getValue(), "arraysort\narraysort alooooooooooooooooooooooooooooong_word"); editor.execCommand("insertstring", " looooooooooooooooooooooooooooong_"); - checkInnerHTML('alooooooooooooooooooooooooooooong_wordlocal', function() { + checkInnerHTML('alooooooooooooooooooooooooooooong_wordlocal', function() { editor.onCommandKey(null, 0, 13); editor.destroy(); editor.container.remove(); diff --git a/src/ext/static_highlight_test.js b/src/ext/static_highlight_test.js index f28d7833cca..e9f4a8cb690 100644 --- a/src/ext/static_highlight_test.js +++ b/src/ext/static_highlight_test.js @@ -44,10 +44,10 @@ module.exports = { + "
*\n
" + "
*/\n
" + "
\n
" - + "
function hello (a, b, c) {\n
" - + "
console.log(a * b + c + 'sup$');\n
" - + "
//\n
" - + "
//\n
" + + "
function hello (a, b, c) {\n
" + + "
console.log(a * b + c + 'sup$');\n
" + + "
//\n
" + + "
//\n
" + "
}\n
" + ""); assert.ok(!!result.css); @@ -97,7 +97,7 @@ module.exports = { var mode = new TextMode(); var result = highlighter.render(snippet, mode, theme); - assert.ok(result.html.indexOf("$'$1$2$$$&\n") != -1); + assert.ok(result.html.indexOf("$'$1$2$$$&\n") != -1); next(); }, @@ -108,7 +108,7 @@ module.exports = { var mode = new TextMode(); var result = highlighter.render(snippet, mode, theme); - assert.ok(result.html.indexOf("&<>'"\n") != -1); + assert.ok(result.html.indexOf("&<>'"\n") != -1); var mode = new JavaScriptMode(); var result = highlighter.render("/*" + snippet, mode, theme); diff --git a/src/layer/font_metrics.js b/src/layer/font_metrics.js index 2fbc2e44e40..459d982146a 100644 --- a/src/layer/font_metrics.js +++ b/src/layer/font_metrics.js @@ -5,30 +5,30 @@ var event = require("../lib/event"); var useragent = require("../lib/useragent"); var EventEmitter = require("../lib/event_emitter").EventEmitter; -var DEFAULT_CHAR_COUNT = 250; +var CHAR_COUNT = 512; var USE_OBSERVER = typeof ResizeObserver == "function"; var L = 200; -var FontMetrics = exports.FontMetrics = function(parentEl, charCount) { - this.charCount = charCount || DEFAULT_CHAR_COUNT; - +var FontMetrics = exports.FontMetrics = function(parentEl) { this.el = dom.createElement("div"); this.$setMeasureNodeStyles(this.el.style, true); - + this.$main = dom.createElement("div"); this.$setMeasureNodeStyles(this.$main.style); - + this.$measureNode = dom.createElement("div"); this.$setMeasureNodeStyles(this.$measureNode.style); - + + this.el.appendChild(this.$main); this.el.appendChild(this.$measureNode); parentEl.appendChild(this.el); - - this.$measureNode.textContent = lang.stringRepeat("X", this.charCount); - + + this.$measureNode.textContent = lang.stringRepeat("X", CHAR_COUNT); + this.$characterSize = {width: 0, height: 0}; - + + if (USE_OBSERVER) this.$addObserver(); else @@ -38,9 +38,9 @@ var FontMetrics = exports.FontMetrics = function(parentEl, charCount) { (function() { oop.implement(this, EventEmitter); - + this.$characterSize = {width: 0, height: 0}; - + this.$setMeasureNodeStyles = function(style, isRoot) { style.width = style.height = "auto"; style.left = style.top = "0px"; @@ -69,7 +69,7 @@ var FontMetrics = exports.FontMetrics = function(parentEl, charCount) { this._emit("changeCharacterSize", {data: size}); } }; - + this.$addObserver = function() { var self = this; this.$observer = new window.ResizeObserver(function(e) { @@ -83,13 +83,13 @@ var FontMetrics = exports.FontMetrics = function(parentEl, charCount) { if (this.$pollSizeChangesTimer || this.$observer) return this.$pollSizeChangesTimer; var self = this; - + return this.$pollSizeChangesTimer = event.onIdle(function cb() { self.checkForSizeChanges(); event.onIdle(cb, 500); }, 500); }; - + this.setPolling = function(val) { if (val) { this.$pollSizeChanges(); @@ -100,32 +100,24 @@ var FontMetrics = exports.FontMetrics = function(parentEl, charCount) { }; this.$measureSizes = function(node) { - node = node || this.$measureNode; - - // Avoid `Element.clientWidth` since it is rounded to an integer (see - // https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth). - // Using it here can result in a noticeable cursor offset for long lines. - const rect = node.getBoundingClientRect(); - const charSize = { - height: rect.height, - width: rect.width / this.charCount + var size = { + height: (node || this.$measureNode).clientHeight, + width: (node || this.$measureNode).clientWidth / CHAR_COUNT }; - + // Size and width can be null if the editor is not visible or // detached from the document - if (charSize.width === 0 || charSize.height === 0) + if (size.width === 0 || size.height === 0) return null; - return charSize; + return size; }; this.$measureCharWidth = function(ch) { - this.$main.textContent = lang.stringRepeat(ch, this.charCount); - // Avoid `Element.clientWidth` since it is rounded to an integer (see - // https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth). + this.$main.textContent = lang.stringRepeat(ch, CHAR_COUNT); var rect = this.$main.getBoundingClientRect(); - return rect.width / this.charCount; + return rect.width / CHAR_COUNT; }; - + this.getCharacterWidth = function(ch) { var w = this.charSizes[ch]; if (w === undefined) { @@ -142,7 +134,7 @@ var FontMetrics = exports.FontMetrics = function(parentEl, charCount) { this.el.parentNode.removeChild(this.el); }; - + this.$getZoom = function getZoom(element) { if (!element || !element.parentElement) return 1; return (window.getComputedStyle(element).zoom || 1) * getZoom(element.parentElement); @@ -179,7 +171,7 @@ var FontMetrics = exports.FontMetrics = function(parentEl, charCount) { if (!this.els) this.$initTransformMeasureNodes(); - + function p(el) { var r = el.getBoundingClientRect(); return [r.left, r.top]; @@ -194,7 +186,7 @@ var FontMetrics = exports.FontMetrics = function(parentEl, charCount) { var m1 = mul(1 + h[0], sub(b, a)); var m2 = mul(1 + h[1], sub(c, a)); - + if (elPos) { var x = elPos; var k = h[0] * x[0] / L + h[1] * x[1] / L + 1; @@ -205,5 +197,5 @@ var FontMetrics = exports.FontMetrics = function(parentEl, charCount) { var f = solve(sub(m1, mul(h[0], u)), sub(m2, mul(h[1], u)), u); return mul(L, f); }; - + }).call(FontMetrics.prototype); diff --git a/src/layer/text.js b/src/layer/text.js index 87f50ece9c7..aa3be47f9cf 100644 --- a/src/layer/text.js +++ b/src/layer/text.js @@ -27,8 +27,6 @@ var Text = function(parentEl) { this.SPACE_CHAR = "\xB7"; this.$padding = 0; this.MAX_LINE_LENGTH = 10000; - // Smaller chunks result in higher cursor precision at the cost of more DOM nodes - this.MAX_CHUNK_LENGTH = 250; this.$updateEolChar = function() { var doc = this.session.doc; @@ -293,7 +291,6 @@ var Text = function(parentEl) { lineEl.className = "ace_line_group"; } else { lineEl.className = "ace_line"; - lineEl.setAttribute("role", "option"); } fragment.push(line); @@ -323,19 +320,6 @@ var Text = function(parentEl) { "lparen": true }; - this.$renderTokenInChunks = function(parent, screenColumn, token, value) { - var newScreenColumn; - for (var i = 0; i < value.length; i += this.MAX_CHUNK_LENGTH) { - var valueChunk = value.substring(i, i + this.MAX_CHUNK_LENGTH); - var tokenChunk = { - type: token.type, - value: valueChunk - }; - newScreenColumn = this.$renderToken(parent, screenColumn + i, tokenChunk, valueChunk); - } - return newScreenColumn; - }; - this.$renderToken = function(parent, screenColumn, token, value) { var self = this; var re = /(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC\u2066\u2067\u2068\u202A\u202B\u202D\u202E\u202C\u2069]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g; @@ -401,16 +385,20 @@ var Text = function(parentEl) { valueFragment.appendChild(this.dom.createTextNode(i ? value.slice(i) : value, this.element)); - var span = this.dom.createElement("span"); if (!this.$textToken[token.type]) { var classes = "ace_" + token.type.replace(/\./g, " ace_"); + var span = this.dom.createElement("span"); if (token.type == "fold") span.style.width = (token.value.length * this.config.characterWidth) + "px"; span.className = classes; + span.appendChild(valueFragment); + + parent.appendChild(span); + } + else { + parent.appendChild(valueFragment); } - span.appendChild(valueFragment); - parent.appendChild(span); return screenColumn + value.length; }; @@ -577,11 +565,11 @@ var Text = function(parentEl) { } if (chars + value.length < splitChars) { - screenColumn = this.$renderTokenInChunks(lineEl, screenColumn, token, value); + screenColumn = this.$renderToken(lineEl, screenColumn, token, value); chars += value.length; } else { while (chars + value.length >= splitChars) { - screenColumn = this.$renderTokenInChunks( + screenColumn = this.$renderToken( lineEl, screenColumn, token, value.substring(0, splitChars - chars) ); @@ -599,7 +587,7 @@ var Text = function(parentEl) { } if (value.length != 0) { chars += value.length; - screenColumn = this.$renderTokenInChunks( + screenColumn = this.$renderToken( lineEl, screenColumn, token, value ); } @@ -621,23 +609,15 @@ var Text = function(parentEl) { if (!value) continue; } - if (screenColumn + value.length > this.MAX_LINE_LENGTH) { - this.$renderOverflowMessage(parent, screenColumn, token, value); - return; - } - screenColumn = this.$renderTokenInChunks(parent, screenColumn, token, value); + if (screenColumn + value.length > this.MAX_LINE_LENGTH) + return this.$renderOverflowMessage(parent, screenColumn, token, value); + screenColumn = this.$renderToken(parent, screenColumn, token, value); } }; this.$renderOverflowMessage = function(parent, screenColumn, token, value, hide) { - if (token) { - this.$renderTokenInChunks( - parent, - screenColumn, - token, - value.slice(0, this.MAX_LINE_LENGTH - screenColumn) - ); - } + token && this.$renderToken(parent, screenColumn, token, + value.slice(0, this.MAX_LINE_LENGTH - screenColumn)); var overflowEl = this.dom.createElement("span"); overflowEl.className = "ace_inline_button ace_keyword ace_toggle_wrap"; diff --git a/src/layer/text_test.js b/src/layer/text_test.js index 15bfebcb62b..7276fa840dd 100644 --- a/src/layer/text_test.js +++ b/src/layer/text_test.js @@ -44,13 +44,13 @@ module.exports = { var parent = dom.createElement("div"); this.textLayer.$renderLine(parent, 0); - assert.domNode(parent, ["div", {}, ["span", {}, ["span", {class: "ace_cjk", style: "width: 20px;"}, "\u3000"]]]); + assert.domNode(parent, ["div", {}, ["span", {class: "ace_cjk", style: "width: 20px;"}, "\u3000"]]); this.textLayer.setShowInvisibles(true); var parent = dom.createElement("div"); this.textLayer.$renderLine(parent, 0); assert.domNode(parent, ["div", {}, - ["span", {}, ["span", {class: "ace_cjk ace_invisible ace_invisible_space", style: "width: 20px;"}, this.textLayer.SPACE_CHAR]], + ["span", {class: "ace_cjk ace_invisible ace_invisible_space", style: "width: 20px;"}, this.textLayer.SPACE_CHAR], ["span", {class: "ace_invisible ace_invisible_eol"}, "\xB6"] ]); }, @@ -72,21 +72,21 @@ module.exports = { this.session.setValue(" \n\t\tf\n "); testRender([ - "" + SPACE(4) + "" + SPACE(2) + "", - "" + SPACE(4) + "" + SPACE(4) + "f", - "" + SPACE(3) + "" + "" + SPACE(4) + "" + SPACE(2), + "" + SPACE(4) + "" + SPACE(4) + "f", + SPACE(3) ]); this.textLayer.setShowInvisibles(true); testRender([ - "" + DOT(4) + "" + DOT(2) + "" + EOL, - "" + TAB(4) + "" + TAB(4) + "f" + EOL + "" + DOT(4) + "" + DOT(2) + "" + EOL, + "" + TAB(4) + "" + TAB(4) + "f" + EOL ]); this.textLayer.setDisplayIndentGuides(false); testRender([ - "" + DOT(6) + "" + EOL, - "" + TAB(4) + "" + TAB(4) + "f" + EOL + "" + DOT(6) + "" + EOL, + "" + TAB(4) + "" + TAB(4) + "f" + EOL ]); } }; diff --git a/src/virtual_renderer.js b/src/virtual_renderer.js index 5a9b442025b..76d99adf144 100644 --- a/src/virtual_renderer.js +++ b/src/virtual_renderer.js @@ -98,7 +98,7 @@ var VirtualRenderer = function(container, theme) { column : 0 }; - this.$fontMetrics = new FontMetrics(this.container, this.$textLayer.MAX_CHUNK_LENGTH); + this.$fontMetrics = new FontMetrics(this.container); this.$textLayer.$setFontMetrics(this.$fontMetrics); this.$textLayer.on("changeCharacterSize", function(e) { _self.updateCharacterSize();