diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 3643939a6..aa69a287b 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -18,13 +18,63 @@ describe "TextEditorPresenter", -> for key, value of expected expect(actual[key]).toBe value - describe "lines", -> + describe "::state.content", -> + describe "on initialization", -> + it "assigns .scrollWidth based on the clientWidth and the width of the longest line", -> + maxLineLength = editor.getMaxScreenLineLength() + + presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, baseCharacterWidth: 10, lineHeight: 10, lineOverdrawMargin: 0) + expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 1 + + presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 10 * maxLineLength + 20, scrollTop: 0, baseCharacterWidth: 10, lineHeight: 10, lineOverdrawMargin: 0) + expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 20 + + describe "when the ::clientWidth changes", -> + it "updates .scrollWidth", -> + maxLineLength = editor.getMaxScreenLineLength() + presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, scrollWidth: 70, lineHeight: 10, baseCharacterWidth: 10, lineOverdrawMargin: 0) + + expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 1 + presenter.setClientWidth(10 * maxLineLength + 20) + expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 20 + + describe "when the ::baseCharacterWidth changes", -> + it "updates the width of the lines if it changes the ::scrollWidth", -> + maxLineLength = editor.getMaxScreenLineLength() + presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, scrollWidth: 70, lineHeight: 10, baseCharacterWidth: 10, lineOverdrawMargin: 0) + + expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 1 + presenter.setBaseCharacterWidth(15) + expect(presenter.state.content.scrollWidth).toBe 15 * maxLineLength + 1 + + describe "when the scoped character widths change", -> + beforeEach -> + waitsForPromise -> atom.packages.activatePackage('language-javascript') + + it "updates the width of the lines if the ::scrollWidth changes", -> + maxLineLength = editor.getMaxScreenLineLength() + presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, scrollWidth: 70, lineHeight: 10, baseCharacterWidth: 10, lineOverdrawMargin: 0) + + expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 1 + presenter.setScopedCharWidth(['source.js', 'support.function.js'], 'p', 20) + expect(presenter.state.content.scrollWidth).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 # 2 of the characters are 20px wide now instead of 10px wide + + describe "when ::softWrapped changes on the editor", -> + it "only accounts for the cursor in .scrollWidth if ::softWrapped is false", -> + presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, scrollWidth: 70, lineHeight: 10, baseCharacterWidth: 10, lineOverdrawMargin: 0) + expect(presenter.state.content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + editor.setSoftWrapped(true) + expect(presenter.state.content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + editor.setSoftWrapped(false) + expect(presenter.state.content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + + describe "::state.content.lines", -> describe "on initialization", -> it "contains the lines that are visible on screen, plus the overdraw margin", -> presenter = new TextEditorPresenter(model: editor, clientHeight: 25, scrollTop: 0, lineHeight: 10, lineOverdrawMargin: 1) line0 = editor.tokenizedLineForScreenRow(0) - expectValues presenter.state.lines[line0.id], { + expectValues presenter.state.content.lines[line0.id], { screenRow: 0 text: line0.text tokens: line0.tokens @@ -32,7 +82,7 @@ describe "TextEditorPresenter", -> } line1 = editor.tokenizedLineForScreenRow(1) - expectValues presenter.state.lines[line1.id], { + expectValues presenter.state.content.lines[line1.id], { screenRow: 1 text: line1.text tokens: line1.tokens @@ -40,7 +90,7 @@ describe "TextEditorPresenter", -> } line2 = editor.tokenizedLineForScreenRow(2) - expectValues presenter.state.lines[line2.id], { + expectValues presenter.state.content.lines[line2.id], { screenRow: 2 text: line2.text tokens: line2.tokens @@ -49,7 +99,7 @@ describe "TextEditorPresenter", -> # this row is rendered due to the overdraw margin line3 = editor.tokenizedLineForScreenRow(3) - expectValues presenter.state.lines[line3.id], { + expectValues presenter.state.content.lines[line3.id], { screenRow: 3 text: line3.text tokens: line3.tokens @@ -61,7 +111,7 @@ describe "TextEditorPresenter", -> # this row is rendered due to the overdraw margin line10 = editor.tokenizedLineForScreenRow(10) - expectValues presenter.state.lines[line10.id], { + expectValues presenter.state.content.lines[line10.id], { screenRow: 10 text: line10.text tokens: line10.tokens @@ -69,7 +119,7 @@ describe "TextEditorPresenter", -> } line11 = editor.tokenizedLineForScreenRow(11) - expectValues presenter.state.lines[line11.id], { + expectValues presenter.state.content.lines[line11.id], { screenRow: 11 text: line11.text tokens: line11.tokens @@ -77,7 +127,7 @@ describe "TextEditorPresenter", -> } line12 = editor.tokenizedLineForScreenRow(12) - expectValues presenter.state.lines[line12.id], { + expectValues presenter.state.content.lines[line12.id], { screenRow: 12 text: line12.text tokens: line12.tokens @@ -88,43 +138,26 @@ describe "TextEditorPresenter", -> it "contains the lines that are visible on screen, plus and minus the overdraw margin", -> presenter = new TextEditorPresenter(model: editor, clientHeight: 25, scrollTop: 50, lineHeight: 10, lineOverdrawMargin: 1) - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(3).id]).toBeUndefined() - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(4).id]).toBeDefined() - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(9).id]).toBeDefined() - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(10).id]).toBeUndefined() + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(3).id]).toBeUndefined() + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(4).id]).toBeDefined() + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(9).id]).toBeDefined() + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(10).id]).toBeUndefined() it "reports all lines as visible if no external ::clientHeight is assigned", -> presenter = new TextEditorPresenter(model: editor, scrollTop: 0, lineHeight: 10, lineOverdrawMargin: 1) - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(0).id]).toBeDefined() - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(12).id]).toBeDefined() - - it "uses the computed scrollWidth as the length of each line", -> - line0 = editor.tokenizedLineForScreenRow(0) - line1 = editor.tokenizedLineForScreenRow(1) - line2 = editor.tokenizedLineForScreenRow(2) - - maxLineLength = editor.getMaxScreenLineLength() - - presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, baseCharacterWidth: 10, lineHeight: 10, lineOverdrawMargin: 0) - expect(presenter.state.lines[line0.id].width).toBe 10 * maxLineLength + 1 - expect(presenter.state.lines[line1.id].width).toBe 10 * maxLineLength + 1 - expect(presenter.state.lines[line2.id].width).toBe 10 * maxLineLength + 1 - - presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 10 * maxLineLength + 20, scrollTop: 0, baseCharacterWidth: 10, lineHeight: 10, lineOverdrawMargin: 0) - expect(presenter.state.lines[line0.id].width).toBe 10 * maxLineLength + 20 - expect(presenter.state.lines[line1.id].width).toBe 10 * maxLineLength + 20 - expect(presenter.state.lines[line2.id].width).toBe 10 * maxLineLength + 20 + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(0).id]).toBeDefined() + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(12).id]).toBeDefined() it "includes the endOfLineInvisibles in the line state", -> editor.setText("hello\nworld\r\n") presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, baseCharacterWidth: 10, lineHeight: 10, lineOverdrawMargin: 0) - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(0).id].endOfLineInvisibles).toBeNull() - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(1).id].endOfLineInvisibles).toBeNull() + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(0).id].endOfLineInvisibles).toBeNull() + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(1).id].endOfLineInvisibles).toBeNull() atom.config.set('editor.showInvisibles', true) presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, baseCharacterWidth: 10, lineHeight: 10, lineOverdrawMargin: 0) - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(0).id].endOfLineInvisibles).toEqual [atom.config.get('editor.invisibles.eol')] - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(1).id].endOfLineInvisibles).toEqual [atom.config.get('editor.invisibles.cr'), atom.config.get('editor.invisibles.eol')] + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(0).id].endOfLineInvisibles).toEqual [atom.config.get('editor.invisibles.eol')] + expect(presenter.state.content.lines[editor.tokenizedLineForScreenRow(1).id].endOfLineInvisibles).toEqual [atom.config.get('editor.invisibles.cr'), atom.config.get('editor.invisibles.eol')] describe "when ::scrollTop changes", -> it "updates the lines that are visible on screen", -> @@ -132,10 +165,10 @@ describe "TextEditorPresenter", -> presenter.setScrollTop(25) line0 = editor.tokenizedLineForScreenRow(0) - expect(presenter.state.lines[line0.id]).toBeUndefined() + expect(presenter.state.content.lines[line0.id]).toBeUndefined() line1 = editor.tokenizedLineForScreenRow(1) - expectValues presenter.state.lines[line1.id], { + expectValues presenter.state.content.lines[line1.id], { screenRow: 1 text: line1.text tokens: line1.tokens @@ -143,7 +176,7 @@ describe "TextEditorPresenter", -> } line2 = editor.tokenizedLineForScreenRow(2) - expectValues presenter.state.lines[line2.id], { + expectValues presenter.state.content.lines[line2.id], { screenRow: 2 text: line2.text tokens: line2.tokens @@ -151,7 +184,7 @@ describe "TextEditorPresenter", -> } line3 = editor.tokenizedLineForScreenRow(3) - expectValues presenter.state.lines[line3.id], { + expectValues presenter.state.content.lines[line3.id], { screenRow: 3 text: line3.text tokens: line3.tokens @@ -159,7 +192,7 @@ describe "TextEditorPresenter", -> } line4 = editor.tokenizedLineForScreenRow(4) - expectValues presenter.state.lines[line4.id], { + expectValues presenter.state.content.lines[line4.id], { screenRow: 4 text: line4.text tokens: line4.tokens @@ -171,12 +204,12 @@ describe "TextEditorPresenter", -> presenter = new TextEditorPresenter(model: editor, clientHeight: 15, scrollTop: 15, lineHeight: 10, lineOverdrawMargin: 1) line5 = editor.tokenizedLineForScreenRow(5) - expect(presenter.state.lines[line5.id]).toBeUndefined() + expect(presenter.state.content.lines[line5.id]).toBeUndefined() presenter.setClientHeight(35) line1 = editor.tokenizedLineForScreenRow(1) - expectValues presenter.state.lines[line1.id], { + expectValues presenter.state.content.lines[line1.id], { screenRow: 1 text: line1.text tokens: line1.tokens @@ -184,7 +217,7 @@ describe "TextEditorPresenter", -> } line2 = editor.tokenizedLineForScreenRow(2) - expectValues presenter.state.lines[line2.id], { + expectValues presenter.state.content.lines[line2.id], { screenRow: 2 text: line2.text tokens: line2.tokens @@ -192,7 +225,7 @@ describe "TextEditorPresenter", -> } line3 = editor.tokenizedLineForScreenRow(3) - expectValues presenter.state.lines[line3.id], { + expectValues presenter.state.content.lines[line3.id], { screenRow: 3 text: line3.text tokens: line3.tokens @@ -200,14 +233,14 @@ describe "TextEditorPresenter", -> } line4 = editor.tokenizedLineForScreenRow(4) - expectValues presenter.state.lines[line4.id], { + expectValues presenter.state.content.lines[line4.id], { screenRow: 4 text: line4.text tokens: line4.tokens top: 10 * 4 } - expectValues presenter.state.lines[line4.id], { + expectValues presenter.state.content.lines[line4.id], { screenRow: 4 text: line4.text tokens: line4.tokens @@ -225,105 +258,45 @@ describe "TextEditorPresenter", -> line5 = editor.tokenizedLineForScreenRow(5) line6 = editor.tokenizedLineForScreenRow(6) - expect(presenter.state.lines[line1.id]).toBeDefined() - expect(presenter.state.lines[line2.id]).toBeDefined() - expect(presenter.state.lines[line3.id]).toBeDefined() - expect(presenter.state.lines[line4.id]).toBeUndefined() - expect(presenter.state.lines[line5.id]).toBeUndefined() + expect(presenter.state.content.lines[line1.id]).toBeDefined() + expect(presenter.state.content.lines[line2.id]).toBeDefined() + expect(presenter.state.content.lines[line3.id]).toBeDefined() + expect(presenter.state.content.lines[line4.id]).toBeUndefined() + expect(presenter.state.content.lines[line5.id]).toBeUndefined() presenter.setLineHeight(5) - expect(presenter.state.lines[line1.id]).toBeUndefined() + expect(presenter.state.content.lines[line1.id]).toBeUndefined() - expectValues presenter.state.lines[line2.id], { + expectValues presenter.state.content.lines[line2.id], { screenRow: 2 text: line2.text tokens: line2.tokens top: 5 * 2 } - expectValues presenter.state.lines[line3.id], { + expectValues presenter.state.content.lines[line3.id], { screenRow: 3 text: line3.text tokens: line3.tokens top: 5 * 3 } - expectValues presenter.state.lines[line4.id], { + expectValues presenter.state.content.lines[line4.id], { screenRow: 4 text: line4.text tokens: line4.tokens top: 5 * 4 } - expectValues presenter.state.lines[line5.id], { + expectValues presenter.state.content.lines[line5.id], { screenRow: 5 text: line5.text tokens: line5.tokens top: 5 * 5 } - expect(presenter.state.lines[line6.id]).toBeUndefined() - - describe "when the ::clientWidth changes", -> - it "updates the width of the lines if it changes the ::scrollWidth", -> - line0 = editor.tokenizedLineForScreenRow(0) - line1 = editor.tokenizedLineForScreenRow(1) - line2 = editor.tokenizedLineForScreenRow(2) - - maxLineLength = editor.getMaxScreenLineLength() - - presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, scrollWidth: 70, lineHeight: 10, baseCharacterWidth: 10, lineOverdrawMargin: 0) - expect(presenter.state.lines[line0.id].width).toBe 10 * maxLineLength + 1 - expect(presenter.state.lines[line1.id].width).toBe 10 * maxLineLength + 1 - expect(presenter.state.lines[line2.id].width).toBe 10 * maxLineLength + 1 - - presenter.setClientWidth(10 * maxLineLength + 20) - - expect(presenter.state.lines[line0.id].width).toBe 10 * maxLineLength + 20 - expect(presenter.state.lines[line1.id].width).toBe 10 * maxLineLength + 20 - expect(presenter.state.lines[line2.id].width).toBe 10 * maxLineLength + 20 - - describe "when the scoped character widths change", -> - beforeEach -> - waitsForPromise -> atom.packages.activatePackage('language-javascript') - - it "updates the width of the lines if the ::scrollWidth changes", -> - line0 = editor.tokenizedLineForScreenRow(0) - line1 = editor.tokenizedLineForScreenRow(1) - line2 = editor.tokenizedLineForScreenRow(2) - - maxLineLength = editor.getMaxScreenLineLength() - - presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, scrollWidth: 70, lineHeight: 10, baseCharacterWidth: 10, lineOverdrawMargin: 0) - expect(presenter.state.lines[line0.id].width).toBe 10 * maxLineLength + 1 - expect(presenter.state.lines[line1.id].width).toBe 10 * maxLineLength + 1 - expect(presenter.state.lines[line2.id].width).toBe 10 * maxLineLength + 1 - - presenter.setScopedCharWidth(['source.js', 'support.function.js'], 'p', 20) - - expect(presenter.state.lines[line0.id].width).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 # 2 of the characters are 20px wide now instead of 10px wide - expect(presenter.state.lines[line1.id].width).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 - expect(presenter.state.lines[line2.id].width).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 - - describe "when the ::baseCharacterWidth changes", -> - it "updates the width of the lines if it changes the ::scrollWidth", -> - line0 = editor.tokenizedLineForScreenRow(0) - line1 = editor.tokenizedLineForScreenRow(1) - line2 = editor.tokenizedLineForScreenRow(2) - - maxLineLength = editor.getMaxScreenLineLength() - - presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, scrollWidth: 70, lineHeight: 10, baseCharacterWidth: 10, lineOverdrawMargin: 0) - expect(presenter.state.lines[line0.id].width).toBe 10 * maxLineLength + 1 - expect(presenter.state.lines[line1.id].width).toBe 10 * maxLineLength + 1 - expect(presenter.state.lines[line2.id].width).toBe 10 * maxLineLength + 1 - - presenter.setBaseCharacterWidth(15) - - expect(presenter.state.lines[line0.id].width).toBe 15 * maxLineLength + 1 - expect(presenter.state.lines[line1.id].width).toBe 15 * maxLineLength + 1 - expect(presenter.state.lines[line2.id].width).toBe 15 * maxLineLength + 1 + expect(presenter.state.content.lines[line6.id]).toBeUndefined() describe "when the editor's content changes", -> it "updates the lines state accordingly", -> @@ -332,7 +305,7 @@ describe "TextEditorPresenter", -> buffer.insert([2, 0], "hello\nworld\n") line1 = editor.tokenizedLineForScreenRow(1) - expectValues presenter.state.lines[line1.id], { + expectValues presenter.state.content.lines[line1.id], { screenRow: 1 text: line1.text tokens: line1.tokens @@ -340,7 +313,7 @@ describe "TextEditorPresenter", -> } line2 = editor.tokenizedLineForScreenRow(2) - expectValues presenter.state.lines[line2.id], { + expectValues presenter.state.content.lines[line2.id], { screenRow: 2 text: line2.text tokens: line2.tokens @@ -348,18 +321,9 @@ describe "TextEditorPresenter", -> } line3 = editor.tokenizedLineForScreenRow(3) - expectValues presenter.state.lines[line3.id], { + expectValues presenter.state.content.lines[line3.id], { screenRow: 3 text: line3.text tokens: line3.tokens top: 10 * 3 } - - describe "when ::softWrapped changes on the editor", -> - it "only accounts for the cursor width if ::softWrapped is false", -> - presenter = new TextEditorPresenter(model: editor, clientHeight: 25, clientWidth: 50, scrollTop: 0, scrollWidth: 70, lineHeight: 10, baseCharacterWidth: 10, lineOverdrawMargin: 0) - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(0).id].width).toBe 10 * editor.getMaxScreenLineLength() + 1 - editor.setSoftWrapped(true) - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(0).id].width).toBe 10 * editor.getMaxScreenLineLength() - editor.setSoftWrapped(false) - expect(presenter.state.lines[editor.tokenizedLineForScreenRow(0).id].width).toBe 10 * editor.getMaxScreenLineLength() + 1 diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 870bbe726..2b4a5c5bf 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -89,32 +89,31 @@ LinesComponent = React.createClass @lineIdsByScreenRow = {} removeLineNodes: -> - @removeLineNode(id) for id of @oldState + @removeLineNode(id) for id of @oldState.lines removeLineNode: (id) -> @lineNodesByLineId[id].remove() delete @lineNodesByLineId[id] delete @lineIdsByScreenRow[@screenRowsByLineId[id]] delete @screenRowsByLineId[id] - delete @oldState[id] + delete @oldState.lines[id] updateLineNodes: -> {presenter, lineDecorations, mouseWheelScreenRow} = @props - @newState = presenter?.state.lines + return unless @newState = presenter?.state.content - return unless @newState? - @oldState ?= {} + @oldState ?= {lines: {}} @lineNodesByLineId ?= {} - for id of @oldState - unless @newState.hasOwnProperty(id) or mouseWheelScreenRow is @screenRowsByLineId[id] + for id of @oldState.lines + unless @newState.lines.hasOwnProperty(id) or mouseWheelScreenRow is @screenRowsByLineId[id] @removeLineNode(id) newLineIds = null newLinesHTML = null - for id, lineState of @newState - if @oldState.hasOwnProperty(id) + for id, lineState of @newState.lines + if @oldState.lines.hasOwnProperty(id) @updateLineNode(id) else newLineIds ?= [] @@ -123,10 +122,12 @@ LinesComponent = React.createClass newLinesHTML += @buildLineHTML(id) @screenRowsByLineId[id] = lineState.screenRow @lineIdsByScreenRow[lineState.screenRow] = id - @oldState[id] = _.clone(lineState) + @oldState.lines[id] = _.clone(lineState) @renderedDecorationsByLineId[id] = lineDecorations[lineState.screenRow] + @oldState.scrollWidth = @newState.scrollWidth + return unless newLineIds? WrapperDiv.innerHTML = newLinesHTML @@ -139,7 +140,8 @@ LinesComponent = React.createClass buildLineHTML: (id) -> {presenter, showIndentGuide, lineHeightInPixels, lineDecorations} = @props - {screenRow, tokens, text, top, width, lineEnding, fold, isSoftWrapped, indentLevel} = @newState[id] + {scrollWidth} = @newState + {screenRow, tokens, text, top, lineEnding, fold, isSoftWrapped, indentLevel} = @newState.lines[id] classes = '' if decorations = lineDecorations[screenRow] @@ -148,7 +150,7 @@ LinesComponent = React.createClass classes += decoration.class + ' ' classes += 'line' - lineHTML = "
" + lineHTML = "
" if text is "" lineHTML += @buildEmptyLineInnerHTML(id) @@ -161,7 +163,7 @@ LinesComponent = React.createClass buildEmptyLineInnerHTML: (id) -> {showIndentGuide} = @props - {indentLevel, tabLength, endOfLineInvisibles} = @newState[id] + {indentLevel, tabLength, endOfLineInvisibles} = @newState.lines[id] if showIndentGuide and indentLevel > 0 invisibleIndex = 0 @@ -184,7 +186,7 @@ LinesComponent = React.createClass buildLineInnerHTML: (id) -> {editor, showIndentGuide} = @props - {tokens, text} = @newState[id] + {tokens, text} = @newState.lines[id] innerHTML = "" scopeStack = [] @@ -200,7 +202,7 @@ LinesComponent = React.createClass innerHTML buildEndOfLineHTML: (id) -> - {endOfLineInvisibles} = @newState[id] + {endOfLineInvisibles} = @newState.lines[id] html = '' if endOfLineInvisibles? @@ -234,9 +236,9 @@ LinesComponent = React.createClass "" updateLineNode: (id) -> - {lineHeightInPixels, lineDecorations} = @props - {screenRow, top, width} = @newState[id] + {scrollWidth} = @newState + {screenRow, top} = @newState.lines[id] lineNode = @lineNodesByLineId[id] @@ -254,7 +256,7 @@ LinesComponent = React.createClass if Decoration.isType(decoration, 'line') and not @hasDecoration(previousDecorations, decoration) lineNode.classList.add(decoration.class) - lineNode.style.width = width + 'px' + lineNode.style.width = scrollWidth + 'px' lineNode.style.top = top + 'px' lineNode.dataset.screenRow = screenRow @screenRowsByLineId[id] = screenRow @@ -291,7 +293,7 @@ LinesComponent = React.createClass node = @getDOMNode() editor.batchCharacterMeasurement => - for id, lineState of @oldState + for id, lineState of @oldState.lines unless @measuredLines.has(id) lineNode = @lineNodesByLineId[id] @measureCharactersInLine(lineState, lineNode) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 37fb4ca78..f506b3a48 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -5,10 +5,9 @@ module.exports = class TextEditorPresenter constructor: ({@model, @clientHeight, @clientWidth, @scrollTop, @lineHeight, @baseCharacterWidth, @lineOverdrawMargin}) -> @disposables = new CompositeDisposable - @state = {} @charWidthsByScope = {} @subscribeToModel() - @buildLinesState() + @buildState() destroy: -> @disposables.dispose() @@ -16,13 +15,24 @@ class TextEditorPresenter subscribeToModel: -> @disposables.add @model.onDidChange(@updateLinesState.bind(this)) @disposables.add @model.onDidChangeSoftWrapped => - @computeScrollWidth() + @updateContentState() @updateLinesState() + buildState: -> + @state = {} + @buildContentState() + @buildLinesState() + + buildContentState: -> + @state.content = {scrollWidth: @computeScrollWidth()} + buildLinesState: -> - @state.lines = {} + @state.content.lines = {} @updateLinesState() + updateContentState: -> + @state.content.scrollWidth = @computeScrollWidth() + updateLinesState: -> visibleLineIds = {} startRow = @getStartRow() @@ -32,24 +42,23 @@ class TextEditorPresenter while row < endRow line = @model.tokenizedLineForScreenRow(row) visibleLineIds[line.id] = true - if @state.lines.hasOwnProperty(line.id) + if @state.content.lines.hasOwnProperty(line.id) @updateLineState(row, line) else @buildLineState(row, line) row++ - for id, line of @state.lines + for id, line of @state.content.lines unless visibleLineIds.hasOwnProperty(id) - delete @state.lines[id] + delete @state.content.lines[id] updateLineState: (row, line) -> - lineState = @state.lines[line.id] + lineState = @state.content.lines[line.id] lineState.screenRow = row lineState.top = row * @getLineHeight() - lineState.width = @getScrollWidth() buildLineState: (row, line) -> - @state.lines[line.id] = + @state.content.lines[line.id] = screenRow: row text: line.text tokens: line.tokens @@ -58,7 +67,6 @@ class TextEditorPresenter tabLength: line.tabLength fold: line.fold top: row * @getLineHeight() - width: @getScrollWidth() getStartRow: -> startRow = Math.floor(@getScrollTop() / @getLineHeight()) - @lineOverdrawMargin @@ -73,9 +81,7 @@ class TextEditorPresenter computeScrollWidth: -> contentWidth = @pixelPositionForScreenPosition([@model.getLongestScreenRow(), Infinity]).left contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width - @scrollWidth = Math.max(contentWidth, @getClientWidth()) - - getScrollWidth: -> @scrollWidth ? @computeScrollWidth() + Math.max(contentWidth, @getClientWidth()) setScrollTop: (@scrollTop) -> @updateLinesState() @@ -89,7 +95,7 @@ class TextEditorPresenter @clientHeight ? @model.getScreenLineCount() * @getLineHeight() setClientWidth: (@clientWidth) -> - @computeScrollWidth() + @updateContentState() @updateLinesState() getClientWidth: -> @clientWidth @@ -100,7 +106,7 @@ class TextEditorPresenter getLineHeight: -> @lineHeight setBaseCharacterWidth: (@baseCharacterWidth) -> - @computeScrollWidth() + @updateContentState() @updateLinesState() getBaseCharacterWidth: -> @baseCharacterWidth @@ -129,7 +135,7 @@ class TextEditorPresenter @characterWidthsChanged() unless @batchingCharacterMeasurement characterWidthsChanged: -> - @computeScrollWidth() + @updateContentState() @updateLinesState() clearScopedCharWidths: ->