From 024618b69ac24d6a2c0a08ac05497c5f4f2ed3f2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 6 Jun 2012 12:42:47 -0600 Subject: [PATCH 1/3] Build Renderer in EditSession and assign it when switching to that EditSession --- src/app/edit-session.coffee | 17 ++++++++++++++--- src/app/editor.coffee | 37 ++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index ac30d010e..12a9c4f43 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,8 +15,9 @@ class EditSession scrollTop: 0 scrollLeft: 0 cursorScreenPosition: null + renderer: null - constructor: (@buffer) -> + constructor: (@editor, @buffer) -> @setCursorScreenPosition([0, 0]) serialize: -> @@ -24,6 +26,9 @@ class EditSession scrollLeft: @getScrollLeft() cursorScreenPosition: @getCursorScreenPosition().serialize() + getRenderer: -> + @renderer ?= new Renderer(@buffer, { softWrapColumn: @editor.calcSoftWrapColumn(), tabText: @editor.tabText }) + setScrollTop: (@scrollTop) -> getScrollTop: -> @scrollTop @@ -36,3 +41,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..382108aff 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 } @@ -327,14 +325,15 @@ class Editor extends View @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) @renderer.on 'change', (e) => @handleRendererChange(e) + setRenderer: (renderer) -> + @renderer?.off() + @renderer = renderer + editSessionForBuffer: (buffer) -> for editSession, index in @editSessions return [editSession, index] if editSession.buffer == buffer @@ -345,7 +344,7 @@ class Editor extends View if editSession @activeEditSessionIndex = index else - @editSessions.push(new EditSession(buffer)) + @editSessions.push(new EditSession(this, buffer)) @activeEditSessionIndex = @editSessions.length - 1 @loadEditSession() @@ -363,7 +362,11 @@ class Editor extends View throw new Error("Edit session not found") unless editSession @setBuffer(editSession.buffer) unless @buffer == editSession.buffer @activeEditSessionIndex = index - @setScrollPositionFromActiveEditSession() if @attached + @setRenderer(editSession.getRenderer()) + if @attached + @prepareForScrolling() + @setScrollPositionFromActiveEditSession() + @renderLines() @setCursorScreenPosition(editSession.cursorScreenPosition ? [0, 0]) getActiveEditSession: -> From 1344d42061476329ca2677c0757883e4a0676572 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 6 Jun 2012 12:48:45 -0600 Subject: [PATCH 2/3] Add spec to ensure we unsubscribe from every buffer when we remove editor --- spec/app/editor-spec.coffee | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 97d959b35..1c99b4380 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -79,6 +79,18 @@ 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)", -> it "sets the cursor to the beginning of the file", -> expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) From ff52993c9e75bd764291eca56dde9cd85707e316 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 6 Jun 2012 17:00:02 -0600 Subject: [PATCH 3/3] Fix bugs when switching between EditSessions --- spec/app/editor-spec.coffee | 34 +++++++++++++++++++-------- src/app/edit-session.coffee | 3 +++ src/app/editor.coffee | 47 +++++++++++++++++++------------------ 3 files changed, 51 insertions(+), 33 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 1c99b4380..5f977e280 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -92,20 +92,24 @@ describe "Editor", -> 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 @@ -118,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' @@ -135,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) @@ -153,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 12a9c4f43..13f1b7778 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -20,6 +20,9 @@ class EditSession constructor: (@editor, @buffer) -> @setCursorScreenPosition([0, 0]) + destroy: -> + @renderer.destroy() + serialize: -> buffer: @buffer.serialize() scrollTop: @getScrollTop() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 382108aff..c14f6a0e5 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -317,36 +317,31 @@ class Editor extends View @renderer.destroyFoldsContainingBufferRow(bufferRow) setBuffer: (buffer) -> - if @buffer - @saveCurrentEditSession() - @unsubscribeFromBuffer() - - @buffer = buffer - @trigger 'editor-path-change' - @buffer.on "path-change.editor#{@id}", => @trigger 'editor-path-change' - - @loadEditSessionForBuffer(@buffer) - - @buffer.on "change.editor#{@id}", (e) => @handleBufferChange(e) - @renderer.on 'change', (e) => @handleRendererChange(e) + @loadEditSessionForBuffer(buffer) 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 + index = @editSessionIndexForBuffer(buffer) + unless index? + index = @editSessions.length @editSessions.push(new EditSession(this, buffer)) - @activeEditSessionIndex = @editSessions.length - 1 - @loadEditSession() + + @loadEditSession(index) loadNextEditSession: -> nextIndex = (@activeEditSessionIndex + 1) % @editSessions.length @@ -360,7 +355,8 @@ 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 @setRenderer(editSession.getRenderer()) if @attached @@ -752,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}" @@ -761,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