diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee
index 5f5e9a371..2193eaca9 100644
--- a/spec/editor-spec.coffee
+++ b/spec/editor-spec.coffee
@@ -2389,17 +2389,16 @@ describe "Editor", ->
describe "when a better-matched grammar is added to syntax", ->
it "switches to the better-matched grammar and re-tokenizes the buffer", ->
- editor.destroy()
jsGrammar = atom.syntax.selectGrammar('a.js')
atom.syntax.removeGrammar(jsGrammar)
- editor = atom.project.openSync('sample.js', autoIndent: false)
- expect(editor.getGrammar()).toBe atom.syntax.nullGrammar
- expect(editor.lineForScreenRow(0).tokens.length).toBe 1
+ editor2 = atom.project.openSync('sample.js', autoIndent: false)
+ expect(editor2.getGrammar()).toBe atom.syntax.nullGrammar
+ expect(editor2.lineForScreenRow(0).tokens.length).toBe 1
atom.syntax.addGrammar(jsGrammar)
- expect(editor.getGrammar()).toBe jsGrammar
- expect(editor.lineForScreenRow(0).tokens.length).toBeGreaterThan 1
+ expect(editor2.getGrammar()).toBe jsGrammar
+ expect(editor2.lineForScreenRow(0).tokens.length).toBeGreaterThan 1
describe "auto-indent", ->
copyText = (text, {startColumn}={}) ->
diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee
index 029765227..75d3661dc 100644
--- a/spec/pane-spec.coffee
+++ b/spec/pane-spec.coffee
@@ -698,18 +698,16 @@ describe "Pane", ->
expect(newPane.items.length).toBe pane.items.length - 1
it "focuses the pane after attach only if had focus when serialized", ->
- reloadContainer = ->
- containerState = container.serialize()
- container.remove()
- container = atom.deserializers.deserialize(containerState)
- pane = container.getRoot()
- container.attachToDom()
-
container.attachToDom()
pane.focus()
- reloadContainer()
- expect(pane).toMatchSelector(':has(:focus)')
+
+ container2 = atom.deserializers.deserialize(container.serialize())
+ pane2 = container2.getRoot()
+ container2.attachToDom()
+ expect(pane2).toMatchSelector(':has(:focus)')
$(document.activeElement).blur()
- reloadContainer()
- expect(pane).not.toMatchSelector(':has(:focus)')
+ container3 = atom.deserializers.deserialize(container.serialize())
+ pane3 = container3.getRoot()
+ container3.attachToDom()
+ expect(pane3).not.toMatchSelector(':has(:focus)')
diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee
index 1fd9b98de..90adf05d4 100644
--- a/spec/tokenized-buffer-spec.coffee
+++ b/spec/tokenized-buffer-spec.coffee
@@ -18,22 +18,14 @@ describe "TokenizedBuffer", ->
advanceClock() while tokenizedBuffer.firstInvalidRow()?
changeHandler?.reset()
- describe "@deserialize(state)", ->
- it "constructs a tokenized buffer with the same buffer and tabLength setting", ->
- buffer = atom.project.bufferForPathSync('sample.js')
- tokenizedBuffer1 = new TokenizedBuffer(buffer: buffer, tabLength: 4)
- tokenizedBuffer2 = atom.deserializers.deserialize(tokenizedBuffer1.serialize())
- expect(tokenizedBuffer2.buffer).toBe tokenizedBuffer1.buffer
- expect(tokenizedBuffer2.getTabLength()).toBe tokenizedBuffer1.getTabLength()
-
describe "when the buffer is destroyed", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
- tokenizedBuffer = new TokenizedBuffer({buffer})
+ tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
startTokenizing(tokenizedBuffer)
it "stops tokenization", ->
- tokenizedBuffer.destroy()
+ tokenizedBuffer.state.destroy()
spyOn(tokenizedBuffer, 'tokenizeNextChunk')
advanceClock()
expect(tokenizedBuffer.tokenizeNextChunk).not.toHaveBeenCalled()
@@ -41,7 +33,7 @@ describe "TokenizedBuffer", ->
describe "when the buffer contains soft-tabs", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
- tokenizedBuffer = new TokenizedBuffer({buffer})
+ tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
startTokenizing(tokenizedBuffer)
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
@@ -321,7 +313,7 @@ describe "TokenizedBuffer", ->
beforeEach ->
atom.packages.activatePackage('language-coffee-script', sync: true)
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
- tokenizedBuffer = new TokenizedBuffer({buffer})
+ tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
startTokenizing(tokenizedBuffer)
afterEach ->
@@ -355,7 +347,7 @@ describe "TokenizedBuffer", ->
'abc\uD835\uDF97def'
//\uD835\uDF97xyz
"""
- tokenizedBuffer = new TokenizedBuffer({buffer})
+ tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
fullyTokenize(tokenizedBuffer)
afterEach ->
@@ -392,7 +384,7 @@ describe "TokenizedBuffer", ->
buffer = atom.project.bufferForPathSync()
buffer.setText "
<%= User.find(2).full_name %>
"
- tokenizedBuffer = new TokenizedBuffer({buffer})
+ tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb'))
fullyTokenize(tokenizedBuffer)
@@ -411,7 +403,7 @@ describe "TokenizedBuffer", ->
it "returns the correct token (regression)", ->
buffer = atom.project.bufferForPathSync('sample.js')
- tokenizedBuffer = new TokenizedBuffer({buffer})
+ tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.tokenForPosition([1,0]).scopes).toEqual ["source.js"]
expect(tokenizedBuffer.tokenForPosition([1,1]).scopes).toEqual ["source.js"]
@@ -420,7 +412,7 @@ describe "TokenizedBuffer", ->
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
- tokenizedBuffer = new TokenizedBuffer({buffer})
+ tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
fullyTokenize(tokenizedBuffer)
describe "when the selector does not match the token at the position", ->
diff --git a/src/atom.coffee b/src/atom.coffee
index 4cabe7372..9fdcd106b 100644
--- a/src/atom.coffee
+++ b/src/atom.coffee
@@ -80,6 +80,11 @@ class Atom
setBodyPlatformClass: ->
document.body.classList.add("platform-#{process.platform}")
+ # Public: Create a new telepath model. This won't be needed when Atom is itself
+ # a telepath model.
+ create: (model) ->
+ @site.createDocument(model)
+
# Public: Get the current window
getCurrentWindow: ->
remote.getCurrentWindow()
@@ -420,7 +425,7 @@ class Atom
serializedWindowState = @loadSerializedWindowState()
doc = Document.deserialize(serializedWindowState) if serializedWindowState?
doc ?= Document.create()
- doc.registerModelClasses(require('./text-buffer'), require('./project'))
+ doc.registerModelClasses(require('./text-buffer'), require('./project'), require('./tokenized-buffer'))
# TODO: Remove this when everything is using telepath models
if @site?
@site.setRootDocument(doc)
diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee
index 938551e85..d2e60028b 100644
--- a/src/display-buffer.coffee
+++ b/src/display-buffer.coffee
@@ -27,17 +27,18 @@ class DisplayBuffer
if optionsOrState instanceof telepath.Document
@state = optionsOrState
@id = @state.get('id')
- @tokenizedBuffer = atom.deserializers.deserialize(@state.get('tokenizedBuffer'))
+ @tokenizedBuffer = @state.get('tokenizedBuffer')
+ @tokenizedBuffer.created()
@buffer = @tokenizedBuffer.buffer
else
- {@buffer, softWrap, editorWidthInChars} = optionsOrState
+ {@buffer, softWrap, editorWidthInChars, tabLength} = optionsOrState
@id = guid.create().toString()
- @tokenizedBuffer = new TokenizedBuffer(optionsOrState)
+ @tokenizedBuffer = new TokenizedBuffer({tabLength, @buffer, project: atom.project})
@state = atom.site.createDocument
deserializer: @constructor.name
version: @constructor.version
id: @id
- tokenizedBuffer: @tokenizedBuffer.getState()
+ tokenizedBuffer: @tokenizedBuffer
softWrap: softWrap ? atom.config.get('editor.softWrap') ? false
editorWidthInChars: editorWidthInChars
@@ -62,6 +63,7 @@ class DisplayBuffer
@updateWrappedScreenLines() if @getSoftWrap()
serialize: -> @state.clone()
+
getState: -> @state
copy: ->
@@ -654,6 +656,7 @@ class DisplayBuffer
softWraps = 0
while wrapScreenColumn = @findWrapColumn(tokenizedLine.text)
[wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt(wrapScreenColumn)
+
newScreenLines.push(wrappedLine)
softWraps++
newScreenLines.push(tokenizedLine)
diff --git a/src/editor-view.coffee b/src/editor-view.coffee
index 8f45492bf..a11c0fa93 100644
--- a/src/editor-view.coffee
+++ b/src/editor-view.coffee
@@ -106,7 +106,7 @@ class EditorView extends View
@edit(editor)
else if @mini
@edit(new Editor
- buffer: TextBuffer.createAsRoot()
+ buffer: atom.create(new TextBuffer)
softWrap: false
tabLength: 2
softTabs: true
diff --git a/src/project.coffee b/src/project.coffee
index 8b1758e28..4caa1a392 100644
--- a/src/project.coffee
+++ b/src/project.coffee
@@ -34,7 +34,7 @@ class Project extends telepath.Model
# Private: Called by telepath.
created: ->
for buffer in @buffers.getValues()
- buffer.once 'destroyed', (buffer) => @removeBuffer(buffer)
+ buffer.once 'destroyed', (buffer) => @removeBuffer(buffer) if @isAlive()
@openers = []
@editors = []
@@ -64,7 +64,7 @@ class Project extends telepath.Model
# Private:
destroyed: ->
editor.destroy() for editor in @getEditors()
- buffer.release() for buffer in @getBuffers()
+ buffer.destroy() for buffer in @getBuffers()
@destroyRepo()
# Private:
@@ -239,7 +239,7 @@ class Project extends telepath.Model
# Private:
addBufferAtIndex: (buffer, index, options={}) ->
buffer = @buffers.insert(index, buffer)
- buffer.once 'destroyed', => @removeBuffer(buffer)
+ buffer.once 'destroyed', => @removeBuffer(buffer) if @isAlive()
@emit 'buffer-created', buffer
buffer
diff --git a/src/text-buffer.coffee b/src/text-buffer.coffee
index a32913679..d84ee24da 100644
--- a/src/text-buffer.coffee
+++ b/src/text-buffer.coffee
@@ -81,12 +81,12 @@ class TextBuffer extends telepath.Model
@emit 'changed', bufferChangeEvent
@scheduleModifiedEvents()
- destroy: ->
- unless @destroyed
+ destroyed: ->
+ unless @alreadyDestroyed
@cancelStoppedChangingTimeout()
@file?.off()
@unsubscribe()
- @destroyed = true
+ @alreadyDestroyed = true
@emit 'destroyed', this
isRetained: -> @refcount > 0
diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee
index 0b6ba31dd..a3146262c 100644
--- a/src/tokenized-buffer.coffee
+++ b/src/tokenized-buffer.coffee
@@ -1,16 +1,16 @@
_ = require 'underscore-plus'
TokenizedLine = require './tokenized-line'
-{Emitter, Subscriber} = require 'emissary'
Token = require './token'
-telepath = require 'telepath'
-{Point, Range} = telepath
+{Model, Point, Range} = require 'telepath'
### Internal ###
module.exports =
-class TokenizedBuffer
- Emitter.includeInto(this)
- Subscriber.includeInto(this)
+class TokenizedBuffer extends Model
+ @properties
+ bufferPath: null
+ tabLength: -> atom.config.get('editor.tabLength') ? 2
+ project: null
grammar: null
currentGrammarScore: null
@@ -20,24 +20,19 @@ class TokenizedBuffer
invalidRows: null
visible: false
- @acceptsDocuments: true
- atom.deserializers.add(this)
+ constructor: ->
+ super
+ @deserializing = @state?
- @deserialize: (state) ->
- new this(state)
+ created: ->
+ if @deserializing
+ @deserializing = false
+ return this
- constructor: (optionsOrState) ->
- if optionsOrState instanceof telepath.Document
- @state = optionsOrState
-
- # TODO: This needs to be made async, but should wait until the new Telepath changes land
- @buffer = atom.project.bufferForPathSync(optionsOrState.get('bufferPath'))
+ if @buffer? and @buffer.isAlive()
+ @bufferPath = @buffer.getPath()
else
- { @buffer, tabLength } = optionsOrState
- @state = atom.site.createDocument
- deserializer: @constructor.name
- bufferPath: @buffer.getPath()
- tabLength: tabLength ? atom.config.get('editor.tabLength') ? 2
+ @buffer = @project.bufferForPathSync(@bufferPath)
@subscribe atom.syntax, 'grammar-added grammar-updated', (grammar) =>
if grammar.injectionSelector?
@@ -48,12 +43,22 @@ class TokenizedBuffer
@on 'grammar-changed grammar-updated', => @resetTokenizedLines()
@subscribe @buffer, "changed", (e) => @handleBufferChange(e)
- @subscribe @buffer, "path-changed", => @state.set('bufferPath', @buffer.getPath())
+ @subscribe @buffer, "path-changed", => @bufferPath = @buffer.getPath()
+
+ @subscribe @$tabLength.changes.onValue (tabLength) =>
+ lastRow = @buffer.getLastRow()
+ @tokenizedLines = @buildPlaceholderTokenizedLinesForRows(0, lastRow)
+ @invalidateRow(0)
+ @emit "changed", { start: 0, end: lastRow, delta: 0 }
@reloadGrammar()
- serialize: -> @state.clone()
- getState: -> @state
+ # TODO: Remove when everything is a telepath model
+ destroy: ->
+ @destroyed()
+
+ destroyed: ->
+ @unsubscribe()
setGrammar: (grammar, score) ->
return if grammar is @grammar
@@ -87,24 +92,19 @@ class TokenizedBuffer
#
# Returns a {Number}.
getTabLength: ->
- @state.get('tabLength')
+ @tabLength
# Specifies the tab length.
#
# tabLength - A {Number} that defines the new tab length.
- setTabLength: (tabLength) ->
- @state.set('tabLength', tabLength)
- lastRow = @buffer.getLastRow()
- @tokenizedLines = @buildPlaceholderTokenizedLinesForRows(0, lastRow)
- @invalidateRow(0)
- @emit "changed", { start: 0, end: lastRow, delta: 0 }
+ setTabLength: (@tabLength) ->
tokenizeInBackground: ->
- return if not @visible or @pendingChunk or @destroyed
+ return if not @visible or @pendingChunk or not @isAlive()
@pendingChunk = true
_.defer =>
@pendingChunk = false
- @tokenizeNextChunk() unless @destroyed
+ @tokenizeNextChunk() if @isAlive() and @buffer.isAlive()
tokenizeNextChunk: ->
rowsRemaining = @chunkSize
@@ -248,10 +248,6 @@ class TokenizedBuffer
endColumn = tokenizedLine.bufferColumnForToken(lastToken) + lastToken.bufferDelta
new Range([position.row, startColumn], [position.row, endColumn])
- destroy: ->
- @unsubscribe()
- @destroyed = true
-
iterateTokensInBufferRange: (bufferRange, iterator) ->
bufferRange = Range.fromObject(bufferRange)
{ start, end } = bufferRange