diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 883749aab..5b2e6d78d 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -220,10 +220,36 @@ describe "TextEditorPresenter", -> expect(stateFn(presenter).tiles[0]).toBeDefined() describe "during state retrieval", -> - it "does not trigger onDidUpdateState events", -> + it "does not trigger ::onDidUpdateState events", -> presenter = buildPresenter() expectNoStateUpdate presenter, -> presenter.getState() + it "triggers ::onWillMeasure events before computing any state that needs measurement", -> + editor.setCursorBufferPosition([0, 0]) + cursorLine = editor.tokenizedLineForScreenRow(0) + called = false + + onWillMeasureSpy = (state) -> + called = true + expect(Object.keys(state.content.tiles).length).toBeGreaterThan(0) + for tile, tileState of state.content.tiles + expect(tileState.highlights).toEqual({}) + expect(state.content.tiles[0].lines[cursorLine.id].decorationClasses).not.toBeNull() + + expect(state.gutters).toEqual([]) + expect(state.hiddenInput).toEqual({}) + expect(state.content.overlays).toEqual({}) + expect(state.content.cursors).toEqual({}) + expect(state.content.width).toBeUndefined() + expect(state.content.scrollWidth).toBeUndefined() + + presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2) + + presenter.onWillMeasure(onWillMeasureSpy) + presenter.getState() + + expect(called).toBe(true) + describe ".horizontalScrollbar", -> describe ".visible", -> it "is true if the scrollWidth exceeds the computed client width", -> diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index a82c88825..87a030217 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -43,6 +43,10 @@ class TextEditorPresenter destroy: -> @disposables.dispose() + # Calls your `callback` while performing ::getState(), before computing any state that needs measurements. + onWillMeasure: (callback) -> + @emitter.on "will-measure", callback + # Calls your `callback` when some changes in the model occurred and the current state has been updated. onDidUpdateState: (callback) -> @emitter.on 'did-update-state', callback @@ -74,8 +78,13 @@ class TextEditorPresenter @updateScrollbarDimensions() @updateStartRow() @updateEndRow() - @updateCommonGutterState() + @updateLinesDecorations() if @shouldUpdateDecorations + @updateTilesState() if @shouldUpdateLinesState or @shouldUpdateLineNumbersState + + @emitter.emit "will-measure", @state + + @updateCommonGutterState() @updateHorizontalDimensions() @updateFocusedState() if @shouldUpdateFocusedState @@ -85,8 +94,7 @@ class TextEditorPresenter @updateScrollbarsState() if @shouldUpdateScrollbarsState @updateHiddenInputState() if @shouldUpdateHiddenInputState @updateContentState() if @shouldUpdateContentState - @updateDecorations() if @shouldUpdateDecorations - @updateTilesState() if @shouldUpdateLinesState or @shouldUpdateLineNumbersState + @updateHighlightDecorations() if @shouldUpdateDecorations @updateCursorsState() if @shouldUpdateCursorsState @updateOverlaysState() if @shouldUpdateOverlaysState @updateLineNumberGutterState() if @shouldUpdateLineNumberGutterState @@ -240,6 +248,7 @@ class TextEditorPresenter tiles: {} highlights: {} overlays: {} + cursors: {} gutters: [] # Shared state that is copied into ``@state.gutters`. @sharedGutterStyles = {} @@ -1144,22 +1153,28 @@ class TextEditorPresenter @emitDidUpdateState() - updateDecorations: -> + updateLinesDecorations: -> @rangesByDecorationId = {} @lineDecorationsByScreenRow = {} @lineNumberDecorationsByScreenRow = {} @customGutterDecorationsByGutterNameAndScreenRow = {} + + return unless 0 <= @startRow <= @endRow <= Infinity + + for markerId, decorations of @model.decorationsForScreenRowRange(@startRow, @endRow - 1) + range = @model.getMarker(markerId).getScreenRange() + for decoration in decorations when decoration.isType('line') or decoration.isType('gutter') + @addToLineDecorationCaches(decoration, range) + + updateHighlightDecorations: -> @visibleHighlights = {} return unless 0 <= @startRow <= @endRow <= Infinity 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('gutter') - @addToLineDecorationCaches(decoration, range) - else if decoration.isType('highlight') - @updateHighlightState(decoration, range) + for decoration in decorations when decoration.isType('highlight') + @updateHighlightState(decoration, range) for tileId, tileState of @state.content.tiles for id, highlight of tileState.highlights