diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 2ee966630..4ad319cc1 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -8,7 +8,7 @@ TextEditorPresenter = require '../src/text-editor-presenter' describe "TextEditorPresenter", -> # These `describe` and `it` blocks mirror the structure of the ::state object. # Please maintain this structure when adding specs for new state fields. - describe "::state", -> + describe "::getState()", -> [buffer, editor] = [] beforeEach -> @@ -43,13 +43,22 @@ describe "TextEditorPresenter", -> for key, value of expected expect(actual[key]).toEqual value - expectStateUpdate = (presenter, fn) -> + expectStateUpdatedToBe = (value, presenter, fn) -> updatedState = false disposable = presenter.onDidUpdateState -> updatedState = true disposable.dispose() fn() - expect(updatedState).toBe true + expect(updatedState).toBe(value) + + expectStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(true, presenter, fn) + + expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) + + describe "during state retrieval", -> + it "does not trigger onDidUpdateState events", -> + presenter = buildPresenter() + expectNoStateUpdate presenter, -> presenter.getState() describe ".horizontalScrollbar", -> describe ".visible", -> @@ -61,21 +70,20 @@ describe "TextEditorPresenter", -> lineHeight: 10 horizontalScrollbarHeight: 10 verticalScrollbarWidth: 10 - {state} = presenter - expect(state.horizontalScrollbar.visible).toBe false + expect(presenter.getState().horizontalScrollbar.visible).toBe false # ::contentFrameWidth itself is smaller than scrollWidth presenter.setContentFrameWidth(editor.getMaxScreenLineLength() * 10) - expect(state.horizontalScrollbar.visible).toBe true + expect(presenter.getState().horizontalScrollbar.visible).toBe true # restore... presenter.setContentFrameWidth(editor.getMaxScreenLineLength() * 10 + 1) - expect(state.horizontalScrollbar.visible).toBe false + expect(presenter.getState().horizontalScrollbar.visible).toBe false # visible vertical scrollbar makes the clientWidth smaller than the scrollWidth presenter.setExplicitHeight((editor.getLineCount() * 10) - 1) - expect(state.horizontalScrollbar.visible).toBe true + expect(presenter.getState().horizontalScrollbar.visible).toBe true it "is false if the editor is mini", -> presenter = buildPresenter @@ -83,18 +91,18 @@ describe "TextEditorPresenter", -> contentFrameWidth: editor.getMaxScreenLineLength() * 10 - 10 baseCharacterWidth: 10 - expect(presenter.state.horizontalScrollbar.visible).toBe true + expect(presenter.getState().horizontalScrollbar.visible).toBe true editor.setMini(true) - expect(presenter.state.horizontalScrollbar.visible).toBe false + expect(presenter.getState().horizontalScrollbar.visible).toBe false editor.setMini(false) - expect(presenter.state.horizontalScrollbar.visible).toBe true + expect(presenter.getState().horizontalScrollbar.visible).toBe true describe ".height", -> it "tracks the value of ::horizontalScrollbarHeight", -> presenter = buildPresenter(horizontalScrollbarHeight: 10) - expect(presenter.state.horizontalScrollbar.height).toBe 10 + expect(presenter.getState().horizontalScrollbar.height).toBe 10 expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(20) - expect(presenter.state.horizontalScrollbar.height).toBe 20 + expect(presenter.getState().horizontalScrollbar.height).toBe 20 describe ".right", -> it "is ::verticalScrollbarWidth if the vertical scrollbar is visible and 0 otherwise", -> @@ -105,37 +113,36 @@ describe "TextEditorPresenter", -> lineHeight: 10 horizontalScrollbarHeight: 10 verticalScrollbarWidth: 10 - {state} = presenter - expect(state.horizontalScrollbar.right).toBe 0 + expect(presenter.getState().horizontalScrollbar.right).toBe 0 presenter.setExplicitHeight((editor.getLineCount() * 10) - 1) - expect(state.horizontalScrollbar.right).toBe 10 + expect(presenter.getState().horizontalScrollbar.right).toBe 10 describe ".scrollWidth", -> it "is initialized as the max of the ::contentFrameWidth and the width of the longest line", -> maxLineLength = editor.getMaxScreenLineLength() presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1 presenter = buildPresenter(contentFrameWidth: 10 * maxLineLength + 20, baseCharacterWidth: 10) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 20 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 20 it "updates when the ::contentFrameWidth changes", -> maxLineLength = editor.getMaxScreenLineLength() presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1 expectStateUpdate presenter, -> presenter.setContentFrameWidth(10 * maxLineLength + 20) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 20 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 20 it "updates when the ::baseCharacterWidth changes", -> maxLineLength = editor.getMaxScreenLineLength() presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1 expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(15) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 15 * maxLineLength + 1 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 15 * maxLineLength + 1 it "updates when the scoped character widths change", -> waitsForPromise -> atom.packages.activatePackage('language-javascript') @@ -144,58 +151,58 @@ describe "TextEditorPresenter", -> maxLineLength = editor.getMaxScreenLineLength() presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1 expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['source.js', 'support.function.js'], 'p', 20) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 # 2 of the characters are 20px wide now instead of 10px wide + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 # 2 of the characters are 20px wide now instead of 10px wide it "updates when ::softWrapped changes on the editor", -> presenter = buildPresenter(contentFrameWidth: 470, baseCharacterWidth: 10) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 expectStateUpdate presenter, -> editor.setSoftWrapped(true) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe presenter.clientWidth + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe presenter.clientWidth expectStateUpdate presenter, -> editor.setSoftWrapped(false) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 it "updates when the longest line changes", -> presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 expectStateUpdate presenter, -> editor.setCursorBufferPosition([editor.getLongestScreenRow(), 0]) expectStateUpdate presenter, -> editor.insertText('xyz') - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 describe ".scrollLeft", -> it "tracks the value of ::scrollLeft", -> presenter = buildPresenter(scrollLeft: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe 10 + expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe 10 expectStateUpdate presenter, -> presenter.setScrollLeft(50) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe 50 + expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe 50 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.scrollWidth - presenter.clientWidth + expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setContentFrameWidth(600) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setVerticalScrollbarWidth(5) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> editor.getBuffer().delete([[6, 0], [6, Infinity]]) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth # Scroll top only gets smaller when needed as dimensions change, never bigger - scrollLeftBefore = presenter.state.horizontalScrollbar.scrollLeft + scrollLeftBefore = presenter.getState().horizontalScrollbar.scrollLeft expectStateUpdate presenter, -> editor.getBuffer().insert([6, 0], new Array(100).join('x')) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe scrollLeftBefore + expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe scrollLeftBefore it "never goes negative", -> presenter = buildPresenter(scrollLeft: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) expectStateUpdate presenter, -> presenter.setScrollLeft(-300) - expect(presenter.state.horizontalScrollbar.scrollLeft).toBe 0 + expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe 0 describe ".verticalScrollbar", -> describe ".visible", -> @@ -208,28 +215,27 @@ describe "TextEditorPresenter", -> lineHeight: 10 horizontalScrollbarHeight: 10 verticalScrollbarWidth: 10 - {state} = presenter - expect(state.verticalScrollbar.visible).toBe false + expect(presenter.getState().verticalScrollbar.visible).toBe false # ::explicitHeight itself is smaller than scrollWidth presenter.setExplicitHeight(editor.getLineCount() * 10 - 1) - expect(state.verticalScrollbar.visible).toBe true + expect(presenter.getState().verticalScrollbar.visible).toBe true # restore... presenter.setExplicitHeight(editor.getLineCount() * 10) - expect(state.verticalScrollbar.visible).toBe false + expect(presenter.getState().verticalScrollbar.visible).toBe false # visible horizontal scrollbar makes the clientHeight smaller than the scrollHeight presenter.setContentFrameWidth(editor.getMaxScreenLineLength() * 10) - expect(state.verticalScrollbar.visible).toBe true + expect(presenter.getState().verticalScrollbar.visible).toBe true describe ".width", -> it "is assigned based on ::verticalScrollbarWidth", -> presenter = buildPresenter(verticalScrollbarWidth: 10) - expect(presenter.state.verticalScrollbar.width).toBe 10 + expect(presenter.getState().verticalScrollbar.width).toBe 10 expectStateUpdate presenter, -> presenter.setVerticalScrollbarWidth(20) - expect(presenter.state.verticalScrollbar.width).toBe 20 + expect(presenter.getState().verticalScrollbar.width).toBe 20 describe ".bottom", -> it "is ::horizontalScrollbarHeight if the horizontal scrollbar is visible and 0 otherwise", -> @@ -240,131 +246,130 @@ describe "TextEditorPresenter", -> lineHeight: 10 horizontalScrollbarHeight: 10 verticalScrollbarWidth: 10 - {state} = presenter - expect(state.verticalScrollbar.bottom).toBe 0 + expect(presenter.getState().verticalScrollbar.bottom).toBe 0 presenter.setContentFrameWidth(editor.getMaxScreenLineLength() * 10) - expect(state.verticalScrollbar.bottom).toBe 10 + expect(presenter.getState().verticalScrollbar.bottom).toBe 10 describe ".scrollHeight", -> it "is initialized based on the lineHeight, the number of lines, and the height", -> presenter = buildPresenter(scrollTop: 0, lineHeight: 10) - expect(presenter.state.verticalScrollbar.scrollHeight).toBe editor.getScreenLineCount() * 10 + expect(presenter.getState().verticalScrollbar.scrollHeight).toBe editor.getScreenLineCount() * 10 presenter = buildPresenter(scrollTop: 0, lineHeight: 10, explicitHeight: 500) - expect(presenter.state.verticalScrollbar.scrollHeight).toBe 500 + expect(presenter.getState().verticalScrollbar.scrollHeight).toBe 500 it "updates when the ::lineHeight changes", -> presenter = buildPresenter(scrollTop: 0, lineHeight: 10) expectStateUpdate presenter, -> presenter.setLineHeight(20) - expect(presenter.state.verticalScrollbar.scrollHeight).toBe editor.getScreenLineCount() * 20 + expect(presenter.getState().verticalScrollbar.scrollHeight).toBe editor.getScreenLineCount() * 20 it "updates when the line count changes", -> presenter = buildPresenter(scrollTop: 0, lineHeight: 10) expectStateUpdate presenter, -> editor.getBuffer().append("\n\n\n") - expect(presenter.state.verticalScrollbar.scrollHeight).toBe editor.getScreenLineCount() * 10 + expect(presenter.getState().verticalScrollbar.scrollHeight).toBe editor.getScreenLineCount() * 10 it "updates when ::explicitHeight changes", -> presenter = buildPresenter(scrollTop: 0, lineHeight: 10) expectStateUpdate presenter, -> presenter.setExplicitHeight(500) - expect(presenter.state.verticalScrollbar.scrollHeight).toBe 500 + expect(presenter.getState().verticalScrollbar.scrollHeight).toBe 500 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.contentHeight + expect(presenter.getState().verticalScrollbar.scrollHeight).toBe presenter.contentHeight expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) - expect(presenter.state.verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) + expect(presenter.getState().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.contentHeight + expect(presenter.getState().verticalScrollbar.scrollHeight).toBe presenter.contentHeight describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 20, horizontalScrollbarHeight: 10) - expect(presenter.state.verticalScrollbar.scrollTop).toBe 10 + expect(presenter.getState().verticalScrollbar.scrollTop).toBe 10 expectStateUpdate presenter, -> presenter.setScrollTop(50) - expect(presenter.state.verticalScrollbar.scrollTop).toBe 50 + expect(presenter.getState().verticalScrollbar.scrollTop).toBe 50 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.scrollHeight - presenter.clientHeight + expect(presenter.getState().verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setExplicitHeight(60) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getState().verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getState().verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getState().verticalScrollbar.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight # Scroll top only gets smaller when needed as dimensions change, never bigger - scrollTopBefore = presenter.state.verticalScrollbar.scrollTop + scrollTopBefore = presenter.getState().verticalScrollbar.scrollTop expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n') - expect(presenter.state.verticalScrollbar.scrollTop).toBe scrollTopBefore + expect(presenter.getState().verticalScrollbar.scrollTop).toBe scrollTopBefore it "never goes negative", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(-100) - expect(presenter.state.verticalScrollbar.scrollTop).toBe 0 + expect(presenter.getState().verticalScrollbar.scrollTop).toBe 0 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.contentHeight - presenter.clientHeight + expect(presenter.getState().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.contentHeight - (presenter.lineHeight * 3) + expect(presenter.getState().verticalScrollbar.scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3) expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.state.verticalScrollbar.scrollTop).toBe presenter.contentHeight - presenter.clientHeight + expect(presenter.getState().verticalScrollbar.scrollTop).toBe presenter.contentHeight - presenter.clientHeight describe ".hiddenInput", -> describe ".top/.left", -> it "is positioned over the last cursor it is in view and the editor is focused", -> editor.setCursorBufferPosition([3, 6]) presenter = buildPresenter(focused: false, explicitHeight: 50, contentFrameWidth: 300, horizontalScrollbarHeight: 0, verticalScrollbarWidth: 0) - expectValues presenter.state.hiddenInput, {top: 0, left: 0} + expectValues presenter.getState().hiddenInput, {top: 0, left: 0} expectStateUpdate presenter, -> presenter.setFocused(true) - expectValues presenter.state.hiddenInput, {top: 3 * 10, left: 6 * 10} + expectValues presenter.getState().hiddenInput, {top: 3 * 10, left: 6 * 10} expectStateUpdate presenter, -> presenter.setScrollTop(15) - expectValues presenter.state.hiddenInput, {top: (3 * 10) - 15, left: 6 * 10} + expectValues presenter.getState().hiddenInput, {top: (3 * 10) - 15, left: 6 * 10} expectStateUpdate presenter, -> presenter.setScrollLeft(35) - expectValues presenter.state.hiddenInput, {top: (3 * 10) - 15, left: (6 * 10) - 35} + expectValues presenter.getState().hiddenInput, {top: (3 * 10) - 15, left: (6 * 10) - 35} expectStateUpdate presenter, -> presenter.setScrollTop(40) - expectValues presenter.state.hiddenInput, {top: 0, left: (6 * 10) - 35} + expectValues presenter.getState().hiddenInput, {top: 0, left: (6 * 10) - 35} expectStateUpdate presenter, -> presenter.setScrollLeft(70) - expectValues presenter.state.hiddenInput, {top: 0, left: 0} + expectValues presenter.getState().hiddenInput, {top: 0, left: 0} expectStateUpdate presenter, -> editor.setCursorBufferPosition([11, 43]) - expectValues presenter.state.hiddenInput, {top: 50 - 10, left: 300 - 10} + expectValues presenter.getState().hiddenInput, {top: 50 - 10, left: 300 - 10} newCursor = null expectStateUpdate presenter, -> newCursor = editor.addCursorAtBufferPosition([6, 10]) - expectValues presenter.state.hiddenInput, {top: (6 * 10) - 40, left: (10 * 10) - 70} + expectValues presenter.getState().hiddenInput, {top: (6 * 10) - 40, left: (10 * 10) - 70} expectStateUpdate presenter, -> newCursor.destroy() - expectValues presenter.state.hiddenInput, {top: 50 - 10, left: 300 - 10} + expectValues presenter.getState().hiddenInput, {top: 50 - 10, left: 300 - 10} expectStateUpdate presenter, -> presenter.setFocused(false) - expectValues presenter.state.hiddenInput, {top: 0, left: 0} + expectValues presenter.getState().hiddenInput, {top: 0, left: 0} describe ".height", -> it "is assigned based on the line height", -> presenter = buildPresenter() - expect(presenter.state.hiddenInput.height).toBe 10 + expect(presenter.getState().hiddenInput.height).toBe 10 expectStateUpdate presenter, -> presenter.setLineHeight(20) - expect(presenter.state.hiddenInput.height).toBe 20 + expect(presenter.getState().hiddenInput.height).toBe 20 describe ".width", -> it "is assigned based on the width of the character following the cursor", -> @@ -373,93 +378,93 @@ describe "TextEditorPresenter", -> runs -> editor.setCursorBufferPosition([3, 6]) presenter = buildPresenter() - expect(presenter.state.hiddenInput.width).toBe 10 + expect(presenter.getState().hiddenInput.width).toBe 10 expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(15) - expect(presenter.state.hiddenInput.width).toBe 15 + expect(presenter.getState().hiddenInput.width).toBe 15 expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'r', 20) - expect(presenter.state.hiddenInput.width).toBe 20 + expect(presenter.getState().hiddenInput.width).toBe 20 it "is 2px at the end of lines", -> presenter = buildPresenter() editor.setCursorBufferPosition([3, Infinity]) - expect(presenter.state.hiddenInput.width).toBe 2 + expect(presenter.getState().hiddenInput.width).toBe 2 describe ".content", -> describe ".scrollingVertically", -> it "is true for ::stoppedScrollingDelay milliseconds following a changes to ::scrollTop", -> presenter = buildPresenter(scrollTop: 10, stoppedScrollingDelay: 200, explicitHeight: 100) - expect(presenter.state.content.scrollingVertically).toBe false + expect(presenter.getState().content.scrollingVertically).toBe false expectStateUpdate presenter, -> presenter.setScrollTop(0) - expect(presenter.state.content.scrollingVertically).toBe true + expect(presenter.getState().content.scrollingVertically).toBe true advanceClock(100) - expect(presenter.state.content.scrollingVertically).toBe true + expect(presenter.getState().content.scrollingVertically).toBe true presenter.setScrollTop(10) advanceClock(100) - expect(presenter.state.content.scrollingVertically).toBe true + expect(presenter.getState().content.scrollingVertically).toBe true expectStateUpdate presenter, -> advanceClock(100) - expect(presenter.state.content.scrollingVertically).toBe false + expect(presenter.getState().content.scrollingVertically).toBe false describe ".scrollHeight", -> it "is initialized based on the lineHeight, the number of lines, and the height", -> presenter = buildPresenter(scrollTop: 0, lineHeight: 10) - expect(presenter.state.content.scrollHeight).toBe editor.getScreenLineCount() * 10 + expect(presenter.getState().content.scrollHeight).toBe editor.getScreenLineCount() * 10 presenter = buildPresenter(scrollTop: 0, lineHeight: 10, explicitHeight: 500) - expect(presenter.state.content.scrollHeight).toBe 500 + expect(presenter.getState().content.scrollHeight).toBe 500 it "updates when the ::lineHeight changes", -> presenter = buildPresenter(scrollTop: 0, lineHeight: 10) expectStateUpdate presenter, -> presenter.setLineHeight(20) - expect(presenter.state.content.scrollHeight).toBe editor.getScreenLineCount() * 20 + expect(presenter.getState().content.scrollHeight).toBe editor.getScreenLineCount() * 20 it "updates when the line count changes", -> presenter = buildPresenter(scrollTop: 0, lineHeight: 10) expectStateUpdate presenter, -> editor.getBuffer().append("\n\n\n") - expect(presenter.state.content.scrollHeight).toBe editor.getScreenLineCount() * 10 + expect(presenter.getState().content.scrollHeight).toBe editor.getScreenLineCount() * 10 it "updates when ::explicitHeight changes", -> presenter = buildPresenter(scrollTop: 0, lineHeight: 10) expectStateUpdate presenter, -> presenter.setExplicitHeight(500) - expect(presenter.state.content.scrollHeight).toBe 500 + expect(presenter.getState().content.scrollHeight).toBe 500 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.contentHeight + expect(presenter.getState().content.scrollHeight).toBe presenter.contentHeight expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) - expect(presenter.state.content.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) + expect(presenter.getState().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.contentHeight + expect(presenter.getState().content.scrollHeight).toBe presenter.contentHeight describe ".scrollWidth", -> it "is initialized as the max of the computed clientWidth and the width of the longest line", -> maxLineLength = editor.getMaxScreenLineLength() presenter = buildPresenter(explicitHeight: 100, contentFrameWidth: 50, baseCharacterWidth: 10, verticalScrollbarWidth: 10) - expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 1 + expect(presenter.getState().content.scrollWidth).toBe 10 * maxLineLength + 1 presenter = buildPresenter(explicitHeight: 100, contentFrameWidth: 10 * maxLineLength + 20, baseCharacterWidth: 10, verticalScrollbarWidth: 10) - expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 20 - 10 # subtract vertical scrollbar width + expect(presenter.getState().content.scrollWidth).toBe 10 * maxLineLength + 20 - 10 # subtract vertical scrollbar width it "updates when the ::contentFrameWidth changes", -> maxLineLength = editor.getMaxScreenLineLength() presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10) - expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 1 + expect(presenter.getState().content.scrollWidth).toBe 10 * maxLineLength + 1 expectStateUpdate presenter, -> presenter.setContentFrameWidth(10 * maxLineLength + 20) - expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 20 + expect(presenter.getState().content.scrollWidth).toBe 10 * maxLineLength + 20 it "updates when the ::baseCharacterWidth changes", -> maxLineLength = editor.getMaxScreenLineLength() presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10) - expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 1 + expect(presenter.getState().content.scrollWidth).toBe 10 * maxLineLength + 1 expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(15) - expect(presenter.state.content.scrollWidth).toBe 15 * maxLineLength + 1 + expect(presenter.getState().content.scrollWidth).toBe 15 * maxLineLength + 1 it "updates when the scoped character widths change", -> waitsForPromise -> atom.packages.activatePackage('language-javascript') @@ -468,126 +473,126 @@ describe "TextEditorPresenter", -> maxLineLength = editor.getMaxScreenLineLength() presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10) - expect(presenter.state.content.scrollWidth).toBe 10 * maxLineLength + 1 + expect(presenter.getState().content.scrollWidth).toBe 10 * maxLineLength + 1 expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['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 + expect(presenter.getState().content.scrollWidth).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 # 2 of the characters are 20px wide now instead of 10px wide it "updates when ::softWrapped changes on the editor", -> presenter = buildPresenter(contentFrameWidth: 470, baseCharacterWidth: 10) - expect(presenter.state.content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + expect(presenter.getState().content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 expectStateUpdate presenter, -> editor.setSoftWrapped(true) - expect(presenter.state.horizontalScrollbar.scrollWidth).toBe presenter.clientWidth + expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe presenter.clientWidth expectStateUpdate presenter, -> editor.setSoftWrapped(false) - expect(presenter.state.content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + expect(presenter.getState().content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 it "updates when the longest line changes", -> presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10) - expect(presenter.state.content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + expect(presenter.getState().content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 expectStateUpdate presenter, -> editor.setCursorBufferPosition([editor.getLongestScreenRow(), 0]) expectStateUpdate presenter, -> editor.insertText('xyz') - expect(presenter.state.content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 + expect(presenter.getState().content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> presenter = buildPresenter(scrollTop: 10, lineHeight: 10, explicitHeight: 20) - expect(presenter.state.content.scrollTop).toBe 10 + expect(presenter.getState().content.scrollTop).toBe 10 expectStateUpdate presenter, -> presenter.setScrollTop(50) - expect(presenter.state.content.scrollTop).toBe 50 + expect(presenter.getState().content.scrollTop).toBe 50 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.scrollHeight - presenter.clientHeight + expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setExplicitHeight(60) - expect(presenter.state.content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5) - expect(presenter.state.content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) - expect(presenter.state.content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight # Scroll top only gets smaller when needed as dimensions change, never bigger - scrollTopBefore = presenter.state.verticalScrollbar.scrollTop + scrollTopBefore = presenter.getState().verticalScrollbar.scrollTop expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n') - expect(presenter.state.content.scrollTop).toBe scrollTopBefore + expect(presenter.getState().content.scrollTop).toBe scrollTopBefore it "never goes negative", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(-100) - expect(presenter.state.content.scrollTop).toBe 0 + expect(presenter.getState().content.scrollTop).toBe 0 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.contentHeight - presenter.clientHeight + expect(presenter.getState().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.contentHeight - (presenter.lineHeight * 3) + expect(presenter.getState().content.scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3) expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.state.content.scrollTop).toBe presenter.contentHeight - presenter.clientHeight + expect(presenter.getState().content.scrollTop).toBe presenter.contentHeight - presenter.clientHeight describe ".scrollLeft", -> it "tracks the value of ::scrollLeft", -> presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) - expect(presenter.state.content.scrollLeft).toBe 10 + expect(presenter.getState().content.scrollLeft).toBe 10 expectStateUpdate presenter, -> presenter.setScrollLeft(50) - expect(presenter.state.content.scrollLeft).toBe 50 + expect(presenter.getState().content.scrollLeft).toBe 50 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.scrollWidth - presenter.clientWidth + expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setContentFrameWidth(600) - expect(presenter.state.content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setVerticalScrollbarWidth(5) - expect(presenter.state.content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> editor.getBuffer().delete([[6, 0], [6, Infinity]]) - expect(presenter.state.content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth # Scroll top only gets smaller when needed as dimensions change, never bigger - scrollLeftBefore = presenter.state.content.scrollLeft + scrollLeftBefore = presenter.getState().content.scrollLeft expectStateUpdate presenter, -> editor.getBuffer().insert([6, 0], new Array(100).join('x')) - expect(presenter.state.content.scrollLeft).toBe scrollLeftBefore + expect(presenter.getState().content.scrollLeft).toBe scrollLeftBefore it "never goes negative", -> presenter = buildPresenter(scrollLeft: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) expectStateUpdate presenter, -> presenter.setScrollLeft(-300) - expect(presenter.state.content.scrollLeft).toBe 0 + expect(presenter.getState().content.scrollLeft).toBe 0 describe ".indentGuidesVisible", -> it "is initialized based on the editor.showIndentGuide config setting", -> presenter = buildPresenter() - expect(presenter.state.content.indentGuidesVisible).toBe false + expect(presenter.getState().content.indentGuidesVisible).toBe false atom.config.set('editor.showIndentGuide', true) presenter = buildPresenter() - expect(presenter.state.content.indentGuidesVisible).toBe true + expect(presenter.getState().content.indentGuidesVisible).toBe true it "updates when the editor.showIndentGuide config setting changes", -> presenter = buildPresenter() - expect(presenter.state.content.indentGuidesVisible).toBe false + expect(presenter.getState().content.indentGuidesVisible).toBe false expectStateUpdate presenter, -> atom.config.set('editor.showIndentGuide', true) - expect(presenter.state.content.indentGuidesVisible).toBe true + expect(presenter.getState().content.indentGuidesVisible).toBe true expectStateUpdate presenter, -> atom.config.set('editor.showIndentGuide', false) - expect(presenter.state.content.indentGuidesVisible).toBe false + expect(presenter.getState().content.indentGuidesVisible).toBe false it "updates when the editor's grammar changes", -> atom.config.set('editor.showIndentGuide', true, scopeSelector: ".source.js") presenter = buildPresenter() - expect(presenter.state.content.indentGuidesVisible).toBe false + expect(presenter.getState().content.indentGuidesVisible).toBe false stateUpdated = false presenter.onDidUpdateState -> stateUpdated = true @@ -596,56 +601,56 @@ describe "TextEditorPresenter", -> runs -> expect(stateUpdated).toBe true - expect(presenter.state.content.indentGuidesVisible).toBe true + expect(presenter.getState().content.indentGuidesVisible).toBe true expectStateUpdate presenter, -> editor.setGrammar(atom.grammars.selectGrammar('.txt')) - expect(presenter.state.content.indentGuidesVisible).toBe false + expect(presenter.getState().content.indentGuidesVisible).toBe false it "is always false when the editor is mini", -> atom.config.set('editor.showIndentGuide', true) editor.setMini(true) presenter = buildPresenter() - expect(presenter.state.content.indentGuidesVisible).toBe false + expect(presenter.getState().content.indentGuidesVisible).toBe false editor.setMini(false) - expect(presenter.state.content.indentGuidesVisible).toBe true + expect(presenter.getState().content.indentGuidesVisible).toBe true editor.setMini(true) - expect(presenter.state.content.indentGuidesVisible).toBe false + expect(presenter.getState().content.indentGuidesVisible).toBe false describe ".backgroundColor", -> it "is assigned to ::backgroundColor unless the editor is mini", -> presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)') - expect(presenter.state.content.backgroundColor).toBe 'rgba(255, 0, 0, 0)' + expect(presenter.getState().content.backgroundColor).toBe 'rgba(255, 0, 0, 0)' editor.setMini(true) presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)') - expect(presenter.state.content.backgroundColor).toBeNull() + expect(presenter.getState().content.backgroundColor).toBeNull() it "updates when ::backgroundColor changes", -> presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)') - expect(presenter.state.content.backgroundColor).toBe 'rgba(255, 0, 0, 0)' + expect(presenter.getState().content.backgroundColor).toBe 'rgba(255, 0, 0, 0)' expectStateUpdate presenter, -> presenter.setBackgroundColor('rgba(0, 0, 255, 0)') - expect(presenter.state.content.backgroundColor).toBe 'rgba(0, 0, 255, 0)' + expect(presenter.getState().content.backgroundColor).toBe 'rgba(0, 0, 255, 0)' it "updates when ::mini changes", -> presenter = buildPresenter(backgroundColor: 'rgba(255, 0, 0, 0)') - expect(presenter.state.content.backgroundColor).toBe 'rgba(255, 0, 0, 0)' + expect(presenter.getState().content.backgroundColor).toBe 'rgba(255, 0, 0, 0)' expectStateUpdate presenter, -> editor.setMini(true) - expect(presenter.state.content.backgroundColor).toBeNull() + expect(presenter.getState().content.backgroundColor).toBeNull() describe ".placeholderText", -> it "is present when the editor has no text", -> editor.setPlaceholderText("the-placeholder-text") presenter = buildPresenter() - expect(presenter.state.content.placeholderText).toBeNull() + expect(presenter.getState().content.placeholderText).toBeNull() expectStateUpdate presenter, -> editor.setText("") - expect(presenter.state.content.placeholderText).toBe "the-placeholder-text" + expect(presenter.getState().content.placeholderText).toBe "the-placeholder-text" expectStateUpdate presenter, -> editor.setPlaceholderText("new-placeholder-text") - expect(presenter.state.content.placeholderText).toBe "new-placeholder-text" + expect(presenter.getState().content.placeholderText).toBe "new-placeholder-text" describe ".lines", -> lineStateForScreenRow = (presenter, screenRow) -> - presenter.state.content.lines[presenter.model.tokenizedLineForScreenRow(screenRow).id] + presenter.getState().content.lines[presenter.model.tokenizedLineForScreenRow(screenRow).id] it "contains states for lines that are visible on screen, plus and minus the overdraw margin", -> presenter = buildPresenter(explicitHeight: 15, scrollTop: 50, lineHeight: 10, lineOverdrawMargin: 1) @@ -720,16 +725,16 @@ describe "TextEditorPresenter", -> it "is empty until all of the required measurements are assigned", -> presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null) - expect(presenter.state.content.lines).toEqual({}) + expect(presenter.getState().content.lines).toEqual({}) presenter.setExplicitHeight(25) - expect(presenter.state.content.lines).toEqual({}) + expect(presenter.getState().content.lines).toEqual({}) presenter.setLineHeight(10) - expect(presenter.state.content.lines).toEqual({}) + expect(presenter.getState().content.lines).toEqual({}) presenter.setScrollTop(0) - expect(presenter.state.content.lines).not.toEqual({}) + expect(presenter.getState().content.lines).not.toEqual({}) it "updates when ::scrollTop changes", -> presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10, lineOverdrawMargin: 1) @@ -836,8 +841,8 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> editor.getBuffer().insert([3, Infinity], 'xyz') newLine3 = editor.tokenizedLineForScreenRow(3) - expect(presenter.state.content.lines[oldLine3.id]).toBeUndefined() - expect(presenter.state.content.lines[newLine3.id]).toBeDefined() + expect(presenter.getState().content.lines[oldLine3.id]).toBeUndefined() + expect(presenter.getState().content.lines[newLine3.id]).toBeDefined() it "does not attempt to preserve lines corresponding to ::mouseWheelScreenRow if they have been deleted", -> presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10, lineOverdrawMargin: 1, stoppedScrollingDelay: 200) @@ -984,7 +989,7 @@ describe "TextEditorPresenter", -> describe ".cursors", -> stateForCursor = (presenter, cursorIndex) -> - presenter.state.content.cursors[presenter.model.getCursors()[cursorIndex].id] + presenter.getState().content.cursors[presenter.model.getCursors()[cursorIndex].id] it "contains pixelRects for empty selections that are visible on screen", -> editor.setSelectedBufferRanges([ @@ -1004,22 +1009,22 @@ describe "TextEditorPresenter", -> it "is empty until all of the required measurements are assigned", -> presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, baseCharacterWidth: null, horizontalScrollbarHeight: null) - expect(presenter.state.content.cursors).toEqual({}) + expect(presenter.getState().content.cursors).toEqual({}) presenter.setExplicitHeight(25) - expect(presenter.state.content.cursors).toEqual({}) + expect(presenter.getState().content.cursors).toEqual({}) presenter.setLineHeight(10) - expect(presenter.state.content.cursors).toEqual({}) + expect(presenter.getState().content.cursors).toEqual({}) presenter.setScrollTop(0) - expect(presenter.state.content.cursors).toEqual({}) + expect(presenter.getState().content.cursors).toEqual({}) presenter.setBaseCharacterWidth(8) - expect(presenter.state.content.cursors).toEqual({}) + expect(presenter.getState().content.cursors).toEqual({}) presenter.setHorizontalScrollbarHeight(10) - expect(presenter.state.content.cursors).not.toEqual({}) + expect(presenter.getState().content.cursors).not.toEqual({}) it "updates when ::scrollTop changes", -> editor.setSelectedBufferRanges([ @@ -1128,7 +1133,7 @@ describe "TextEditorPresenter", -> # destroying destroyedCursor = editor.getCursors()[2] expectStateUpdate presenter, -> destroyedCursor.destroy() - expect(presenter.state.content.cursors[destroyedCursor.id]).toBeUndefined() + expect(presenter.getState().content.cursors[destroyedCursor.id]).toBeUndefined() it "makes cursors as wide as the ::baseCharacterWidth if they're at the end of a line", -> editor.setCursorBufferPosition([1, Infinity]) @@ -1141,61 +1146,61 @@ describe "TextEditorPresenter", -> cursorBlinkResumeDelay = 200 presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay, focused: true}) - expect(presenter.state.content.cursorsVisible).toBe true + expect(presenter.getState().content.cursorsVisible).toBe true expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe true + expect(presenter.getState().content.cursorsVisible).toBe true expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe true + expect(presenter.getState().content.cursorsVisible).toBe true expectStateUpdate presenter, -> presenter.setFocused(false) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false expectStateUpdate presenter, -> presenter.setFocused(true) - expect(presenter.state.content.cursorsVisible).toBe true + expect(presenter.getState().content.cursorsVisible).toBe true expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false it "stops alternating for ::cursorBlinkResumeDelay when a cursor moves or a cursor is added", -> cursorBlinkPeriod = 100 cursorBlinkResumeDelay = 200 presenter = buildPresenter({cursorBlinkPeriod, cursorBlinkResumeDelay, focused: true}) - expect(presenter.state.content.cursorsVisible).toBe true + expect(presenter.getState().content.cursorsVisible).toBe true expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false expectStateUpdate presenter, -> editor.moveRight() - expect(presenter.state.content.cursorsVisible).toBe true + expect(presenter.getState().content.cursorsVisible).toBe true expectStateUpdate presenter, -> advanceClock(cursorBlinkResumeDelay) advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe true + expect(presenter.getState().content.cursorsVisible).toBe true expectStateUpdate presenter, -> advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false expectStateUpdate presenter, -> editor.addCursorAtBufferPosition([1, 0]) - expect(presenter.state.content.cursorsVisible).toBe true + expect(presenter.getState().content.cursorsVisible).toBe true expectStateUpdate presenter, -> advanceClock(cursorBlinkResumeDelay) advanceClock(cursorBlinkPeriod / 2) - expect(presenter.state.content.cursorsVisible).toBe false + expect(presenter.getState().content.cursorsVisible).toBe false describe ".highlights", -> stateForHighlight = (presenter, decoration) -> - presenter.state.content.highlights[decoration.id] + presenter.getState().content.highlights[decoration.id] stateForSelection = (presenter, selectionIndex) -> selection = presenter.model.getSelections()[selectionIndex] @@ -1286,19 +1291,19 @@ describe "TextEditorPresenter", -> ]) presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, baseCharacterWidth: null) - expect(presenter.state.content.highlights).toEqual({}) + expect(presenter.getState().content.highlights).toEqual({}) presenter.setExplicitHeight(25) - expect(presenter.state.content.highlights).toEqual({}) + expect(presenter.getState().content.highlights).toEqual({}) presenter.setLineHeight(10) - expect(presenter.state.content.highlights).toEqual({}) + expect(presenter.getState().content.highlights).toEqual({}) presenter.setScrollTop(0) - expect(presenter.state.content.highlights).toEqual({}) + expect(presenter.getState().content.highlights).toEqual({}) presenter.setBaseCharacterWidth(8) - expect(presenter.state.content.highlights).not.toEqual({}) + expect(presenter.getState().content.highlights).not.toEqual({}) it "does not include highlights for invalid markers", -> marker = editor.markBufferRange([[2, 2], [2, 4]], invalidate: 'touch') @@ -1481,7 +1486,7 @@ describe "TextEditorPresenter", -> describe ".overlays", -> stateForOverlay = (presenter, decoration) -> - presenter.state.content.overlays[decoration.id] + presenter.getState().content.overlays[decoration.id] it "contains state for overlay decorations both initially and when their markers move", -> item = {} @@ -1583,114 +1588,114 @@ describe "TextEditorPresenter", -> decoration = editor.decorateMarker(marker, {type: 'overlay', position: 'tail', item}) presenter = buildPresenter(baseCharacterWidth: null, lineHeight: null) - expect(presenter.state.content.overlays).toEqual({}) + expect(presenter.getState().content.overlays).toEqual({}) presenter.setBaseCharacterWidth(10) - expect(presenter.state.content.overlays).toEqual({}) + expect(presenter.getState().content.overlays).toEqual({}) presenter.setLineHeight(10) - expect(presenter.state.content.overlays).not.toEqual({}) + expect(presenter.getState().content.overlays).not.toEqual({}) describe ".gutter", -> describe ".scrollHeight", -> it "is initialized based on ::lineHeight, the number of lines, and ::explicitHeight", -> presenter = buildPresenter() - expect(presenter.state.gutter.scrollHeight).toBe editor.getScreenLineCount() * 10 + expect(presenter.getState().gutter.scrollHeight).toBe editor.getScreenLineCount() * 10 presenter = buildPresenter(explicitHeight: 500) - expect(presenter.state.gutter.scrollHeight).toBe 500 + expect(presenter.getState().gutter.scrollHeight).toBe 500 it "updates when the ::lineHeight changes", -> presenter = buildPresenter() expectStateUpdate presenter, -> presenter.setLineHeight(20) - expect(presenter.state.gutter.scrollHeight).toBe editor.getScreenLineCount() * 20 + expect(presenter.getState().gutter.scrollHeight).toBe editor.getScreenLineCount() * 20 it "updates when the line count changes", -> presenter = buildPresenter() expectStateUpdate presenter, -> editor.getBuffer().append("\n\n\n") - expect(presenter.state.gutter.scrollHeight).toBe editor.getScreenLineCount() * 10 + expect(presenter.getState().gutter.scrollHeight).toBe editor.getScreenLineCount() * 10 it "updates when ::explicitHeight changes", -> presenter = buildPresenter() expectStateUpdate presenter, -> presenter.setExplicitHeight(500) - expect(presenter.state.gutter.scrollHeight).toBe 500 + expect(presenter.getState().gutter.scrollHeight).toBe 500 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.contentHeight + expect(presenter.getState().gutter.scrollHeight).toBe presenter.contentHeight expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) - expect(presenter.state.gutter.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) + expect(presenter.getState().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.contentHeight + expect(presenter.getState().gutter.scrollHeight).toBe presenter.contentHeight describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 20) - expect(presenter.state.gutter.scrollTop).toBe 10 + expect(presenter.getState().gutter.scrollTop).toBe 10 expectStateUpdate presenter, -> presenter.setScrollTop(50) - expect(presenter.state.gutter.scrollTop).toBe 50 + expect(presenter.getState().gutter.scrollTop).toBe 50 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.scrollHeight - presenter.clientHeight + expect(presenter.getState().gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setExplicitHeight(60) - expect(presenter.state.gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getState().gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5) - expect(presenter.state.gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getState().gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) - expect(presenter.state.gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getState().gutter.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight # Scroll top only gets smaller when needed as dimensions change, never bigger - scrollTopBefore = presenter.state.verticalScrollbar.scrollTop + scrollTopBefore = presenter.getState().verticalScrollbar.scrollTop expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n') - expect(presenter.state.gutter.scrollTop).toBe scrollTopBefore + expect(presenter.getState().gutter.scrollTop).toBe scrollTopBefore it "never goes negative", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) expectStateUpdate presenter, -> presenter.setScrollTop(-100) - expect(presenter.state.gutter.scrollTop).toBe 0 + expect(presenter.getState().gutter.scrollTop).toBe 0 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.contentHeight - presenter.clientHeight + expect(presenter.getState().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.contentHeight - (presenter.lineHeight * 3) + expect(presenter.getState().gutter.scrollTop).toBe presenter.contentHeight - (presenter.lineHeight * 3) expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(presenter.state.gutter.scrollTop).toBe presenter.contentHeight - presenter.clientHeight + expect(presenter.getState().gutter.scrollTop).toBe presenter.contentHeight - presenter.clientHeight describe ".backgroundColor", -> it "is assigned to ::gutterBackgroundColor if present, and to ::backgroundColor otherwise", -> presenter = buildPresenter(backgroundColor: "rgba(255, 0, 0, 0)", gutterBackgroundColor: "rgba(0, 255, 0, 0)") - expect(presenter.state.gutter.backgroundColor).toBe "rgba(0, 255, 0, 0)" + expect(presenter.getState().gutter.backgroundColor).toBe "rgba(0, 255, 0, 0)" expectStateUpdate presenter, -> presenter.setGutterBackgroundColor("rgba(0, 0, 255, 0)") - expect(presenter.state.gutter.backgroundColor).toBe "rgba(0, 0, 255, 0)" + expect(presenter.getState().gutter.backgroundColor).toBe "rgba(0, 0, 255, 0)" expectStateUpdate presenter, -> presenter.setGutterBackgroundColor("rgba(0, 0, 0, 0)") - expect(presenter.state.gutter.backgroundColor).toBe "rgba(255, 0, 0, 0)" + expect(presenter.getState().gutter.backgroundColor).toBe "rgba(255, 0, 0, 0)" expectStateUpdate presenter, -> presenter.setBackgroundColor("rgba(0, 0, 255, 0)") - expect(presenter.state.gutter.backgroundColor).toBe "rgba(0, 0, 255, 0)" + expect(presenter.getState().gutter.backgroundColor).toBe "rgba(0, 0, 255, 0)" describe ".maxLineNumberDigits", -> it "is set to the number of digits used by the greatest line number", -> presenter = buildPresenter() expect(editor.getLastBufferRow()).toBe 12 - expect(presenter.state.gutter.maxLineNumberDigits).toBe 2 + expect(presenter.getState().gutter.maxLineNumberDigits).toBe 2 editor.setText("1\n2\n3") - expect(presenter.state.gutter.maxLineNumberDigits).toBe 1 + expect(presenter.getState().gutter.maxLineNumberDigits).toBe 1 describe ".lineNumbers", -> lineNumberStateForScreenRow = (presenter, screenRow) -> @@ -1702,7 +1707,7 @@ describe "TextEditorPresenter", -> else key = bufferRow - presenter.state.gutter.lineNumbers[key] + presenter.getState().gutter.lineNumbers[key] it "contains states for line numbers that are visible on screen, plus and minus the overdraw margin", -> editor.foldBufferRow(4) @@ -1994,28 +1999,28 @@ describe "TextEditorPresenter", -> presenter = buildPresenter() expect(editor.isGutterVisible()).toBe true - expect(presenter.state.gutter.visible).toBe true + expect(presenter.getState().gutter.visible).toBe true expectStateUpdate presenter, -> editor.setMini(true) - expect(presenter.state.gutter.visible).toBe false + expect(presenter.getState().gutter.visible).toBe false expectStateUpdate presenter, -> editor.setMini(false) - expect(presenter.state.gutter.visible).toBe true + expect(presenter.getState().gutter.visible).toBe true expectStateUpdate presenter, -> editor.setGutterVisible(false) - expect(presenter.state.gutter.visible).toBe false + expect(presenter.getState().gutter.visible).toBe false expectStateUpdate presenter, -> editor.setGutterVisible(true) - expect(presenter.state.gutter.visible).toBe true + expect(presenter.getState().gutter.visible).toBe true expectStateUpdate presenter, -> atom.config.set('editor.showLineNumbers', false) - expect(presenter.state.gutter.visible).toBe false + expect(presenter.getState().gutter.visible).toBe false it "updates when the editor's grammar changes", -> presenter = buildPresenter() atom.config.set('editor.showLineNumbers', false, scopeSelector: '.source.js') - expect(presenter.state.gutter.visible).toBe true + expect(presenter.getState().gutter.visible).toBe true stateUpdated = false presenter.onDidUpdateState -> stateUpdated = true @@ -2023,33 +2028,33 @@ describe "TextEditorPresenter", -> runs -> expect(stateUpdated).toBe true - expect(presenter.state.gutter.visible).toBe false + expect(presenter.getState().gutter.visible).toBe false describe ".height", -> it "tracks the computed content height if ::autoHeight is true so the editor auto-expands vertically", -> presenter = buildPresenter(explicitHeight: null, autoHeight: true) - expect(presenter.state.height).toBe editor.getScreenLineCount() * 10 + expect(presenter.getState().height).toBe editor.getScreenLineCount() * 10 expectStateUpdate presenter, -> presenter.setAutoHeight(false) - expect(presenter.state.height).toBe null + expect(presenter.getState().height).toBe null expectStateUpdate presenter, -> presenter.setAutoHeight(true) - expect(presenter.state.height).toBe editor.getScreenLineCount() * 10 + expect(presenter.getState().height).toBe editor.getScreenLineCount() * 10 expectStateUpdate presenter, -> presenter.setLineHeight(20) - expect(presenter.state.height).toBe editor.getScreenLineCount() * 20 + expect(presenter.getState().height).toBe editor.getScreenLineCount() * 20 expectStateUpdate presenter, -> editor.getBuffer().append("\n\n\n") - expect(presenter.state.height).toBe editor.getScreenLineCount() * 20 + expect(presenter.getState().height).toBe editor.getScreenLineCount() * 20 describe ".focused", -> it "tracks the value of ::focused", -> presenter = buildPresenter(focused: false) - expect(presenter.state.focused).toBe false + expect(presenter.getState().focused).toBe false expectStateUpdate presenter, -> presenter.setFocused(true) - expect(presenter.state.focused).toBe true + expect(presenter.getState().focused).toBe true expectStateUpdate presenter, -> presenter.setFocused(false) - expect(presenter.state.focused).toBe false + expect(presenter.getState().focused).toBe false # disabled until we fix an issue with display buffer markers not updating when # they are moved on screen but not in the buffer @@ -2106,7 +2111,7 @@ describe "TextEditorPresenter", -> expectValidState = -> presenterParams.scrollTop = presenter.scrollTop presenterParams.scrollLeft = presenter.scrollLeft - actualState = presenter.state + actualState = presenter.getState() expectedState = new TextEditorPresenter(presenterParams).state delete actualState.content.scrollingVertically delete expectedState.content.scrollingVertically diff --git a/src/cursors-component.coffee b/src/cursors-component.coffee index ae844bfe0..f4f5d749f 100644 --- a/src/cursors-component.coffee +++ b/src/cursors-component.coffee @@ -2,14 +2,13 @@ module.exports = class CursorsComponent oldState: null - constructor: (@presenter) -> + constructor: -> @cursorNodesById = {} @domNode = document.createElement('div') @domNode.classList.add('cursors') - @updateSync() - updateSync: -> - newState = @presenter.state.content + updateSync: (state) -> + newState = state.content @oldState ?= {cursors: {}} # update blink class diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index b6cb0c582..6eb11ff1f 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -18,10 +18,8 @@ class GutterComponent @domNode.addEventListener 'click', @onClick @domNode.addEventListener 'mousedown', @onMouseDown - @updateSync() - - updateSync: -> - @newState = @presenter.state.gutter + updateSync: (state) -> + @newState = state.gutter @oldState ?= {lineNumbers: {}} @appendDummyLineNumber() unless @dummyLineNumberNode? diff --git a/src/highlights-component.coffee b/src/highlights-component.coffee index 48d2cc70e..3bd5197fe 100644 --- a/src/highlights-component.coffee +++ b/src/highlights-component.coffee @@ -5,7 +5,7 @@ module.exports = class HighlightsComponent oldState: null - constructor: (@presenter) -> + constructor: -> @highlightNodesById = {} @regionNodesByHighlightId = {} @@ -17,8 +17,8 @@ class HighlightsComponent insertionPoint.setAttribute('select', '.underlayer') @domNode.appendChild(insertionPoint) - updateSync: -> - newState = @presenter.state.content.highlights + updateSync: (state) -> + newState = state.content.highlights @oldState ?= {} # remove highlights diff --git a/src/input-component.coffee b/src/input-component.coffee index 2037fbe48..f6f8917f6 100644 --- a/src/input-component.coffee +++ b/src/input-component.coffee @@ -1,16 +1,15 @@ module.exports = class InputComponent - constructor: (@presenter) -> + constructor: -> @domNode = document.createElement('input') @domNode.classList.add('hidden-input') @domNode.setAttribute('data-react-skip-selection-restoration', true) @domNode.style['-webkit-transform'] = 'translateZ(0)' @domNode.addEventListener 'paste', (event) -> event.preventDefault() - @updateSync() - updateSync: -> + updateSync: (state) -> @oldState ?= {} - newState = @presenter.state.hiddenInput + newState = state.hiddenInput if newState.top isnt @oldState.top @domNode.style.top = newState.top + 'px' diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 9619a175c..fa4ab23b0 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -42,15 +42,13 @@ class LinesComponent insertionPoint = document.createElement('content') insertionPoint.setAttribute('select', 'atom-overlay') - @overlayManager = new OverlayManager(@hostElement) + @overlayManager = new OverlayManager(@presenter, @hostElement) @domNode.appendChild(insertionPoint) else - @overlayManager = new OverlayManager(@domNode) + @overlayManager = new OverlayManager(@presenter, @domNode) - @updateSync(visible) - - updateSync: -> - @newState = @presenter.state.content + updateSync: (state) -> + @newState = state.content @oldState ?= {lines: {}} if @newState.scrollHeight isnt @oldState.scrollHeight @@ -81,10 +79,10 @@ class LinesComponent @domNode.style.width = @newState.scrollWidth + 'px' @oldState.scrollWidth = @newState.scrollWidth - @cursorsComponent.updateSync() - @highlightsComponent.updateSync() + @cursorsComponent.updateSync(state) + @highlightsComponent.updateSync(state) - @overlayManager?.render(@presenter) + @overlayManager?.render(state) @oldState.indentGuidesVisible = @newState.indentGuidesVisible @oldState.scrollWidth = @newState.scrollWidth diff --git a/src/overlay-manager.coffee b/src/overlay-manager.coffee index 7d9c9d95d..c8b5da0e8 100644 --- a/src/overlay-manager.coffee +++ b/src/overlay-manager.coffee @@ -1,20 +1,20 @@ module.exports = class OverlayManager - constructor: (@container) -> + constructor: (@presenter, @container) -> @overlayNodesById = {} - render: (presenter) -> - for decorationId, {pixelPosition, item} of presenter.state.content.overlays - @renderOverlay(presenter, decorationId, item, pixelPosition) + render: (state) -> + for decorationId, {pixelPosition, item} of state.content.overlays + @renderOverlay(state, decorationId, item, pixelPosition) for id, overlayNode of @overlayNodesById - unless presenter.state.content.overlays.hasOwnProperty(id) + unless state.content.overlays.hasOwnProperty(id) delete @overlayNodesById[id] overlayNode.remove() return - renderOverlay: (presenter, decorationId, item, pixelPosition) -> + renderOverlay: (state, decorationId, item, pixelPosition) -> item = atom.views.getView(item) unless overlayNode = @overlayNodesById[decorationId] overlayNode = @overlayNodesById[decorationId] = document.createElement('atom-overlay') @@ -25,15 +25,15 @@ class OverlayManager itemHeight = item.offsetHeight - {scrollTop, scrollLeft} = presenter.state.content + {scrollTop, scrollLeft} = state.content left = pixelPosition.left - if left + itemWidth - scrollLeft > presenter.contentFrameWidth and left - itemWidth >= scrollLeft + if left + itemWidth - scrollLeft > @presenter.contentFrameWidth and left - itemWidth >= scrollLeft left -= itemWidth - top = pixelPosition.top + presenter.lineHeight - if top + itemHeight - scrollTop > presenter.height and top - itemHeight - presenter.lineHeight >= scrollTop - top -= itemHeight + presenter.lineHeight + top = pixelPosition.top + @presenter.lineHeight + if top + itemHeight - scrollTop > @presenter.height and top - itemHeight - @presenter.lineHeight >= scrollTop + top -= itemHeight + @presenter.lineHeight overlayNode.style.top = top + 'px' overlayNode.style.left = left + 'px' diff --git a/src/scrollbar-component.coffee b/src/scrollbar-component.coffee index 3e94a0708..3a63a33ed 100644 --- a/src/scrollbar-component.coffee +++ b/src/scrollbar-component.coffee @@ -1,6 +1,6 @@ module.exports = class ScrollbarComponent - constructor: ({@presenter, @orientation, @onScroll}) -> + constructor: ({@orientation, @onScroll}) -> @domNode = document.createElement('div') @domNode.classList.add "#{@orientation}-scrollbar" @domNode.style['-webkit-transform'] = 'translateZ(0)' # See atom/atom#3559 @@ -12,16 +12,14 @@ class ScrollbarComponent @domNode.addEventListener 'scroll', @onScrollCallback - @updateSync() - - updateSync: -> + updateSync: (state) -> @oldState ?= {} switch @orientation when 'vertical' - @newState = @presenter.state.verticalScrollbar + @newState = state.verticalScrollbar @updateVertical() when 'horizontal' - @newState = @presenter.state.horizontalScrollbar + @newState = state.horizontalScrollbar @updateHorizontal() if @newState.visible isnt @oldState.visible diff --git a/src/scrollbar-corner-component.coffee b/src/scrollbar-corner-component.coffee index 1a266afc5..c0fbdfd60 100644 --- a/src/scrollbar-corner-component.coffee +++ b/src/scrollbar-corner-component.coffee @@ -1,20 +1,18 @@ module.exports = class ScrollbarCornerComponent - constructor: (@presenter) -> + constructor: () -> @domNode = document.createElement('div') @domNode.classList.add('scrollbar-corner') @contentNode = document.createElement('div') @domNode.appendChild(@contentNode) - @updateSync() - - updateSync: -> + updateSync: (state) -> @oldState ?= {} @newState ?= {} - newHorizontalState = @presenter.state.horizontalScrollbar - newVerticalState = @presenter.state.verticalScrollbar + newHorizontalState = state.horizontalScrollbar + newVerticalState = state.verticalScrollbar @newState.visible = newHorizontalState.visible and newVerticalState.visible @newState.height = newHorizontalState.height @newState.width = newVerticalState.width diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index cea4cae3e..9536bcaad 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -64,21 +64,21 @@ class TextEditorComponent @scrollViewNode.classList.add('scroll-view') @domNode.appendChild(@scrollViewNode) - @mountGutterComponent() if @presenter.state.gutter.visible + @mountGutterComponent() if @presenter.getState().gutter.visible - @hiddenInputComponent = new InputComponent(@presenter) + @hiddenInputComponent = new InputComponent @scrollViewNode.appendChild(@hiddenInputComponent.domNode) @linesComponent = new LinesComponent({@presenter, @hostElement, @useShadowDOM}) @scrollViewNode.appendChild(@linesComponent.domNode) - @horizontalScrollbarComponent = new ScrollbarComponent({@presenter, orientation: 'horizontal', onScroll: @onHorizontalScroll}) + @horizontalScrollbarComponent = new ScrollbarComponent({orientation: 'horizontal', onScroll: @onHorizontalScroll}) @scrollViewNode.appendChild(@horizontalScrollbarComponent.domNode) - @verticalScrollbarComponent = new ScrollbarComponent({@presenter, orientation: 'vertical', onScroll: @onVerticalScroll}) + @verticalScrollbarComponent = new ScrollbarComponent({orientation: 'vertical', onScroll: @onVerticalScroll}) @domNode.appendChild(@verticalScrollbarComponent.domNode) - @scrollbarCornerComponent = new ScrollbarCornerComponent(@presenter) + @scrollbarCornerComponent = new ScrollbarCornerComponent @domNode.appendChild(@scrollbarCornerComponent.domNode) @observeEditor() @@ -104,7 +104,7 @@ class TextEditorComponent updateSync: -> @oldState ?= {} - @newState = @presenter.state + @newState = @presenter.getState() cursorMoved = @cursorMoved selectionChanged = @selectionChanged @@ -128,18 +128,18 @@ class TextEditorComponent else @domNode.style.height = '' - if @presenter.state.gutter.visible + if @newState.gutter.visible @mountGutterComponent() unless @gutterComponent? - @gutterComponent.updateSync() + @gutterComponent.updateSync(@newState) else @gutterComponent?.domNode?.remove() @gutterComponent = null - @hiddenInputComponent.updateSync() - @linesComponent.updateSync() - @horizontalScrollbarComponent.updateSync() - @verticalScrollbarComponent.updateSync() - @scrollbarCornerComponent.updateSync() + @hiddenInputComponent.updateSync(@newState) + @linesComponent.updateSync(@newState) + @horizontalScrollbarComponent.updateSync(@newState) + @verticalScrollbarComponent.updateSync(@newState) + @scrollbarCornerComponent.updateSync(@newState) if @editor.isAlive() @updateParentViewFocusedClassIfNeeded() @@ -152,7 +152,7 @@ class TextEditorComponent @linesComponent.measureCharactersInNewLines() if @isVisible() and not @newState.content.scrollingVertically mountGutterComponent: -> - @gutterComponent = new GutterComponent({@presenter, @editor, onMouseDown: @onGutterMouseDown}) + @gutterComponent = new GutterComponent({@editor, onMouseDown: @onGutterMouseDown}) @domNode.insertBefore(@gutterComponent.domNode, @domNode.firstChild) becameVisible: -> diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 5690f51c9..70eef98a9 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -26,13 +26,18 @@ class TextEditorPresenter @observeConfig() @buildState() @startBlinkingCursors() if @focused + @updating = false destroy: -> @disposables.dispose() + # 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 + emitDidUpdateState: -> + @emitter.emit "did-update-state" if @isBatching() + transferMeasurementsToModel: -> @model.setHeight(@explicitHeight) if @explicitHeight? @model.setWidth(@contentFrameWidth) if @contentFrameWidth? @@ -43,6 +48,59 @@ class TextEditorPresenter @model.setVerticalScrollbarWidth(@measuredVerticalScrollbarWidth) if @measuredVerticalScrollbarWidth? @model.setHorizontalScrollbarHeight(@measuredHorizontalScrollbarHeight) if @measuredHorizontalScrollbarHeight? + # Private: Determines whether {TextEditorPresenter} is currently batching changes. + # Returns a {Boolean}, `true` if is collecting changes, `false` if is applying them. + isBatching: -> + @updating is false + + # Private: Executes `fn` if `isBatching()` is false, otherwise sets `@[flagName]` to `true` for later processing. In either cases, it calls `emitDidUpdateState`. + # * `flagName` {String} name of a property of this presenter + # * `fn` {Function} to call when not batching. + batch: (flagName, fn) -> + if @isBatching() + @[flagName] = true + else + fn.apply(this) + + @emitDidUpdateState() + + # Public: Gets this presenter's state, updating it just in time before returning from this function. + # Returns a state {Object}, useful for rendering to screen. + getState: -> + @updating = true + + @updateFocusedState() if @shouldUpdateFocusedState + @updateHeightState() if @shouldUpdateHeightState + @updateVerticalScrollState() if @shouldUpdateVerticalScrollState + @updateHorizontalScrollState() if @shouldUpdateHorizontalScrollState + @updateScrollbarsState() if @shouldUpdateScrollbarsState + @updateHiddenInputState() if @shouldUpdateHiddenInputState + @updateContentState() if @shouldUpdateContentState + @updateDecorations() if @shouldUpdateDecorations + @updateLinesState() if @shouldUpdateLinesState + @updateCursorsState() if @shouldUpdateCursorsState + @updateOverlaysState() if @shouldUpdateOverlaysState + @updateGutterState() if @shouldUpdateGutterState + @updateLineNumbersState() if @shouldUpdateLineNumbersState + + @shouldUpdateFocusedState = false + @shouldUpdateHeightState = false + @shouldUpdateVerticalScrollState = false + @shouldUpdateHorizontalScrollState = false + @shouldUpdateScrollbarsState = false + @shouldUpdateHiddenInputState = false + @shouldUpdateContentState = false + @shouldUpdateDecorations = false + @shouldUpdateLinesState = false + @shouldUpdateCursorsState = false + @shouldUpdateOverlaysState = false + @shouldUpdateGutterState = false + @shouldUpdateLineNumbersState = false + + @updating = false + + @state + observeModel: -> @disposables.add @model.onDidChange => @updateContentDimensions() @@ -141,18 +199,16 @@ class TextEditorPresenter @updateGutterState() @updateLineNumbersState() - updateFocusedState: -> + updateFocusedState: -> @batch "shouldUpdateFocusedState", -> @state.focused = @focused - updateHeightState: -> + updateHeightState: -> @batch "shouldUpdateHeightState", -> if @autoHeight @state.height = @contentHeight else @state.height = null - @emitter.emit 'did-update-state' - - updateVerticalScrollState: -> + updateVerticalScrollState: -> @batch "shouldUpdateVerticalScrollState", -> @state.content.scrollHeight = @scrollHeight @state.gutter.scrollHeight = @scrollHeight @state.verticalScrollbar.scrollHeight = @scrollHeight @@ -161,18 +217,14 @@ class TextEditorPresenter @state.gutter.scrollTop = @scrollTop @state.verticalScrollbar.scrollTop = @scrollTop - @emitter.emit 'did-update-state' - - updateHorizontalScrollState: -> + updateHorizontalScrollState: -> @batch "shouldUpdateHorizontalScrollState", -> @state.content.scrollWidth = @scrollWidth @state.horizontalScrollbar.scrollWidth = @scrollWidth @state.content.scrollLeft = @scrollLeft @state.horizontalScrollbar.scrollLeft = @scrollLeft - @emitter.emit 'did-update-state' - - updateScrollbarsState: -> + updateScrollbarsState: -> @batch "shouldUpdateScrollbarsState", -> @state.horizontalScrollbar.visible = @horizontalScrollbarHeight > 0 @state.horizontalScrollbar.height = @measuredHorizontalScrollbarHeight @state.horizontalScrollbar.right = @verticalScrollbarWidth @@ -181,9 +233,7 @@ class TextEditorPresenter @state.verticalScrollbar.width = @measuredVerticalScrollbarWidth @state.verticalScrollbar.bottom = @horizontalScrollbarHeight - @emitter.emit 'did-update-state' - - updateHiddenInputState: -> + updateHiddenInputState: -> @batch "shouldUpdateHiddenInputState", -> return unless lastCursor = @model.getLastCursor() {top, left, height, width} = @pixelRectForScreenRange(lastCursor.getScreenRange()) @@ -200,17 +250,14 @@ class TextEditorPresenter @state.hiddenInput.height = height @state.hiddenInput.width = Math.max(width, 2) - @emitter.emit 'did-update-state' - - updateContentState: -> + updateContentState: -> @batch "shouldUpdateContentState", -> @state.content.scrollWidth = @scrollWidth @state.content.scrollLeft = @scrollLeft @state.content.indentGuidesVisible = not @model.isMini() and @showIndentGuide @state.content.backgroundColor = if @model.isMini() then null else @backgroundColor @state.content.placeholderText = if @model.isEmpty() then @model.getPlaceholderText() else null - @emitter.emit 'did-update-state' - updateLinesState: -> + updateLinesState: -> @batch "shouldUpdateLinesState", -> return unless @startRow? and @endRow? and @lineHeight? visibleLineIds = {} @@ -235,8 +282,6 @@ class TextEditorPresenter unless visibleLineIds.hasOwnProperty(id) delete @state.content.lines[id] - @emitter.emit 'did-update-state' - updateLineState: (row, line) -> lineState = @state.content.lines[line.id] lineState.screenRow = row @@ -256,14 +301,10 @@ class TextEditorPresenter top: row * @lineHeight decorationClasses: @lineDecorationClassesForRow(row) - updateCursorsState: -> + updateCursorsState: -> @batch "shouldUpdateCursorsState", -> @state.content.cursors = {} - @updateCursorState(cursor) for cursor in @model.cursors # using property directly to avoid allocation - @emitter.emit 'did-update-state' - - updateCursorState: (cursor, destroyOnly = false) -> delete @state.content.cursors[cursor.id] @@ -275,7 +316,9 @@ class TextEditorPresenter pixelRect.width = @baseCharacterWidth if pixelRect.width is 0 @state.content.cursors[cursor.id] = pixelRect - updateOverlaysState: -> + @emitDidUpdateState() + + updateOverlaysState: -> @batch "shouldUpdateOverlaysState", -> return unless @hasPixelRectRequirements() visibleDecorationIds = {} @@ -296,18 +339,15 @@ class TextEditorPresenter for id of @state.content.overlays delete @state.content.overlays[id] unless visibleDecorationIds[id] - @emitter.emit "did-update-state" - - updateGutterState: -> + updateGutterState: -> @batch "shouldUpdateGutterState", -> @state.gutter.visible = not @model.isMini() and (@model.isGutterVisible() ? true) and @showLineNumbers @state.gutter.maxLineNumberDigits = @model.getLineCount().toString().length @state.gutter.backgroundColor = if @gutterBackgroundColor isnt "rgba(0, 0, 0, 0)" @gutterBackgroundColor else @backgroundColor - @emitter.emit "did-update-state" - updateLineNumbersState: -> + updateLineNumbersState: -> @batch "shouldUpdateLineNumbersState", -> return unless @startRow? and @endRow? and @lineHeight? visibleLineNumberIds = {} @@ -350,8 +390,6 @@ class TextEditorPresenter for id of @state.gutter.lineNumbers delete @state.gutter.lineNumbers[id] unless visibleLineNumberIds[id] - @emitter.emit 'did-update-state' - updateStartRow: -> return unless @scrollTop? and @lineHeight? @@ -536,7 +574,7 @@ class TextEditorPresenter @stoppedScrollingTimeoutId = null @stoppedScrollingTimeoutId = setTimeout(@didStopScrolling.bind(this), @stoppedScrollingDelay) @state.content.scrollingVertically = true - @emitter.emit 'did-update-state' + @emitDidUpdateState() didStopScrolling: -> @state.content.scrollingVertically = false @@ -545,7 +583,7 @@ class TextEditorPresenter @updateLinesState() @updateLineNumbersState() else - @emitter.emit 'did-update-state' + @emitDidUpdateState() setScrollLeft: (scrollLeft) -> scrollLeft = @constrainScrollLeft(scrollLeft) @@ -809,7 +847,7 @@ class TextEditorPresenter decorationState.flashCount++ decorationState.flashClass = flash.class decorationState.flashDuration = flash.duration - @emitter.emit "did-update-state" + @emitDidUpdateState() didAddDecoration: (decoration) -> @observeDecoration(decoration) @@ -823,7 +861,7 @@ class TextEditorPresenter else if decoration.isType('overlay') @updateOverlaysState() - updateDecorations: -> + updateDecorations: -> @batch "shouldUpdateDecorations", -> @lineDecorationsByScreenRow = {} @lineNumberDecorationsByScreenRow = {} @highlightDecorationsById = {} @@ -843,7 +881,6 @@ class TextEditorPresenter unless visibleHighlights[id] delete @state.content.highlights[id] - @emitter.emit 'did-update-state' removeFromLineDecorationCaches: (decoration, range) -> for row in [range.start.row..range.end.row] by 1 @@ -883,7 +920,7 @@ class TextEditorPresenter 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' + @emitDidUpdateState() return if range.start.row < @startRow @@ -895,7 +932,7 @@ class TextEditorPresenter if range.isEmpty() delete @state.content.highlights[decoration.id] - @emitter.emit 'did-update-state' + @emitDidUpdateState() return highlightState = @state.content.highlights[decoration.id] ?= { @@ -906,8 +943,8 @@ class TextEditorPresenter highlightState.class = properties.class highlightState.deprecatedRegionClass = properties.deprecatedRegionClass highlightState.regions = @buildHighlightRegions(range) + @emitDidUpdateState() - @emitter.emit 'did-update-state' true buildHighlightRegions: (screenRange) -> @@ -993,10 +1030,10 @@ class TextEditorPresenter toggleCursorBlink: -> @state.content.cursorsVisible = not @state.content.cursorsVisible - @emitter.emit 'did-update-state' + @emitDidUpdateState() pauseCursorBlinking: -> @stopBlinkingCursors(true) @startBlinkingCursorsAfterDelay ?= _.debounce(@startBlinkingCursors, @getCursorBlinkResumeDelay()) @startBlinkingCursorsAfterDelay() - @emitter.emit 'did-update-state' + @emitDidUpdateState() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 2e6cfdf92..b44b6e276 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -834,7 +834,8 @@ class TextEditor extends Model # argument will be a {Selection} and the second argument will be the # {Number} index of that selection. mutateSelectedText: (fn) -> - @transact => fn(selection, index) for selection, index in @getSelections() + @mergeIntersectingSelections => + @transact => fn(selection, index) for selection, index in @getSelections() # Move lines intersection the most recent selection up by one row in screen # coordinates. @@ -993,17 +994,18 @@ class TextEditor extends Model # selections to create multiple single-line selections that cumulatively cover # the same original area. splitSelectionsIntoLines: -> - for selection in @getSelections() - range = selection.getBufferRange() - continue if range.isSingleLine() + @mergeIntersectingSelections => + for selection in @getSelections() + range = selection.getBufferRange() + continue if range.isSingleLine() - selection.destroy() - {start, end} = range - @addSelectionForBufferRange([start, [start.row, Infinity]]) - {row} = start - while ++row < end.row - @addSelectionForBufferRange([[row, 0], [row, Infinity]]) - @addSelectionForBufferRange([[end.row, 0], [end.row, end.column]]) unless end.column is 0 + selection.destroy() + {start, end} = range + @addSelectionForBufferRange([start, [start.row, Infinity]]) + {row} = start + while ++row < end.row + @addSelectionForBufferRange([[row, 0], [row, Infinity]]) + @addSelectionForBufferRange([[end.row, 0], [end.row, end.column]]) unless end.column is 0 # Extended: For each selection, transpose the selected text. # @@ -1140,7 +1142,8 @@ class TextEditor extends Model # with a positive `groupingInterval` is committed while the previous transaction is # still 'groupable', the two transactions are merged with respect to undo and redo. # * `fn` A {Function} to call inside the transaction. - transact: (groupingInterval, fn) -> @buffer.transact(groupingInterval, fn) + transact: (groupingInterval, fn) -> + @buffer.transact(groupingInterval, fn) # Deprecated: Start an open-ended transaction. beginTransaction: (groupingInterval) -> @buffer.beginTransaction(groupingInterval) @@ -2228,6 +2231,7 @@ class TextEditor extends Model [head, tail...] = @getSelectionsOrderedByBufferPosition() _.reduce(tail, reducer, [head]) + return result if fn? # Add a {Selection} based on the given {Marker}. #