Merge branch 'ns-teleditor'

This commit is contained in:
Nathan Sobo
2013-12-11 11:17:45 -08:00
10 changed files with 81 additions and 104 deletions

View File

@@ -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"
},

View File

@@ -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", ->

View File

@@ -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')

View File

@@ -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 ->

View File

@@ -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)

View File

@@ -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

View File

@@ -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")

View File

@@ -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()

View File

@@ -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

View File

@@ -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