diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee
index 2e914048e..77503cd00 100644
--- a/spec/text-editor-component-spec.coffee
+++ b/spec/text-editor-component-spec.coffee
@@ -450,6 +450,31 @@ describe "TextEditorComponent", ->
nextAnimationFrame()
expect(component.lineNodeForScreenRow(10).innerHTML).toBe 'CE'
+ it "keeps rebuilding lines when continuous reflow is on", ->
+ atom.config.set("editor.continuousReflow", true)
+ nextAnimationFrame()
+
+ oldLineNodes = componentNode.querySelectorAll(".line")
+
+ advanceClock(10)
+ expect(nextAnimationFrame).toBe(noAnimationFrame)
+
+ advanceClock(component.presenter.minimumReflowInterval - 10)
+ nextAnimationFrame()
+
+ newLineNodes = componentNode.querySelectorAll(".line")
+ expect(oldLineNodes).not.toEqual(newLineNodes)
+ oldLineNodes = newLineNodes
+
+ atom.config.set("editor.continuousReflow", false)
+ nextAnimationFrame()
+
+ newLineNodes = componentNode.querySelectorAll(".line")
+ expect(oldLineNodes).toEqual(newLineNodes)
+
+ advanceClock(component.presenter.minimumReflowInterval)
+ expect(nextAnimationFrame).toBe(noAnimationFrame)
+
describe "when soft wrapping is enabled", ->
beforeEach ->
editor.setText "a line that wraps \n"
@@ -827,6 +852,31 @@ describe "TextEditorComponent", ->
expect(componentNode.querySelector('.gutter').style.display).toBe ''
expect(component.lineNumberNodeForScreenRow(3)?).toBe true
+ it "keeps rebuilding line numbers when continuous reflow is on", ->
+ atom.config.set("editor.continuousReflow", true)
+ nextAnimationFrame()
+
+ oldLineNodes = componentNode.querySelectorAll(".line-number")
+
+ advanceClock(10)
+ expect(nextAnimationFrame).toBe(noAnimationFrame)
+
+ advanceClock(component.presenter.minimumReflowInterval - 10)
+ nextAnimationFrame()
+
+ newLineNodes = componentNode.querySelectorAll(".line-number")
+ expect(oldLineNodes).not.toEqual(newLineNodes)
+ oldLineNodes = newLineNodes
+
+ atom.config.set("editor.continuousReflow", false)
+ nextAnimationFrame()
+
+ newLineNodes = componentNode.querySelectorAll(".line-number")
+ expect(oldLineNodes).toEqual(newLineNodes)
+
+ advanceClock(component.presenter.minimumReflowInterval)
+ expect(nextAnimationFrame).toBe(noAnimationFrame)
+
describe "fold decorations", ->
describe "rendering fold decorations", ->
it "adds the foldable class to line numbers when the line is foldable", ->
diff --git a/src/config-schema.coffee b/src/config-schema.coffee
index d8eeb6fe3..7f70a0c98 100644
--- a/src/config-schema.coffee
+++ b/src/config-schema.coffee
@@ -217,6 +217,10 @@ module.exports =
type: 'boolean'
default: process.platform isnt 'darwin'
description: 'Increase/decrease the editor font size when pressing the Ctrl key and scrolling the mouse up/down.'
+ continuousReflow:
+ type: 'boolean'
+ default: false
+ description: 'Track down expensive layouts or style recalculations by continously reflowing the editor. (Has performance overhead)'
if process.platform in ['win32', 'linux']
module.exports.core.properties.autoHideMenuBar =
diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee
index 8eef6bd27..c86c36951 100644
--- a/src/line-number-gutter-component.coffee
+++ b/src/line-number-gutter-component.coffee
@@ -62,6 +62,9 @@ class LineNumberGutterComponent extends TiledComponent
buildComponentForTile: (id) -> new LineNumbersTileComponent({id})
+ shouldRecreateAllTilesOnUpdate: ->
+ @newState.continuousReflow
+
###
Section: Private Methods
###
diff --git a/src/lines-component.coffee b/src/lines-component.coffee
index 2b80235dc..40c4daa8a 100644
--- a/src/lines-component.coffee
+++ b/src/lines-component.coffee
@@ -32,7 +32,7 @@ class LinesComponent extends TiledComponent
@domNode
shouldRecreateAllTilesOnUpdate: ->
- @oldState.indentGuidesVisible isnt @newState.indentGuidesVisible
+ @oldState.indentGuidesVisible isnt @newState.indentGuidesVisible or @newState.continuousReflow
beforeUpdateSync: (state) ->
if @newState.maxHeight isnt @oldState.maxHeight
diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee
index 2c0900092..7bacfb466 100644
--- a/src/text-editor-presenter.coffee
+++ b/src/text-editor-presenter.coffee
@@ -11,6 +11,7 @@ class TextEditorPresenter
mouseWheelScreenRow: null
scopedCharacterWidthsChangeCount: 0
overlayDimensions: {}
+ minimumReflowInterval: 200
constructor: (params) ->
{@model, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft, @boundingClientRect, @windowWidth, @windowHeight, @gutterWidth} = params
@@ -35,6 +36,7 @@ class TextEditorPresenter
@observeConfig()
@buildState()
@startBlinkingCursors() if @focused
+ @startReflowing() if @continuousReflow
@updating = false
destroy: ->
@@ -72,6 +74,7 @@ class TextEditorPresenter
@updateStartRow()
@updateEndRow()
@updateCommonGutterState()
+ @updateReflowState()
@updateFocusedState() if @shouldUpdateFocusedState
@updateHeightState() if @shouldUpdateHeightState
@@ -169,6 +172,7 @@ class TextEditorPresenter
observeConfig: ->
configParams = {scope: @model.getRootScopeDescriptor()}
+ @continuousReflow = atom.config.get('editor.continuousReflow', configParams)
@scrollPastEnd = atom.config.get('editor.scrollPastEnd', configParams)
@showLineNumbers = atom.config.get('editor.showLineNumbers', configParams)
@showIndentGuide = atom.config.get('editor.showIndentGuide', configParams)
@@ -199,6 +203,16 @@ class TextEditorPresenter
@emitDidUpdateState()
+ @configDisposables.add atom.config.onDidChange 'editor.continuousReflow', configParams, ({newValue}) =>
+ @continuousReflow = newValue
+
+ if @continuousReflow
+ @startReflowing()
+ else
+ @stopReflowing()
+
+ @emitDidUpdateState()
+
didChangeGrammar: ->
@observeConfig()
@shouldUpdateContentState = true
@@ -254,6 +268,17 @@ class TextEditorPresenter
@resetTrackedUpdates()
+ updateReflowState: ->
+ @state.content.continuousReflow = @continuousReflow
+ @lineNumberGutter.continuousReflow = @continuousReflow
+
+ startReflowing: ->
+ @reflowingInterval = setInterval(@emitDidUpdateState.bind(this), @minimumReflowInterval)
+
+ stopReflowing: ->
+ clearInterval(@reflowingInterval)
+ @reflowingInterval = null
+
updateFocusedState: ->
@state.focused = @focused