diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 97d959b35..5f977e280 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -79,21 +79,37 @@ describe "Editor", -> expect(newEditor.scrollView.scrollLeft()).toBe 44 newEditor.remove() + describe ".remove()", -> + it "removes subscriptions from all edit session buffers", -> + otherBuffer = new Buffer(require.resolve('fixtures/sample.txt')) + expect(buffer.subscriptionCount()).toBeGreaterThan 1 + + editor.setBuffer(otherBuffer) + expect(otherBuffer.subscriptionCount()).toBeGreaterThan 1 + + editor.remove() + expect(buffer.subscriptionCount()).toBe 1 + expect(otherBuffer.subscriptionCount()).toBe 1 + describe ".setBuffer(buffer)", -> + otherBuffer = null + + beforeEach -> + otherBuffer = new Buffer + it "sets the cursor to the beginning of the file", -> expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) it "recalls the cursor position and scroll position when the same buffer is re-assigned", -> editor.attachToDom() + editor.height(editor.lineHeight * 5) editor.width(editor.charWidth * 30) editor.setCursorScreenPosition([8, 28]) - advanceClock() - previousScrollTop = editor.verticalScrollbar.scrollTop() previousScrollLeft = editor.scrollView.scrollLeft() - editor.setBuffer(new Buffer) + editor.setBuffer(otherBuffer) expect(editor.getCursorScreenPosition()).toEqual [0, 0] expect(editor.verticalScrollbar.scrollTop()).toBe 0 expect(editor.scrollView.scrollLeft()).toBe 0 @@ -106,7 +122,6 @@ describe "Editor", -> it "recalls the undo history of the buffer when it is re-assigned", -> editor.insertText('xyz') - otherBuffer = new Buffer editor.setBuffer(otherBuffer) editor.insertText('abc') expect(otherBuffer.lineForRow(0)).toBe 'abc' @@ -123,15 +138,15 @@ describe "Editor", -> editor.redo() expect(otherBuffer.lineForRow(0)).toBe 'abc' - it "fully unsubscribes from the previously assigned buffer", -> - otherBuffer = new Buffer - previousSubscriptionCount = otherBuffer.subscriptionCount() - + it "unsubscribes from the previously assigned buffer", -> editor.setBuffer(otherBuffer) - expect(otherBuffer.subscriptionCount()).toBeGreaterThan previousSubscriptionCount + + previousSubscriptionCount = buffer.subscriptionCount() editor.setBuffer(buffer) - expect(otherBuffer.subscriptionCount()).toBe previousSubscriptionCount + editor.setBuffer(otherBuffer) + + expect(buffer.subscriptionCount()).toBe previousSubscriptionCount it "resizes the vertical scrollbar based on the new buffer's height", -> editor.attachToDom(heightInLines: 5) @@ -141,6 +156,17 @@ describe "Editor", -> editor.setBuffer(new Buffer(require.resolve('fixtures/sample.txt'))) expect(editor.verticalScrollbar.prop('scrollHeight')).toBeLessThan originalHeight + it "handles buffer manipulation correctly after switching to a new buffer", -> + editor.attachToDom() + editor.insertText("abc\n") + expect(editor.lineElementForScreenRow(0).text()).toBe 'abc' + + editor.setBuffer(otherBuffer) + expect(editor.lineElementForScreenRow(0).html()).toBe ' ' + + editor.insertText("def\n") + expect(editor.lineElementForScreenRow(0).text()).toBe 'def' + describe ".clipScreenPosition(point)", -> it "selects the nearest valid position to the given point", -> expect(editor.clipScreenPosition(row: 1000, column: 0)).toEqual(row: buffer.getLastRow(), column: buffer.lineForRow(buffer.getLastRow()).length) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index ac30d010e..13f1b7778 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -1,11 +1,12 @@ Point = require 'point' Buffer = require 'buffer' +Renderer = require 'renderer' module.exports = class EditSession - @deserialize: (state, rootView) -> + @deserialize: (state, editor, rootView) -> buffer = Buffer.deserialize(state.buffer, rootView.project) - session = new EditSession(buffer) + session = new EditSession(editor, buffer) session.setScrollTop(state.scrollTop) session.setScrollLeft(state.scrollLeft) session.setCursorScreenPosition(state.cursorScreenPosition) @@ -14,16 +15,23 @@ class EditSession scrollTop: 0 scrollLeft: 0 cursorScreenPosition: null + renderer: null - constructor: (@buffer) -> + constructor: (@editor, @buffer) -> @setCursorScreenPosition([0, 0]) + destroy: -> + @renderer.destroy() + serialize: -> buffer: @buffer.serialize() scrollTop: @getScrollTop() scrollLeft: @getScrollLeft() cursorScreenPosition: @getCursorScreenPosition().serialize() + getRenderer: -> + @renderer ?= new Renderer(@buffer, { softWrapColumn: @editor.calcSoftWrapColumn(), tabText: @editor.tabText }) + setScrollTop: (@scrollTop) -> getScrollTop: -> @scrollTop @@ -36,3 +44,9 @@ class EditSession getCursorScreenPosition: -> @cursorScreenPosition + isEqual: (other) -> + return false unless other instanceof EditSession + @buffer == other.buffer and + @scrollTop == other.getScrollTop() and + @scrollLeft == other.getScrollLeft() and + @cursorScreenPosition.isEqual(other.getCursorScreenPosition()) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index bbd662af5..c14f6a0e5 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -49,12 +49,14 @@ class Editor extends View attached: false lineOverdraw: 100 - @deserialize: (viewState, rootView) -> - viewState = _.clone(viewState) - viewState.editSessions = viewState.editSessions.map (state) -> EditSession.deserialize(state, rootView) - new Editor(viewState) + @deserialize: (state, rootView) -> + editor = new Editor(suppressBufferCreation: true, mini: state.mini) + editor.editSessions = state.editSessions.map (state) -> EditSession.deserialize(state, editor, rootView) + editor.loadEditSession(state.activeEditSessionIndex) + editor.isFocused = state.isFocused + editor - initialize: ({editSessions, activeEditSessionIndex, buffer, isFocused, @mini} = {}) -> + initialize: ({buffer, suppressBufferCreation, @mini} = {}) -> requireStylesheet 'editor.css' requireStylesheet 'theme/twilight.css' @@ -64,17 +66,13 @@ class Editor extends View @autoIndent = true @buildCursorAndSelection() @handleEvents() + @editSessions = [] - @editSessions = editSessions ? [] - if activeEditSessionIndex? - @loadEditSession(activeEditSessionIndex) - else if buffer? + if buffer? @setBuffer(buffer) - else + else if !suppressBufferCreation @setBuffer(new Buffer) - @isFocused = isFocused if isFocused? - serialize: -> @saveCurrentEditSession() { viewClass: "Editor", editSessions: @serializeEditSessions(), @activeEditSessionIndex, @isFocused } @@ -319,35 +317,31 @@ class Editor extends View @renderer.destroyFoldsContainingBufferRow(bufferRow) setBuffer: (buffer) -> - if @buffer - @saveCurrentEditSession() - @unsubscribeFromBuffer() + @loadEditSessionForBuffer(buffer) - @buffer = buffer - @trigger 'editor-path-change' - @buffer.on "path-change.editor#{@id}", => @trigger 'editor-path-change' - - @renderer = new Renderer(@buffer, { softWrapColumn: @calcSoftWrapColumn(), tabText: @tabText }) - @prepareForScrolling() if @attached - @loadEditSessionForBuffer(@buffer) - @renderLines() if @attached - - @buffer.on "change.editor#{@id}", (e) => @handleBufferChange(e) + setRenderer: (renderer) -> + @renderer?.off() + @renderer = renderer @renderer.on 'change', (e) => @handleRendererChange(e) - editSessionForBuffer: (buffer) -> + @unsubscribeFromBuffer() if @buffer + @buffer = renderer.buffer + @buffer.on "path-change.editor#{@id}", => @trigger 'editor-path-change' + @buffer.on "change.editor#{@id}", (e) => @handleBufferChange(e) + @trigger 'editor-path-change' + + editSessionIndexForBuffer: (buffer) -> for editSession, index in @editSessions - return [editSession, index] if editSession.buffer == buffer - [undefined, -1] + return index if editSession.buffer == buffer + null loadEditSessionForBuffer: (buffer) -> - [editSession, index] = @editSessionForBuffer(buffer) - if editSession - @activeEditSessionIndex = index - else - @editSessions.push(new EditSession(buffer)) - @activeEditSessionIndex = @editSessions.length - 1 - @loadEditSession() + index = @editSessionIndexForBuffer(buffer) + unless index? + index = @editSessions.length + @editSessions.push(new EditSession(this, buffer)) + + @loadEditSession(index) loadNextEditSession: -> nextIndex = (@activeEditSessionIndex + 1) % @editSessions.length @@ -361,9 +355,14 @@ class Editor extends View loadEditSession: (index=@activeEditSessionIndex) -> editSession = @editSessions[index] throw new Error("Edit session not found") unless editSession - @setBuffer(editSession.buffer) unless @buffer == editSession.buffer + @saveCurrentEditSession() if @activeEditSessionIndex? + @activeEditSessionIndex = index - @setScrollPositionFromActiveEditSession() if @attached + @setRenderer(editSession.getRenderer()) + if @attached + @prepareForScrolling() + @setScrollPositionFromActiveEditSession() + @renderLines() @setCursorScreenPosition(editSession.cursorScreenPosition ? [0, 0]) getActiveEditSession: -> @@ -749,7 +748,10 @@ class Editor extends View return super if keepData @trigger 'before-remove' + + @destroyEditSessions() @unsubscribeFromBuffer() + $(window).off ".editor#{@id}" rootView = @rootView() rootView?.off ".editor#{@id}" @@ -758,7 +760,9 @@ class Editor extends View unsubscribeFromBuffer: -> @buffer.off ".editor#{@id}" - @renderer.destroy() + + destroyEditSessions: -> + session.destroy() for session in @editSessions stateForScreenRow: (row) -> @renderer.lineForRow(row).state