From 9cc1244f32f09c175c801e51d93f0ff81b0ac865 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 24 Jun 2014 16:53:38 -0700 Subject: [PATCH 1/7] Compute the longest line width based on the longest line --- src/display-buffer.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index cca84758f..31fa75872 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -239,7 +239,7 @@ class DisplayBuffer extends Model @getLineCount() * @getLineHeightInPixels() getScrollWidth: -> - (@getMaxLineLength() * @getDefaultCharWidth()) + @getCursorWidth() + @scrollWidth getVisibleRowRange: -> return [0, 0] unless @getLineHeightInPixels() > 0 @@ -980,6 +980,9 @@ class DisplayBuffer extends Model @rowMap.spliceRegions(startBufferRow, endBufferRow - startBufferRow, regions) @findMaxLineLength(startScreenRow, endScreenRow, screenLines) + if startBufferRow <= @longestScreenRow < endScreenRow + @computeScrollWidth() + return if options.suppressChangeEvent changeEvent = @@ -1056,6 +1059,8 @@ class DisplayBuffer extends Model @longestScreenRow = maxLengthCandidatesStartRow + screenRow @maxLineLength = length + computeScrollWidth: -> + @scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left + 1 handleBufferMarkersUpdated: => if event = @pendingChangeEvent @pendingChangeEvent = null From 196f64d8460a08e80fc9c406fe45f70f5ea8c77c Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 24 Jun 2014 17:26:30 -0700 Subject: [PATCH 2/7] Fix specs --- spec/display-buffer-spec.coffee | 2 ++ spec/editor-component-spec.coffee | 1 + spec/editor-spec.coffee | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 62875804b..7076d0923 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -1038,6 +1038,7 @@ describe "DisplayBuffer", -> displayBuffer.manageScrollPosition = true displayBuffer.setLineHeightInPixels(10) displayBuffer.setDefaultCharWidth(10) + displayBuffer.updateAllScreenLines() it "disallows negative values", -> displayBuffer.setWidth(displayBuffer.getScrollWidth() + 100) @@ -1062,6 +1063,7 @@ describe "DisplayBuffer", -> displayBuffer.setHorizontalScrollbarHeight(0) displayBuffer.setHeight(50) displayBuffer.setWidth(50) + displayBuffer.updateAllScreenLines() it "sets the scroll top and scroll left so the given screen position is in view", -> displayBuffer.scrollToScreenPosition([8, 20]) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index f1305774e..9d4bc66d8 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -62,6 +62,7 @@ describe "EditorComponent", -> node.style.height = editor.getLineCount() * lineHeightInPixels + 'px' node.style.width = '1000px' + editor.displayBuffer.updateAllScreenLines() component.measureScrollView() nextTick() diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 05b84cc79..72baccbca 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -733,6 +733,7 @@ describe "Editor", -> editor.setHorizontalScrollbarHeight(0) editor.setHeight(5.5 * 10) editor.setWidth(5.5 * 10) + editor.displayBuffer.updateAllScreenLines() it "scrolls down when the last cursor gets closer than ::verticalScrollMargin to the bottom of the editor", -> expect(editor.getScrollTop()).toBe 0 @@ -1196,6 +1197,8 @@ describe "Editor", -> editor.setHeight(50) editor.setWidth(50) editor.setHorizontalScrollbarHeight(0) + editor.displayBuffer.updateAllScreenLines() + expect(editor.getScrollTop()).toBe 0 editor.setSelectedBufferRange([[5, 6], [6, 8]], autoscroll: true) @@ -1230,6 +1233,7 @@ describe "Editor", -> editor.setDefaultCharWidth(10) editor.setHeight(50) editor.setWidth(50) + editor.displayBuffer.updateAllScreenLines() editor.addSelectionForBufferRange([[8, 10], [8, 15]]) expect(editor.getScrollTop()).toBe 75 From e3c3779a737033b1c738daeabd4b677092a16fcc Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 24 Jun 2014 17:31:00 -0700 Subject: [PATCH 3/7] =?UTF-8?q?Don=E2=80=99t=20need=20to=20return=20the=20?= =?UTF-8?q?list.=20So=20just=20return?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/display-buffer.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 31fa75872..e9b314f5f 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -1059,6 +1059,8 @@ class DisplayBuffer extends Model @longestScreenRow = maxLengthCandidatesStartRow + screenRow @maxLineLength = length + return + computeScrollWidth: -> @scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left + 1 handleBufferMarkersUpdated: => From 8054b769d687c8c20fc0fd58964f440d3d6a4c31 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 24 Jun 2014 17:31:19 -0700 Subject: [PATCH 4/7] Default scrollWidth to 0 --- src/display-buffer.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index e9b314f5f..599d6abfd 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -29,6 +29,7 @@ class DisplayBuffer extends Model width: null scrollTop: 0 scrollLeft: 0 + scrollWidth: 0 verticalScrollMargin: 2 horizontalScrollMargin: 6 From 809804d0cc50efd772259af23fac799fcfdbddfc Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 24 Jun 2014 17:32:50 -0700 Subject: [PATCH 5/7] :lipstick: --- src/display-buffer.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 599d6abfd..f02472bce 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -1064,6 +1064,7 @@ class DisplayBuffer extends Model computeScrollWidth: -> @scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left + 1 + handleBufferMarkersUpdated: => if event = @pendingChangeEvent @pendingChangeEvent = null From 77389b051865d387c967461bd95e16218fccd532 Mon Sep 17 00:00:00 2001 From: Ben Ogle & Nathan Sobo Date: Wed, 25 Jun 2014 11:16:46 -0700 Subject: [PATCH 6/7] Update scrollWidth when the max line length / default char width changes --- spec/display-buffer-spec.coffee | 20 ++++++++++++++++++-- spec/editor-component-spec.coffee | 1 - spec/editor-spec.coffee | 3 --- src/display-buffer.coffee | 13 ++++++++----- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 7076d0923..fbbcb2591 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -1038,7 +1038,6 @@ describe "DisplayBuffer", -> displayBuffer.manageScrollPosition = true displayBuffer.setLineHeightInPixels(10) displayBuffer.setDefaultCharWidth(10) - displayBuffer.updateAllScreenLines() it "disallows negative values", -> displayBuffer.setWidth(displayBuffer.getScrollWidth() + 100) @@ -1063,7 +1062,6 @@ describe "DisplayBuffer", -> displayBuffer.setHorizontalScrollbarHeight(0) displayBuffer.setHeight(50) displayBuffer.setWidth(50) - displayBuffer.updateAllScreenLines() it "sets the scroll top and scroll left so the given screen position is in view", -> displayBuffer.scrollToScreenPosition([8, 20]) @@ -1079,3 +1077,21 @@ describe "DisplayBuffer", -> it "does not scroll vertically if the position is already in view", -> displayBuffer.scrollToScreenPosition([4, 20], center: true) expect(displayBuffer.getScrollTop()).toBe 0 + + describe "scroll width", -> + cursorWidth = 1 + beforeEach -> + displayBuffer.setDefaultCharWidth(10) + + it "recomputes the scroll width when the default character width changes", -> + expect(displayBuffer.getScrollWidth()).toBe 10 * 65 + cursorWidth + + displayBuffer.setDefaultCharWidth(12) + expect(displayBuffer.getScrollWidth()).toBe 12 * 65 + cursorWidth + + it "recomputes the scroll width when the max line length changes", -> + buffer.insert([6, 12], ' ') + expect(displayBuffer.getScrollWidth()).toBe 10 * 66 + cursorWidth + + buffer.delete([[6, 10], [6, 12]], ' ') + expect(displayBuffer.getScrollWidth()).toBe 10 * 64 + cursorWidth diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 9d4bc66d8..f1305774e 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -62,7 +62,6 @@ describe "EditorComponent", -> node.style.height = editor.getLineCount() * lineHeightInPixels + 'px' node.style.width = '1000px' - editor.displayBuffer.updateAllScreenLines() component.measureScrollView() nextTick() diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 72baccbca..d697b7491 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -733,7 +733,6 @@ describe "Editor", -> editor.setHorizontalScrollbarHeight(0) editor.setHeight(5.5 * 10) editor.setWidth(5.5 * 10) - editor.displayBuffer.updateAllScreenLines() it "scrolls down when the last cursor gets closer than ::verticalScrollMargin to the bottom of the editor", -> expect(editor.getScrollTop()).toBe 0 @@ -1197,7 +1196,6 @@ describe "Editor", -> editor.setHeight(50) editor.setWidth(50) editor.setHorizontalScrollbarHeight(0) - editor.displayBuffer.updateAllScreenLines() expect(editor.getScrollTop()).toBe 0 @@ -1233,7 +1231,6 @@ describe "Editor", -> editor.setDefaultCharWidth(10) editor.setHeight(50) editor.setWidth(50) - editor.displayBuffer.updateAllScreenLines() editor.addSelectionForBufferRange([[8, 10], [8, 15]]) expect(editor.getScrollTop()).toBe 75 diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index f02472bce..2e72dd754 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -208,7 +208,11 @@ class DisplayBuffer extends Model setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels getDefaultCharWidth: -> @defaultCharWidth - setDefaultCharWidth: (@defaultCharWidth) -> @defaultCharWidth + setDefaultCharWidth: (defaultCharWidth) -> + if defaultCharWidth isnt @defaultCharWidth + @defaultCharWidth = defaultCharWidth + @computeScrollWidth() + defaultCharWidth getCursorWidth: -> 1 @@ -981,9 +985,6 @@ class DisplayBuffer extends Model @rowMap.spliceRegions(startBufferRow, endBufferRow - startBufferRow, regions) @findMaxLineLength(startScreenRow, endScreenRow, screenLines) - if startBufferRow <= @longestScreenRow < endScreenRow - @computeScrollWidth() - return if options.suppressChangeEvent changeEvent = @@ -1045,6 +1046,8 @@ class DisplayBuffer extends Model {screenLines, regions} findMaxLineLength: (startScreenRow, endScreenRow, newScreenLines) -> + oldMaxLineLength = @maxLineLength + if startScreenRow <= @longestScreenRow < endScreenRow @longestScreenRow = 0 @maxLineLength = 0 @@ -1060,7 +1063,7 @@ class DisplayBuffer extends Model @longestScreenRow = maxLengthCandidatesStartRow + screenRow @maxLineLength = length - return + @computeScrollWidth() if oldMaxLineLength isnt @maxLineLength computeScrollWidth: -> @scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left + 1 From f739dce21011f4002ba517e5f138d44d9678830d Mon Sep 17 00:00:00 2001 From: Ben Ogle & Nathan Sobo Date: Wed, 25 Jun 2014 11:56:48 -0700 Subject: [PATCH 7/7] Only recompute scroll width once for each batch of measured chars --- spec/display-buffer-spec.coffee | 21 ++++++++++++++++++++- src/display-buffer.coffee | 16 ++++++++++++---- src/editor.coffee | 2 ++ src/lines-component.coffee | 10 ++++++---- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index fbbcb2591..4a0314483 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -996,7 +996,9 @@ describe "DisplayBuffer", -> displayBuffer.setLineHeightInPixels(20) displayBuffer.setDefaultCharWidth(10) - displayBuffer.setScopedCharWidths(["source.js", "keyword.control.js"], r: 11, e: 11, t: 11, u: 11, n: 11) + + for char in ['r', 'e', 't', 'u', 'r', 'n'] + displayBuffer.setScopedCharWidth(["source.js", "keyword.control.js"], char, 11) {start, end} = marker.getPixelRange() expect(start.top).toBe 5 * 20 @@ -1095,3 +1097,20 @@ describe "DisplayBuffer", -> buffer.delete([[6, 10], [6, 12]], ' ') expect(displayBuffer.getScrollWidth()).toBe 10 * 64 + cursorWidth + + it "recomputes the scroll width when the scoped character widths change", -> + operatorWidth = 20 + displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '<', operatorWidth) + expect(displayBuffer.getScrollWidth()).toBe 10 * 64 + operatorWidth + cursorWidth + + it "recomputes the scroll width when the scoped character widths change in a batch", -> + operatorWidth = 20 + + displayBuffer.on 'character-widths-changed', changedSpy = jasmine.createSpy() + + displayBuffer.batchCharacterMeasurement -> + displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '<', operatorWidth) + displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '?', operatorWidth) + + expect(displayBuffer.getScrollWidth()).toBe 10 * 63 + operatorWidth * 2 + cursorWidth + expect(changedSpy.callCount).toBe 1 diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 2e72dd754..23b288eac 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -227,13 +227,21 @@ class DisplayBuffer extends Model scope.charWidths ?= {} scope.charWidths + batchCharacterMeasurement: (fn) -> + oldChangeCount = @scopedCharacterWidthsChangeCount + @batchingCharacterMeasurement = true + fn() + @batchingCharacterMeasurement = false + @characterWidthsChanged() if oldChangeCount isnt @scopedCharacterWidthsChangeCount + setScopedCharWidth: (scopeNames, char, width) -> @getScopedCharWidths(scopeNames)[char] = width - @emit 'character-widths-changed', @scopedCharacterWidthsChangeCount++ + @scopedCharacterWidthsChangeCount++ + @characterWidthsChanged() unless @batchingCharacterMeasurement - setScopedCharWidths: (scopeNames, charWidths) -> - _.extend(@getScopedCharWidths(scopeNames), charWidths) - @emit 'character-widths-changed', @scopedCharacterWidthsChangeCount++ + characterWidthsChanged: -> + @computeScrollWidth() + @emit 'character-widths-changed', @scopedCharacterWidthsChangeCount clearScopedCharWidths: -> @charWidthsByScope = {} diff --git a/src/editor.coffee b/src/editor.coffee index f1bdfeb77..be0dcd06d 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -1975,6 +1975,8 @@ class Editor extends Model getLineHeightInPixels: -> @displayBuffer.getLineHeightInPixels() setLineHeightInPixels: (lineHeightInPixels) -> @displayBuffer.setLineHeightInPixels(lineHeightInPixels) + batchCharacterMeasurement: (fn) -> @displayBuffer.batchCharacterMeasurement(fn) + getScopedCharWidth: (scopeNames, char) -> @displayBuffer.getScopedCharWidth(scopeNames, char) setScopedCharWidth: (scopeNames, char, width) -> @displayBuffer.setScopedCharWidth(scopeNames, char, width) diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 4762246dd..bf4e01d46 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -248,10 +248,12 @@ LinesComponent = React.createClass [visibleStartRow, visibleEndRow] = @props.renderedRowRange node = @getDOMNode() - for tokenizedLine in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1) - unless @measuredLines.has(tokenizedLine) - lineNode = @lineNodesByLineId[tokenizedLine.id] - @measureCharactersInLine(tokenizedLine, lineNode) + editor.batchCharacterMeasurement => + for tokenizedLine in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1) + unless @measuredLines.has(tokenizedLine) + lineNode = @lineNodesByLineId[tokenizedLine.id] + @measureCharactersInLine(tokenizedLine, lineNode) + return measureCharactersInLine: (tokenizedLine, lineNode) -> {editor} = @props