diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 10d91941d..d4a64c300 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -1119,7 +1119,7 @@ describe "TextEditorComponent", -> nextAnimationFrame() # Should not be rendering range containing the marker - expect(component.presenter.computeEndRow()).toBeLessThan 9 + expect(component.presenter.endRow).toBeLessThan 9 regions = componentNode.querySelectorAll('.some-highlight .region') diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index d86c47337..1dee7d57a 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -140,7 +140,7 @@ describe "TextEditorPresenter", -> presenter = buildPresenter(contentFrameWidth: 470, baseCharacterWidth: 10) expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 expectStateUpdate presenter, -> editor.setSoftWrapped(true) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe presenter.computeClientWidth() + expect(presenter.state.horizontalScrollbar.scrollWidth).toBe presenter.clientWidth expectStateUpdate presenter, -> editor.setSoftWrapped(false) expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 @@ -164,16 +164,16 @@ describe "TextEditorPresenter", -> it "never exceeds the computed scrollWidth minus the computed clientWidth", -> presenter = buildPresenter(scrollLeft: 10, verticalScrollbarWidth: 10, explicitHeight: 100, contentFrameWidth: 500) expectStateUpdate presenter, -> presenter.setScrollLeft(300) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.computeScrollWidth() - presenter.computeClientWidth() + expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setContentFrameWidth(600) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.computeScrollWidth() - presenter.computeClientWidth() + expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setVerticalScrollbarWidth(5) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.computeScrollWidth() - presenter.computeClientWidth() + expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> editor.getBuffer().delete([[6, 0], [6, Infinity]]) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.computeScrollWidth() - presenter.computeClientWidth() + expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth # Scroll top only gets smaller when needed as dimensions change, never bigger scrollLeftBefore = presenter.state.horizontalScrollbar.scrollLeft @@ -260,13 +260,13 @@ describe "TextEditorPresenter", -> it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.state.verticalScrollbar.scrollHeight).toBe presenter.computeContentHeight() + expect(presenter.state.verticalScrollbar.scrollHeight).toBe presenter.contentHeight expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) - expect(presenter.state.verticalScrollbar.scrollHeight).toBe presenter.computeContentHeight() + presenter.computeClientHeight() - (presenter.lineHeight * 3) + expect(presenter.state.verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.state.verticalScrollbar.scrollHeight).toBe presenter.computeContentHeight() + expect(presenter.state.verticalScrollbar.scrollHeight).toBe presenter.contentHeight describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> @@ -278,16 +278,16 @@ describe "TextEditorPresenter", -> it "never exceeds the computed scrollHeight minus the computed clientHeight", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(100) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setExplicitHeight(60) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight # Scroll top only gets smaller when needed as dimensions change, never bigger scrollTopBefore = presenter.state.verticalScrollbar.scrollTop @@ -302,19 +302,19 @@ describe "TextEditorPresenter", -> it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeContentHeight() - presenter.computeClientHeight() + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.contentHeight - presenter.clientHeight atom.config.set("editor.scrollPastEnd", true) expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeContentHeight() - (presenter.lineHeight * 3) + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3) expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.computeContentHeight() - presenter.computeClientHeight() + expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.contentHeight - presenter.clientHeight describe ".content", -> describe ".scrollingVertically", -> it "is true for ::stoppedScrollingDelay milliseconds following a changes to ::scrollTop", -> - presenter = buildPresenter(scrollTop: 10, stoppedScrollingDelay: 200) + presenter = buildPresenter(scrollTop: 10, stoppedScrollingDelay: 200, explicitHeight: 100) expect(presenter.state.content.scrollingVertically).toBe false expectStateUpdate presenter, -> presenter.setScrollTop(0) expect(presenter.state.content.scrollingVertically).toBe true @@ -352,13 +352,13 @@ describe "TextEditorPresenter", -> it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.state.content.scrollHeight).toBe presenter.computeContentHeight() + expect(presenter.state.content.scrollHeight).toBe presenter.contentHeight expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) - expect(presenter.state.content.scrollHeight).toBe presenter.computeContentHeight() + presenter.computeClientHeight() - (presenter.lineHeight * 3) + expect(presenter.state.content.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.state.content.scrollHeight).toBe presenter.computeContentHeight() + expect(presenter.state.content.scrollHeight).toBe presenter.contentHeight describe ".scrollWidth", -> it "is initialized as the max of the computed clientWidth and the width of the longest line", -> @@ -401,7 +401,7 @@ describe "TextEditorPresenter", -> presenter = buildPresenter(contentFrameWidth: 470, baseCharacterWidth: 10) expect(presenter.state.content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 expectStateUpdate presenter, -> editor.setSoftWrapped(true) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe presenter.computeClientWidth() + expect(presenter.state.horizontalScrollbar.scrollWidth).toBe presenter.clientWidth expectStateUpdate presenter, -> editor.setSoftWrapped(false) expect(presenter.state.content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 @@ -425,16 +425,16 @@ describe "TextEditorPresenter", -> it "never exceeds the computed scroll height minus the computed client height", -> presenter = buildPresenter(scrollTop: 10, lineHeight: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(100) - expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setExplicitHeight(60) - expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5) - expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) - expect(presenter.state.content.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight # Scroll top only gets smaller when needed as dimensions change, never bigger scrollTopBefore = presenter.state.verticalScrollbar.scrollTop @@ -449,14 +449,14 @@ describe "TextEditorPresenter", -> it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.state.content.scrollTop).toBe presenter.computeContentHeight() - presenter.computeClientHeight() + expect(presenter.state.content.scrollTop).toBe presenter.contentHeight - presenter.clientHeight atom.config.set("editor.scrollPastEnd", true) expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.state.content.scrollTop).toBe presenter.computeContentHeight() - (presenter.lineHeight * 3) + expect(presenter.state.content.scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3) expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.state.content.scrollTop).toBe presenter.computeContentHeight() - presenter.computeClientHeight() + expect(presenter.state.content.scrollTop).toBe presenter.contentHeight - presenter.clientHeight describe ".scrollLeft", -> it "tracks the value of ::scrollLeft", -> @@ -468,16 +468,16 @@ describe "TextEditorPresenter", -> it "never exceeds the computed scrollWidth minus the computed clientWidth", -> presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) expectStateUpdate presenter, -> presenter.setScrollLeft(300) - expect(presenter.state.content.scrollLeft).toBe presenter.computeScrollWidth() - presenter.computeClientWidth() + expect(presenter.state.content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setContentFrameWidth(600) - expect(presenter.state.content.scrollLeft).toBe presenter.computeScrollWidth() - presenter.computeClientWidth() + expect(presenter.state.content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setVerticalScrollbarWidth(5) - expect(presenter.state.content.scrollLeft).toBe presenter.computeScrollWidth() - presenter.computeClientWidth() + expect(presenter.state.content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> editor.getBuffer().delete([[6, 0], [6, Infinity]]) - expect(presenter.state.content.scrollLeft).toBe presenter.computeScrollWidth() - presenter.computeClientWidth() + expect(presenter.state.content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth # Scroll top only gets smaller when needed as dimensions change, never bigger scrollLeftBefore = presenter.state.content.scrollLeft @@ -1524,13 +1524,13 @@ describe "TextEditorPresenter", -> it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.state.gutter.scrollHeight).toBe presenter.computeContentHeight() + expect(presenter.state.gutter.scrollHeight).toBe presenter.contentHeight expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) - expect(presenter.state.gutter.scrollHeight).toBe presenter.computeContentHeight() + presenter.computeClientHeight() - (presenter.lineHeight * 3) + expect(presenter.state.gutter.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.state.gutter.scrollHeight).toBe presenter.computeContentHeight() + expect(presenter.state.gutter.scrollHeight).toBe presenter.contentHeight describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> @@ -1542,16 +1542,16 @@ describe "TextEditorPresenter", -> it "never exceeds the computed scrollHeight minus the computed clientHeight", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(100) - expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setExplicitHeight(60) - expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5) - expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) - expect(presenter.state.gutter.scrollTop).toBe presenter.computeScrollHeight() - presenter.computeClientHeight() + expect(presenter.state.gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight # Scroll top only gets smaller when needed as dimensions change, never bigger scrollTopBefore = presenter.state.verticalScrollbar.scrollTop @@ -1566,14 +1566,14 @@ describe "TextEditorPresenter", -> it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.state.gutter.scrollTop).toBe presenter.computeContentHeight() - presenter.computeClientHeight() + expect(presenter.state.gutter.scrollTop).toBe presenter.contentHeight - presenter.clientHeight atom.config.set("editor.scrollPastEnd", true) expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(presenter.state.gutter.scrollTop).toBe presenter.computeContentHeight() - (presenter.lineHeight * 3) + expect(presenter.state.gutter.scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3) expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.state.gutter.scrollTop).toBe presenter.computeContentHeight() - presenter.computeClientHeight() + expect(presenter.state.gutter.scrollTop).toBe presenter.contentHeight - presenter.clientHeight describe ".backgroundColor", -> it "is assigned to ::gutterBackgroundColor if present, and to ::backgroundColor otherwise", -> @@ -1995,7 +1995,7 @@ describe "TextEditorPresenter", -> presenter.setScrollTop(newScrollTop) changeScrollLeft = (log) -> - scrollWidth = presenter.computeScrollWidth() + scrollWidth = presenter.scrollWidth newScrollLeft = Math.max(0, _.random(0, scrollWidth - presenterParams.contentFrameWidth)) log """ presenterParams.scrollLeft = #{newScrollLeft} @@ -2015,7 +2015,7 @@ describe "TextEditorPresenter", -> presenter.setExplicitHeight(newExplicitHeight) changeContentFrameWidth = (log) -> - scrollWidth = presenter.computeScrollWidth() + scrollWidth = presenter.scrollWidth newContentFrameWidth = _.random(100, scrollWidth * 1.5) log """ presenterParams.contentFrameWidth = #{newContentFrameWidth} diff --git a/src/overlay-manager.coffee b/src/overlay-manager.coffee index 2fa3cc028..82387fcf7 100644 --- a/src/overlay-manager.coffee +++ b/src/overlay-manager.coffee @@ -34,7 +34,7 @@ class OverlayManager left -= itemWidth top = pixelPosition.top + presenter.lineHeight - if top + itemHeight - scrollTop > presenter.computeHeight() and top - itemHeight - presenter.lineHeight >= scrollTop + if top + itemHeight - scrollTop > presenter.height and top - itemHeight - presenter.lineHeight >= scrollTop top -= itemHeight + presenter.lineHeight overlayNode.style.top = top + 'px' diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index c48c66b03..31475e317 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -12,9 +12,11 @@ class TextEditorPresenter constructor: (params) -> {@model, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft} = params - {@horizontalScrollbarHeight, @verticalScrollbarWidth} = params + {horizontalScrollbarHeight, verticalScrollbarWidth} = params {@lineHeight, @baseCharacterWidth, @lineOverdrawMargin, @backgroundColor, @gutterBackgroundColor} = params {@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay} = params + @measuredHorizontalScrollbarHeight = horizontalScrollbarHeight + @measuredVerticalScrollbarWidth = verticalScrollbarWidth @disposables = new CompositeDisposable @emitter = new Emitter @@ -38,11 +40,13 @@ class TextEditorPresenter @model.setDefaultCharWidth(@baseCharacterWidth) if @baseCharacterWidth? @model.setScrollTop(@scrollTop) if @scrollTop? @model.setScrollLeft(@scrollLeft) if @scrollLeft? - @model.setVerticalScrollbarWidth(@verticalScrollbarWidth) if @verticalScrollbarWidth? - @model.setHorizontalScrollbarHeight(@horizontalScrollbarHeight) if @horizontalScrollbarHeight? + @model.setVerticalScrollbarWidth(@measuredVerticalScrollbarWidth) if @measuredVerticalScrollbarWidth? + @model.setHorizontalScrollbarHeight(@measuredHorizontalScrollbarHeight) if @measuredHorizontalScrollbarHeight? observeModel: -> @disposables.add @model.onDidChange => + @updateContentDimensions() + @updateEndRow() @updateHeightState() @updateVerticalScrollState() @updateHorizontalScrollState() @@ -67,8 +71,12 @@ class TextEditorPresenter @observeCursor(cursor) for cursor in @model.getCursors() observeConfig: -> + @scrollPastEnd = atom.config.get('editor.scrollPastEnd') + @disposables.add atom.config.onDidChange 'editor.showIndentGuide', scope: @model.getRootScopeDescriptor(), @updateContentState.bind(this) - @disposables.add atom.config.onDidChange 'editor.scrollPastEnd', scope: @model.getRootScopeDescriptor(), => + @disposables.add atom.config.onDidChange 'editor.scrollPastEnd', scope: @model.getRootScopeDescriptor(), ({newValue}) => + @scrollPastEnd = newValue + @updateScrollHeight() @updateVerticalScrollState() @updateScrollbarsState() @@ -87,6 +95,11 @@ class TextEditorPresenter @updateState() updateState: -> + @updateContentDimensions() + @updateScrollbarDimensions() + @updateStartRow() + @updateEndRow() + @updateHeightState() @updateVerticalScrollState() @updateHorizontalScrollState() @@ -101,52 +114,45 @@ class TextEditorPresenter updateHeightState: -> if @autoHeight - @state.height = @computeContentHeight() + @state.height = @contentHeight else @state.height = null @emitter.emit 'did-update-state' updateVerticalScrollState: -> - scrollHeight = @computeScrollHeight() - @state.content.scrollHeight = scrollHeight - @state.gutter.scrollHeight = scrollHeight - @state.verticalScrollbar.scrollHeight = scrollHeight + @state.content.scrollHeight = @scrollHeight + @state.gutter.scrollHeight = @scrollHeight + @state.verticalScrollbar.scrollHeight = @scrollHeight - scrollTop = @computeScrollTop() - @state.content.scrollTop = scrollTop - @state.gutter.scrollTop = scrollTop - @state.verticalScrollbar.scrollTop = scrollTop + @state.content.scrollTop = @scrollTop + @state.gutter.scrollTop = @scrollTop + @state.verticalScrollbar.scrollTop = @scrollTop @emitter.emit 'did-update-state' updateHorizontalScrollState: -> - scrollWidth = @computeScrollWidth() - @state.content.scrollWidth = scrollWidth - @state.horizontalScrollbar.scrollWidth = scrollWidth + @state.content.scrollWidth = @scrollWidth + @state.horizontalScrollbar.scrollWidth = @scrollWidth - scrollLeft = @computeScrollLeft() - @state.content.scrollLeft = scrollLeft - @state.horizontalScrollbar.scrollLeft = scrollLeft + @state.content.scrollLeft = @scrollLeft + @state.horizontalScrollbar.scrollLeft = @scrollLeft @emitter.emit 'did-update-state' updateScrollbarsState: -> - horizontalScrollbarHeight = @computeHorizontalScrollbarHeight() - verticalScrollbarWidth = @computeVerticalScrollbarWidth() + @state.horizontalScrollbar.visible = @horizontalScrollbarHeight > 0 + @state.horizontalScrollbar.height = @measuredHorizontalScrollbarHeight + @state.horizontalScrollbar.right = @verticalScrollbarWidth - @state.horizontalScrollbar.visible = horizontalScrollbarHeight > 0 - @state.horizontalScrollbar.height = @horizontalScrollbarHeight - @state.horizontalScrollbar.right = verticalScrollbarWidth - - @state.verticalScrollbar.visible = verticalScrollbarWidth > 0 - @state.verticalScrollbar.width = @verticalScrollbarWidth - @state.verticalScrollbar.bottom = horizontalScrollbarHeight + @state.verticalScrollbar.visible = @verticalScrollbarWidth > 0 + @state.verticalScrollbar.width = @measuredVerticalScrollbarWidth + @state.verticalScrollbar.bottom = @horizontalScrollbarHeight @emitter.emit 'did-update-state' updateContentState: -> - @state.content.scrollWidth = @computeScrollWidth() + @state.content.scrollWidth = @scrollWidth @state.content.scrollLeft = @scrollLeft @state.content.indentGuidesVisible = not @model.isMini() and atom.config.get('editor.showIndentGuide', scope: @model.getRootScopeDescriptor()) @state.content.backgroundColor = if @model.isMini() then null else @backgroundColor @@ -157,10 +163,8 @@ class TextEditorPresenter return unless @hasRequiredMeasurements() visibleLineIds = {} - startRow = @computeStartRow() - endRow = @computeEndRow() - row = startRow - while row < endRow + row = @startRow + while row < @endRow line = @model.tokenizedLineForScreenRow(row) visibleLineIds[line.id] = true if @state.content.lines.hasOwnProperty(line.id) @@ -202,11 +206,8 @@ class TextEditorPresenter @state.content.cursors = {} return unless @hasRequiredMeasurements() - startRow = @computeStartRow() - endRow = @computeEndRow() - - for cursor in @model.getCursors() - if cursor.isVisible() and startRow <= cursor.getScreenRow() < endRow + for cursor in @model.cursors # using property directly to avoid allocation + if cursor.isVisible() and @startRow <= cursor.getScreenRow() < @endRow pixelRect = @pixelRectForScreenRange(cursor.getScreenRange()) pixelRect.width = @baseCharacterWidth if pixelRect.width is 0 @state.content.cursors[cursor.id] = pixelRect @@ -245,20 +246,18 @@ class TextEditorPresenter @emitter.emit "did-update-state" updateLineNumbersState: -> - startRow = @computeStartRow() - endRow = @computeEndRow() visibleLineNumberIds = {} - if startRow > 0 - rowBeforeStartRow = startRow - 1 + if @startRow > 0 + rowBeforeStartRow = @startRow - 1 lastBufferRow = @model.bufferRowForScreenRow(rowBeforeStartRow) wrapCount = rowBeforeStartRow - @model.screenRowForBufferRow(lastBufferRow) else lastBufferRow = null wrapCount = 0 - if endRow > startRow - for bufferRow, i in @model.bufferRowsForScreenRows(startRow, endRow - 1) + if @endRow > @startRow + for bufferRow, i in @model.bufferRowsForScreenRows(@startRow, @endRow - 1) if bufferRow is lastBufferRow wrapCount++ id = bufferRow + '-' + wrapCount @@ -269,7 +268,7 @@ class TextEditorPresenter lastBufferRow = bufferRow softWrapped = false - screenRow = startRow + i + screenRow = @startRow + i top = screenRow * @lineHeight decorationClasses = @lineNumberDecorationClassesForRow(screenRow) foldable = @model.isFoldableAtScreenRow(screenRow) @@ -333,91 +332,121 @@ class TextEditorPresenter regions - computeStartRow: -> - startRow = Math.floor(@computeScrollTop() / @lineHeight) - @lineOverdrawMargin - Math.max(0, startRow) + updateStartRow: -> + startRow = Math.floor(@scrollTop / @lineHeight) - @lineOverdrawMargin + @startRow = Math.max(0, startRow) - computeEndRow: -> - startRow = Math.floor(@computeScrollTop() / @lineHeight) - visibleLinesCount = Math.ceil(@computeHeight() / @lineHeight) + 1 + updateEndRow: -> + startRow = Math.max(0, Math.floor(@scrollTop / @lineHeight)) + visibleLinesCount = Math.ceil(@height / @lineHeight) + 1 endRow = startRow + visibleLinesCount + @lineOverdrawMargin - Math.min(@model.getScreenLineCount(), endRow) + @endRow = Math.min(@model.getScreenLineCount(), endRow) - computeScrollWidth: -> - Math.max(@computeContentWidth(), @computeClientWidth()) + updateScrollWidth: -> + scrollWidth = Math.max(@contentWidth, @clientWidth) + unless @scrollWidth is scrollWidth + @scrollWidth = scrollWidth + @updateScrollLeft() - computeScrollHeight: -> - contentHeight = @computeContentHeight() - if atom.config.get('editor.scrollPastEnd') - extraScrollHeight = @computeClientHeight() - (@lineHeight * 3) + updateScrollHeight: -> + contentHeight = @contentHeight + if @scrollPastEnd + extraScrollHeight = @clientHeight - (@lineHeight * 3) contentHeight += extraScrollHeight if extraScrollHeight > 0 - Math.max(contentHeight, @computeHeight()) + scrollHeight = Math.max(contentHeight, @height) - computeContentWidth: -> - contentWidth = @pixelPositionForScreenPosition([@model.getLongestScreenRow(), Infinity]).left - contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width - contentWidth + unless @scrollHeight is scrollHeight + @scrollHeight = scrollHeight + @updateScrollTop() - computeContentHeight: -> - @lineHeight * @model.getScreenLineCount() + updateContentDimensions: (updateHeight=true, updateWidth=true) -> + if updateHeight + oldContentHeight = @contentHeight + @contentHeight = @lineHeight * @model.getScreenLineCount() - computeClientHeight: -> - @computeHeight() - @computeHorizontalScrollbarHeight() + if updateWidth + oldContentWidth = @contentWidth + @contentWidth = @pixelPositionForScreenPosition([@model.getLongestScreenRow(), Infinity]).left + @contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width - computeClientWidth: -> - @contentFrameWidth - @computeVerticalScrollbarWidth() + if updateHeight and @contentHeight isnt oldContentHeight + @updateHeight() + @updateScrollbarDimensions() + @updateScrollHeight() - computeScrollTop: -> - @scrollTop = @constrainScrollTop(@scrollTop) + if updateWidth and @contentWidth isnt oldContentWidth + @updateScrollbarDimensions() + @updateScrollWidth() + + updateClientHeight: -> + clientHeight = @height - @horizontalScrollbarHeight + unless @clientHeight is clientHeight + @clientHeight = clientHeight + @updateScrollHeight() + @updateScrollTop() + + updateClientWidth: -> + clientWidth = @contentFrameWidth - @verticalScrollbarWidth + unless @clientWidth is clientWidth + @clientWidth = clientWidth + @updateScrollWidth() + @updateScrollLeft() + + updateScrollTop: -> + scrollTop = @constrainScrollTop(@scrollTop) + unless @scrollTop is scrollTop + @scrollTop = scrollTop + @updateStartRow() + @updateEndRow() constrainScrollTop: (scrollTop) -> if @hasRequiredMeasurements() - Math.max(0, Math.min(scrollTop, @computeScrollHeight() - @computeClientHeight())) + Math.max(0, Math.min(scrollTop, @scrollHeight - @clientHeight)) else Math.max(0, scrollTop) if scrollTop? - computeScrollLeft: -> + updateScrollLeft: -> @scrollLeft = @constrainScrollLeft(@scrollLeft) constrainScrollLeft: (scrollLeft) -> if @hasRequiredMeasurements() - Math.max(0, Math.min(scrollLeft, @computeScrollWidth() - @computeClientWidth())) + Math.max(0, Math.min(scrollLeft, @scrollWidth - @clientWidth)) else Math.max(0, scrollLeft) if scrollLeft? - computeHorizontalScrollbarHeight: -> - contentWidth = @computeContentWidth() - contentHeight = @computeContentHeight() + updateScrollbarDimensions: -> clientWidthWithoutVerticalScrollbar = @contentFrameWidth - clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth - clientHeightWithoutHorizontalScrollbar = @computeHeight() - clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight + clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @measuredVerticalScrollbarWidth + clientHeightWithoutHorizontalScrollbar = @height + clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @measuredHorizontalScrollbarHeight horizontalScrollbarVisible = - contentWidth > clientWidthWithoutVerticalScrollbar or - contentWidth > clientWidthWithVerticalScrollbar and contentHeight > clientHeightWithoutHorizontalScrollbar - - if horizontalScrollbarVisible - @horizontalScrollbarHeight - else - 0 - - computeVerticalScrollbarWidth: -> - contentWidth = @computeContentWidth() - contentHeight = @computeContentHeight() - clientWidthWithoutVerticalScrollbar = @contentFrameWidth - clientWidthWithVerticalScrollbar = clientWidthWithoutVerticalScrollbar - @verticalScrollbarWidth - clientHeightWithoutHorizontalScrollbar = @computeHeight() - clientHeightWithHorizontalScrollbar = clientHeightWithoutHorizontalScrollbar - @horizontalScrollbarHeight + @contentWidth > clientWidthWithoutVerticalScrollbar or + @contentWidth > clientWidthWithVerticalScrollbar and @contentHeight > clientHeightWithoutHorizontalScrollbar verticalScrollbarVisible = - contentHeight > clientHeightWithoutHorizontalScrollbar or - contentHeight > clientHeightWithHorizontalScrollbar and contentWidth > clientWidthWithoutVerticalScrollbar + @contentHeight > clientHeightWithoutHorizontalScrollbar or + @contentHeight > clientHeightWithHorizontalScrollbar and @contentWidth > clientWidthWithoutVerticalScrollbar - if verticalScrollbarVisible - @verticalScrollbarWidth - else - 0 + horizontalScrollbarHeight = + if horizontalScrollbarVisible + @measuredHorizontalScrollbarHeight + else + 0 + + verticalScrollbarWidth = + if verticalScrollbarVisible + @measuredVerticalScrollbarWidth + else + 0 + + unless @horizontalScrollbarHeight is horizontalScrollbarHeight + @horizontalScrollbarHeight = horizontalScrollbarHeight + @updateClientHeight() + + unless @verticalScrollbarWidth is verticalScrollbarWidth + @verticalScrollbarWidth = verticalScrollbarWidth + @updateClientWidth() lineDecorationClassesForRow: (row) -> return null if @model.isMini() @@ -447,8 +476,8 @@ class TextEditorPresenter @scrollTop? and @contentFrameWidth? and @scrollLeft? and - @verticalScrollbarWidth? and - @horizontalScrollbarHeight? + @measuredVerticalScrollbarWidth? and + @measuredHorizontalScrollbarHeight? setScrollTop: (scrollTop) -> scrollTop = @constrainScrollTop(scrollTop) @@ -456,6 +485,8 @@ class TextEditorPresenter unless @scrollTop is scrollTop @scrollTop = scrollTop @model.setScrollTop(scrollTop) + @updateStartRow() + @updateEndRow() @didStartScrolling() @updateVerticalScrollState() @updateDecorations() @@ -490,19 +521,21 @@ class TextEditorPresenter @updateCursorsState() unless oldScrollLeft? setHorizontalScrollbarHeight: (horizontalScrollbarHeight) -> - unless @horizontalScrollbarHeight is horizontalScrollbarHeight - oldHorizontalScrollbarHeight = @horizontalScrollbarHeight - @horizontalScrollbarHeight = horizontalScrollbarHeight + unless @measuredHorizontalScrollbarHeight is horizontalScrollbarHeight + oldHorizontalScrollbarHeight = @measuredHorizontalScrollbarHeight + @measuredHorizontalScrollbarHeight = horizontalScrollbarHeight @model.setHorizontalScrollbarHeight(horizontalScrollbarHeight) + @updateScrollbarDimensions() @updateScrollbarsState() @updateVerticalScrollState() @updateCursorsState() unless oldHorizontalScrollbarHeight? setVerticalScrollbarWidth: (verticalScrollbarWidth) -> - unless @verticalScrollbarWidth is verticalScrollbarWidth - oldVerticalScrollbarWidth = @verticalScrollbarWidth - @verticalScrollbarWidth = verticalScrollbarWidth + unless @measuredVerticalScrollbarWidth is verticalScrollbarWidth + oldVerticalScrollbarWidth = @measuredVerticalScrollbarWidth + @measuredVerticalScrollbarWidth = verticalScrollbarWidth @model.setVerticalScrollbarWidth(verticalScrollbarWidth) + @updateScrollbarDimensions() @updateScrollbarsState() @updateHorizontalScrollState() @updateCursorsState() unless oldVerticalScrollbarWidth? @@ -516,6 +549,7 @@ class TextEditorPresenter unless @explicitHeight is explicitHeight @explicitHeight = explicitHeight @model.setHeight(explicitHeight) + @updateHeight() @updateVerticalScrollState() @updateScrollbarsState() @updateDecorations() @@ -523,14 +557,22 @@ class TextEditorPresenter @updateCursorsState() @updateLineNumbersState() - computeHeight: -> - @explicitHeight ? @computeContentHeight() + updateHeight: -> + height = @explicitHeight ? @contentHeight + unless @height is height + @height = height + @updateScrollbarDimensions() + @updateClientHeight() + @updateScrollHeight() + @updateEndRow() setContentFrameWidth: (contentFrameWidth) -> unless @contentFrameWidth is contentFrameWidth oldContentFrameWidth = @contentFrameWidth @contentFrameWidth = contentFrameWidth @model.setWidth(contentFrameWidth) + @updateScrollbarDimensions() + @updateClientWidth() @updateVerticalScrollState() @updateHorizontalScrollState() @updateScrollbarsState() @@ -554,7 +596,11 @@ class TextEditorPresenter unless @lineHeight is lineHeight @lineHeight = lineHeight @model.setLineHeightInPixels(lineHeight) + @updateContentDimensions(true, false) + @updateScrollHeight() @updateHeightState() + @updateStartRow() + @updateEndRow() @updateVerticalScrollState() @updateDecorations() @updateLinesState() @@ -598,6 +644,8 @@ class TextEditorPresenter @characterWidthsChanged() unless @batchingCharacterMeasurement characterWidthsChanged: -> + @updateContentDimensions(false, true) + @updateHorizontalScrollState() @updateContentState() @updateDecorations() @@ -644,7 +692,7 @@ class TextEditorPresenter top = @pixelPositionForScreenPosition(screenRange.start).top left = 0 height = (screenRange.end.row - screenRange.start.row + 1) * @lineHeight - width = @computeScrollWidth() + width = @scrollWidth else {top, left} = @pixelPositionForScreenPosition(screenRange.start, false) height = @lineHeight @@ -669,16 +717,14 @@ class TextEditorPresenter return if change.textChanged intersectsVisibleRowRange = false - startRow = @computeStartRow() - endRow = @computeEndRow() oldRange = new Range(change.oldTailScreenPosition, change.oldHeadScreenPosition) newRange = new Range(change.newTailScreenPosition, change.newHeadScreenPosition) - if oldRange.intersectsRowRange(startRow, endRow - 1) + if oldRange.intersectsRowRange(@startRow, @endRow - 1) @removeFromLineDecorationCaches(decoration, oldRange) intersectsVisibleRowRange = true - if newRange.intersectsRowRange(startRow, endRow - 1) + if newRange.intersectsRowRange(@startRow, @endRow - 1) @addToLineDecorationCaches(decoration, newRange) intersectsVisibleRowRange = true @@ -730,11 +776,9 @@ class TextEditorPresenter @highlightDecorationsById = {} visibleHighlights = {} - startRow = @computeStartRow() - endRow = @computeEndRow() - return unless 0 <= startRow <= endRow <= Infinity + return unless 0 <= @startRow <= @endRow <= Infinity - for markerId, decorations of @model.decorationsForScreenRowRange(startRow, endRow - 1) + for markerId, decorations of @model.decorationsForScreenRowRange(@startRow, @endRow - 1) range = @model.getMarker(markerId).getScreenRange() for decoration in decorations if decoration.isType('line') or decoration.isType('line-number') @@ -780,22 +824,20 @@ class TextEditorPresenter updateHighlightState: (decoration) -> return unless @hasRequiredMeasurements() - startRow = @computeStartRow() - endRow = @computeEndRow() properties = decoration.getProperties() marker = decoration.getMarker() range = marker.getScreenRange() - if decoration.isDestroyed() or not marker.isValid() or range.isEmpty() or not range.intersectsRowRange(startRow, endRow - 1) + if decoration.isDestroyed() or not marker.isValid() or range.isEmpty() or not range.intersectsRowRange(@startRow, @endRow - 1) delete @state.content.highlights[decoration.id] @emitter.emit 'did-update-state' return - if range.start.row < startRow - range.start.row = startRow + if range.start.row < @startRow + range.start.row = @startRow range.start.column = 0 - if range.end.row >= endRow - range.end.row = endRow + if range.end.row >= @endRow + range.end.row = @endRow range.end.column = 0 if range.isEmpty()