diff --git a/package.json b/package.json index 2c5c8a319..37abf3a07 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "season": "0.14.0", "semver": "1.1.4", "space-pen": "2.0.1", - "telepath": "0.65.0", + "telepath": "0.66.0", "temp": "0.5.0", "underscore-plus": "0.5.0" }, diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 2193eaca9..85693921d 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -21,20 +21,24 @@ describe "Editor", -> buffer = editor.buffer lineLengths = buffer.getLines().map (line) -> line.length - describe "@deserialize(state)", -> + describe "when the editor is deserialized", -> it "restores selections and folds based on markers in the buffer", -> editor.setSelectedBufferRange([[1, 2], [3, 4]]) editor.addSelectionForBufferRange([[5, 6], [7, 5]], isReversed: true) editor.foldBufferRow(4) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() - editor2 = atom.deserializers.deserialize(editor.serialize()) + # Simulate serialization with replicate + editor2 = editor.replicate() + # FIXME: The created hook is called manually on deserialization because globals aren't ready otherwise + editor2.created() expect(editor2.id).toBe editor.id expect(editor2.getBuffer().getPath()).toBe editor.getBuffer().getPath() expect(editor2.getSelectedBufferRanges()).toEqual [[[1, 2], [3, 4]], [[5, 6], [7, 5]]] expect(editor2.getSelection(1).isReversed()).toBeTruthy() expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy() + editor2.destroy() describe ".copy()", -> it "returns a different edit session with the same initial state", -> diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index adc6e15d0..24cb8be07 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -59,22 +59,6 @@ describe "Project", -> editor.saveAs(tempFile) expect(atom.project.getPath()).toBe path.dirname(tempFile) - describe "when an edit session is deserialized", -> - it "emits an 'editor-created' event and stores the edit session", -> - handler = jasmine.createSpy('editorCreatedHandler') - atom.project.on 'editor-created', handler - - editor1 = atom.project.openSync("a") - expect(handler.callCount).toBe 1 - expect(atom.project.getEditors().length).toBe 1 - expect(atom.project.getEditors()[0]).toBe editor1 - - editor2 = atom.deserializers.deserialize(editor1.serialize()) - expect(handler.callCount).toBe 2 - expect(atom.project.getEditors().length).toBe 2 - expect(atom.project.getEditors()[0]).toBe editor1 - expect(atom.project.getEditors()[1]).toBe editor2 - describe "when an edit session is copied", -> it "emits an 'editor-created' event and stores the edit session", -> handler = jasmine.createSpy('editorCreatedHandler') diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee index a9290c5e1..cc6f26597 100644 --- a/spec/selection-spec.coffee +++ b/spec/selection-spec.coffee @@ -5,7 +5,7 @@ describe "Selection", -> beforeEach -> buffer = atom.project.bufferForPathSync('sample.js') - editor = new Editor(buffer: buffer, tabLength: 2) + editor = atom.create(new Editor(buffer: buffer, tabLength: 2)) selection = editor.getSelection() afterEach -> diff --git a/src/atom.coffee b/src/atom.coffee index 214efafde..16b073194 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -427,7 +427,13 @@ class Atom serializedWindowState = @loadSerializedWindowState() doc = Document.deserialize(serializedWindowState) if serializedWindowState? doc ?= Document.create() - doc.registerModelClasses(require('./text-buffer'), require('./project'), require('./tokenized-buffer'), require('./display-buffer')) + doc.registerModelClasses( + require('./text-buffer'), + require('./project'), + require('./tokenized-buffer'), + require('./display-buffer'), + require('./editor') + ) # TODO: Remove this when everything is using telepath models if @site? @site.setRootDocument(doc) diff --git a/src/deserializer-manager.coffee b/src/deserializer-manager.coffee index ccf842540..4f6b51588 100644 --- a/src/deserializer-manager.coffee +++ b/src/deserializer-manager.coffee @@ -1,4 +1,4 @@ -{Document} = require 'telepath' +{Document, Model} = require 'telepath' # Public: Manages the deserializers used for serialized state # @@ -25,6 +25,8 @@ class DeserializerManager deserialize: (state, params) -> return unless state? + return state if state instanceof Model + if deserializer = @get(state) stateVersion = state.get?('version') ? state.version return if deserializer.version? and deserializer.version isnt stateVersion diff --git a/src/editor-view.coffee b/src/editor-view.coffee index a11c0fa93..fb1540d61 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -105,12 +105,12 @@ class EditorView extends View if editor? @edit(editor) else if @mini - @edit(new Editor + @edit(atom.create(new Editor buffer: atom.create(new TextBuffer) softWrap: false tabLength: 2 softTabs: true - ) + )) else throw new Error("Must supply an Editor or mini: true") diff --git a/src/editor.coffee b/src/editor.coffee index 51cf56c98..2201c3132 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -1,13 +1,11 @@ _ = require 'underscore-plus' path = require 'path' -telepath = require 'telepath' guid = require 'guid' -{Point, Range} = telepath +{Model, Point, Range} = require 'telepath' LanguageMode = require './language-mode' DisplayBuffer = require './display-buffer' Cursor = require './cursor' Selection = require './selection' -{Emitter, Subscriber} = require 'emissary' TextMateScopeSelector = require('first-mate').ScopeSelector # Public: The core model of Atom. @@ -36,82 +34,70 @@ TextMateScopeSelector = require('first-mate').ScopeSelector # FIXME: Describe how there are both local and remote cursors and selections and # why that is. module.exports = -class Editor - Emitter.includeInto(this) - Subscriber.includeInto(this) +class Editor extends Model - @acceptsDocuments: true + @properties + displayBuffer: null + softTabs: null + scrollTop: 0 + scrollLeft: 0 - atom.deserializers.add(this) - - @version: 6 - - @deserialize: (state) -> - new Editor(state) - - id: null + deserializing: false + callDisplayBufferCreatedHook: false + buffer: null languageMode: null - displayBuffer: null cursors: null remoteCursors: null selections: null remoteSelections: null suppressSelectionMerging: false - # Private: - constructor: (optionsOrState) -> + constructor: -> + super + @deserializing = @state? + + created: -> + if @deserializing + @deserializing = false + @callDisplayBufferCreatedHook = true + return + @cursors = [] @remoteCursors = [] @selections = [] @remoteSelections = [] - if optionsOrState instanceof telepath.Document - @state = optionsOrState - @id = @state.get('id') - displayBuffer = @state.get('displayBuffer') - displayBuffer.created() - @setBuffer(displayBuffer.buffer) - @setDisplayBuffer(displayBuffer) - for marker in @findMarkers(@getSelectionMarkerAttributes()) - marker.setAttributes(preserveFolds: true) - @addSelection(marker) - @setScrollTop(@state.get('scrollTop')) - @setScrollLeft(@state.get('scrollLeft')) - registerEditor = true - else - {buffer, displayBuffer, tabLength, softTabs, softWrap, suppressCursorCreation, initialLine} = optionsOrState - @id = guid.create().toString() - displayBuffer ?= atom.create(new DisplayBuffer({buffer, tabLength, softWrap})) - @state = atom.site.createDocument - deserializer: @constructor.name - version: @constructor.version - id: @id - displayBuffer: displayBuffer - softTabs: buffer.usesSoftTabs() ? softTabs ? atom.config.get('editor.softTabs') ? true - scrollTop: 0 - scrollLeft: 0 - @setBuffer(buffer) - @setDisplayBuffer(displayBuffer) - if @getCursors().length is 0 and not suppressCursorCreation - if initialLine - position = [initialLine, 0] + unless @displayBuffer? + @displayBuffer = new DisplayBuffer({@buffer, @tabLength, @softWrap}) + @softTabs = @buffer.usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true + + @displayBuffer.created() if @callDisplayBufferCreatedHook + @buffer = @displayBuffer.buffer + + for marker in @findMarkers(@getSelectionMarkerAttributes()) + marker.setAttributes(preserveFolds: true) + @addSelection(marker) + + @subscribeToBuffer() + @subscribeToDisplayBuffer() + + if @getCursors().length is 0 and not @suppressCursorCreation + if @initialLine + position = [@initialLine, 0] else position = _.last(@getRemoteCursors())?.getBufferPosition() ? [0, 0] @addCursorAtBufferPosition(position) @languageMode = new LanguageMode(this, @buffer.getExtension()) - @subscribe @state, 'changed', ({newValues}) => - for key, newValue of newValues - switch key - when 'scrollTop' - @emit 'scroll-top-changed', newValue - when 'scrollLeft' - @emit 'scroll-left-changed', newValue - atom.project.addEditor(this) if registerEditor + @subscribe @$scrollTop, 'value', (scrollTop) => @emit 'scroll-top-changed', scrollTop + @subscribe @$scrollLeft, 'value', (scrollLeft) => @emit 'scroll-left-changed', scrollLeft + + # Deprecated: The goal is a world where we don't call serialize explicitly + serialize: -> this # Private: - setBuffer: (@buffer) -> + subscribeToBuffer: -> @buffer.retain() @subscribe @buffer, "path-changed", => unless atom.project.getPath()? @@ -124,7 +110,7 @@ class Editor @preserveCursorPositionOnBufferReload() # Private: - setDisplayBuffer: (@displayBuffer) -> + subscribeToDisplayBuffer: -> @subscribe @displayBuffer, 'marker-created', @handleMarkerCreated @subscribe @displayBuffer, "changed", (e) => @emit 'screen-lines-changed', e @subscribe @displayBuffer, "markers-updated", => @mergeIntersectingSelections() @@ -148,18 +134,12 @@ class Editor @emit 'destroyed' @off() - # Private: - serialize: -> @state.clone() - - # Private: - getState: -> @state - # Private: Creates an {Editor} with the same initial state copy: -> tabLength = @getTabLength() displayBuffer = @displayBuffer.copy() softTabs = @getSoftTabs() - newEditor = new Editor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true}) + newEditor = @create(new Editor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true})) newEditor.setScrollTop(@getScrollTop()) newEditor.setScrollLeft(@getScrollLeft()) for marker in @findMarkers(editorId: @id) @@ -211,17 +191,17 @@ class Editor # Public: Controls visiblity based on the given Boolean. setVisible: (visible) -> @displayBuffer.setVisible(visible) - # Public: FIXME: I don't understand this. - setScrollTop: (scrollTop) -> @state.set('scrollTop', scrollTop) + # Deprecated: Use the ::scrollTop property directly + setScrollTop: (@scrollTop) -> @scrollTop - # Public: Returns the current `scrollTop` value - getScrollTop: -> @state.get('scrollTop') ? 0 + # Deprecated: Use the ::scrollTop property directly + getScrollTop: -> @scrollTop - # Public: FIXME: I don't understand this. - setScrollLeft: (scrollLeft) -> @state.set('scrollLeft', scrollLeft) + # Deprecated: Use the ::scrollLeft property directly + setScrollLeft: (@scrollLeft) -> @scrollLeft - # Public: Returns the current `scrollLeft` value - getScrollLeft: -> @state.get('scrollLeft') + # Deprecated: Use the ::scrollLeft property directly + getScrollLeft: -> @scrollLeft # Set the number of characters that can be displayed horizontally in the # editor that contains this edit session. @@ -233,12 +213,11 @@ class Editor # Public: Sets the column at which columsn will soft wrap getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn() - # Public: Returns whether soft tabs are enabled or not. - getSoftTabs: -> @state.get('softTabs') + # Deprecated: Use the ::softTabs property directly. Indicates whether soft tabs are enabled. + getSoftTabs: -> @softTabs - # Public: Controls whether soft tabs are enabled or not. - setSoftTabs: (softTabs) -> - @state.set('softTabs', softTabs) + # Deprecated: Use the ::softTabs property directly. Indicates whether soft tabs are enabled. + setSoftTabs: (@softTabs) -> @softTabs # Public: Returns whether soft wrap is enabled or not. getSoftWrap: -> @displayBuffer.getSoftWrap() diff --git a/src/pane.coffee b/src/pane.coffee index bd17a0aa4..e7f85a8f3 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -34,7 +34,9 @@ class Pane extends View if args[0] instanceof telepath.Document @state = args[0] @items = _.compact @state.get('items').map (item) -> - atom.deserializers.deserialize(item) + item = atom.deserializers.deserialize(item) + item?.created?() + item else @items = args @state = atom.site.createDocument diff --git a/src/project.coffee b/src/project.coffee index 4caa1a392..27f3c0880 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -334,7 +334,7 @@ class Project extends telepath.Model # Private: buildEditorForBuffer: (buffer, editorOptions) -> - editor = new Editor(_.extend({buffer}, editorOptions)) + editor = @create(new Editor(_.extend({buffer}, editorOptions))) @addEditor(editor) editor