From e01c19e6b7c138d9cd42901cc52c33dc7bb5dbb2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 2 Dec 2013 19:00:13 -0800 Subject: [PATCH 1/3] Use buffer positions when calculating char widths Previously the editor width cache was being accessed using screen columns which could cause incorrect position left values for wrapped lines. Closes #1096 --- src/editor-view.coffee | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/editor-view.coffee b/src/editor-view.coffee index 11350cc8b..bada6e987 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -1587,36 +1587,39 @@ class EditorView extends View @renderedLines[0].removeChild(lineElement) { top: row * @lineHeight, left } - positionLeftForLineAndColumn: (lineElement, screenRow, column) -> - return 0 if column == 0 + positionLeftForLineAndColumn: (lineElement, screenRow, screenColumn) -> + return 0 if screenColumn == 0 bufferRow = @bufferRowsForScreenRows(screenRow, screenRow)[0] ? screenRow + bufferColumn = @bufferPositionForScreenPosition([screenRow, screenColumn]).column tokenizedLine = @editor.displayBuffer.tokenizedBuffer.tokenizedLines[bufferRow] left = 0 index = 0 + startIndex = @bufferPositionForScreenPosition([screenRow, 0]).column for token in tokenizedLine.tokens for char in token.value - return left if index >= column + return left if index >= bufferColumn - val = @getCharacterWidthCache(token.scopes, char) - if val? - left += val - else - return @measureToColumn(lineElement, tokenizedLine, column) + if index >= startIndex + val = @getCharacterWidthCache(token.scopes, char) + if val? + left += val + else + return @measureToColumn(lineElement, tokenizedLine, screenColumn, startIndex) index++ left - scopesForColumn: (tokenizedLine, column) -> + scopesForColumn: (tokenizedLine, bufferColumn) -> index = 0 for token in tokenizedLine.tokens for char in token.value - return token.scopes if index == column + return token.scopes if index == bufferColumn index++ null - measureToColumn: (lineElement, tokenizedLine, column) -> + measureToColumn: (lineElement, tokenizedLine, screenColumn, lineStartBufferColumn) -> left = oldLeft = index = 0 iterator = document.createNodeIterator(lineElement, NodeFilter.SHOW_TEXT, TextNodeFilter) @@ -1630,13 +1633,13 @@ class EditorView extends View for char, i in content # Don't continue caching long lines :racehorse: - break if index > LongLineLength and column < index + break if index > LongLineLength and screenColumn < index # Dont return right away, finish caching the whole line - returnLeft = left if index == column + returnLeft = left if index == screenColumn oldLeft = left - scopes = @scopesForColumn(tokenizedLine, index) + scopes = @scopesForColumn(tokenizedLine, lineStartBufferColumn + index) cachedCharWidth = @getCharacterWidthCache(scopes, char) if cachedCharWidth? @@ -1655,7 +1658,7 @@ class EditorView extends View # Assume all the characters are the same width when dealing with long # lines :racehorse: - return column * cachedCharWidth if index > LongLineLength + return screenColumn * cachedCharWidth if index > LongLineLength index++ From eaf60a00b325ac44380505abc41a003f4c8027d2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 2 Dec 2013 19:15:37 -0800 Subject: [PATCH 2/3] Use TokenizedLine::tokenAtBufferColumn to obtain scopes --- src/editor-view.coffee | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/editor-view.coffee b/src/editor-view.coffee index bada6e987..9dd32d9b8 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -1611,14 +1611,6 @@ class EditorView extends View index++ left - scopesForColumn: (tokenizedLine, bufferColumn) -> - index = 0 - for token in tokenizedLine.tokens - for char in token.value - return token.scopes if index == bufferColumn - index++ - null - measureToColumn: (lineElement, tokenizedLine, screenColumn, lineStartBufferColumn) -> left = oldLeft = index = 0 iterator = document.createNodeIterator(lineElement, NodeFilter.SHOW_TEXT, TextNodeFilter) @@ -1639,7 +1631,7 @@ class EditorView extends View returnLeft = left if index == screenColumn oldLeft = left - scopes = @scopesForColumn(tokenizedLine, lineStartBufferColumn + index) + scopes = tokenizedLine.tokenAtBufferColumn(lineStartBufferColumn + index)?.scopes cachedCharWidth = @getCharacterWidthCache(scopes, char) if cachedCharWidth? From ee7ef0f893433dfba218a90bd0b9432adde5df88 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 3 Dec 2013 08:49:37 -0800 Subject: [PATCH 3/3] Add spec that previously failed --- spec/editor-view-spec.coffee | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/editor-view-spec.coffee b/spec/editor-view-spec.coffee index 8aa3399a5..c7fc72a6f 100644 --- a/spec/editor-view-spec.coffee +++ b/spec/editor-view-spec.coffee @@ -2832,3 +2832,22 @@ describe "EditorView", -> setEditorWidthInChars(editorView, 100) $(window).trigger 'resize' expect(editorView.editor.getSoftWrapColumn()).toBe 100 + + describe "character width caching", -> + describe "when soft wrap is enabled", -> + it "correctly calculates the the position left for a column", -> + editor.setSoftWrap(true) + editorView.setText('lllll 00000') + editorView.setFontFamily('serif') + editorView.setFontSize(10) + editorView.attachToDom() + editorView.setWidthInChars(5) + + expect(editorView.pixelPositionForScreenPosition([0, 5]).left).toEqual 15 + expect(editorView.pixelPositionForScreenPosition([1, 5]).left).toEqual 25 + + # Check that widths are actually being cached + spyOn(editorView, 'measureToColumn').andCallThrough() + editorView.pixelPositionForScreenPosition([0, 5]) + editorView.pixelPositionForScreenPosition([1, 5]) + expect(editorView.measureToColumn.callCount).toBe 0