diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 62875804b..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 @@ -1077,3 +1079,38 @@ 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 + + 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/spec/editor-spec.coffee b/spec/editor-spec.coffee index 05b84cc79..d697b7491 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -1196,6 +1196,7 @@ describe "Editor", -> editor.setHeight(50) editor.setWidth(50) editor.setHorizontalScrollbarHeight(0) + expect(editor.getScrollTop()).toBe 0 editor.setSelectedBufferRange([[5, 6], [6, 8]], autoscroll: true) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index cca84758f..23b288eac 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 @@ -207,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 @@ -222,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 = {} @@ -239,7 +252,7 @@ class DisplayBuffer extends Model @getLineCount() * @getLineHeightInPixels() getScrollWidth: -> - (@getMaxLineLength() * @getDefaultCharWidth()) + @getCursorWidth() + @scrollWidth getVisibleRowRange: -> return [0, 0] unless @getLineHeightInPixels() > 0 @@ -1041,6 +1054,8 @@ class DisplayBuffer extends Model {screenLines, regions} findMaxLineLength: (startScreenRow, endScreenRow, newScreenLines) -> + oldMaxLineLength = @maxLineLength + if startScreenRow <= @longestScreenRow < endScreenRow @longestScreenRow = 0 @maxLineLength = 0 @@ -1056,6 +1071,11 @@ class DisplayBuffer extends Model @longestScreenRow = maxLengthCandidatesStartRow + screenRow @maxLineLength = length + @computeScrollWidth() if oldMaxLineLength isnt @maxLineLength + + computeScrollWidth: -> + @scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left + 1 + handleBufferMarkersUpdated: => if event = @pendingChangeEvent @pendingChangeEvent = null 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 79160fc04..63809691b 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -250,10 +250,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