Merge branch 'telepath-next'

This commit is contained in:
Nathan Sobo
2013-10-31 18:39:44 -06:00
19 changed files with 75 additions and 492 deletions

View File

@@ -36,7 +36,7 @@
"season": "0.14.0",
"semver": "1.1.4",
"space-pen": "2.0.0",
"telepath": "0.8.1",
"telepath": "0.19.0",
"temp": "0.5.0",
"underscore-plus": "0.2.0"
},
@@ -73,10 +73,9 @@
"archive-view": "0.11.0",
"autocomplete": "0.11.0",
"autoflow": "0.5.0",
"autosave": "0.4.0",
"autosave": "0.6.0",
"bookmarks": "0.8.0",
"bracket-matcher": "0.7.0",
"collaboration": "0.35.0",
"command-logger": "0.6.0",
"command-palette": "0.6.0",
"dev-live-reload": "0.13.0",

View File

@@ -1,62 +0,0 @@
{Site} = require 'telepath'
Environment = require './environment'
describe "EditSession replication", ->
[env1, env2, editSession1, editSession2] = []
beforeEach ->
env1 = new Environment(siteId: 1)
env2 = env1.clone(siteId: 2)
envConnection = env1.connect(env2)
doc2 = null
env1.run ->
editSession1 = project.openSync('sample.js')
editSession1.setScrollTop(5)
editSession1.setScrollLeft(5)
editSession1.setCursorScreenPosition([0, 5])
editSession1.addSelectionForBufferRange([[1, 2], [3, 4]])
doc1 = editSession1.getState()
doc2 = doc1.clone(env2.site)
envConnection.connect(doc1, doc2)
env2.run ->
editSession2 = deserialize(doc2)
afterEach ->
env1.destroy()
env2.destroy()
it "replicates the selections of existing replicas", ->
expect(editSession2.getRemoteSelectedBufferRanges()).toEqual editSession1.getSelectedBufferRanges()
editSession1.getLastSelection().setBufferRange([[2, 3], [4, 5]])
expect(editSession2.getRemoteSelectedBufferRanges()).toEqual editSession1.getSelectedBufferRanges()
editSession1.addCursorAtBufferPosition([5, 6])
expect(editSession2.getRemoteSelectedBufferRanges()).toEqual editSession1.getSelectedBufferRanges()
editSession1.consolidateSelections()
expect(editSession2.getRemoteSelectedBufferRanges()).toEqual editSession1.getSelectedBufferRanges()
it "introduces a local cursor for a new replica at the position of the last remote cursor", ->
expect(editSession2.getCursors().length).toBe 1
expect(editSession2.getSelections().length).toBe 1
expect(editSession2.getCursorBufferPosition()).toEqual [3, 4]
expect(editSession2.getSelectedBufferRanges()).toEqual [[[3, 4], [3, 4]]]
expect(editSession1.getRemoteCursors().length).toBe 1
expect(editSession1.getRemoteSelections().length).toBe 1
[cursor] = editSession1.getRemoteCursors()
[selection] = editSession1.getRemoteSelections()
expect(cursor.getBufferPosition()).toEqual [3, 4]
expect(selection.getBufferRange()).toEqual [[3, 4], [3, 4]]
it "replicates the scroll position", ->
expect(editSession2.getScrollTop()).toBe editSession1.getScrollTop()
expect(editSession2.getScrollLeft()).toBe editSession1.getScrollLeft()
editSession1.setScrollTop(10)
expect(editSession2.getScrollTop()).toBe 10
editSession2.setScrollLeft(20)
expect(editSession1.getScrollLeft()).toBe 20

View File

@@ -1,37 +0,0 @@
{Site} = require 'telepath'
Editor = require '../src/editor'
Environment = require './environment'
describe "Editor replication", ->
[env1, env2, editSession1, editSession2, editor1, editor2] = []
beforeEach ->
env1 = new Environment(siteId: 1)
env2 = env1.clone(siteId: 2)
envConnection = env1.connect(env2)
doc2 = null
env1.run ->
editSession1 = project.openSync('sample.js')
editSession1.setSelectedBufferRange([[1, 2], [3, 4]])
doc1 = editSession1.getState()
doc2 = doc1.clone(env2.site)
envConnection.connect(doc1, doc2)
editor1 = new Editor(editSession1)
editor1.attachToDom()
env2.run ->
editSession2 = deserialize(doc2)
editor2 = new Editor(editSession2)
editor2.attachToDom()
afterEach ->
env1.destroy()
env2.destroy()
it "displays the cursors and selections from all replicas", ->
expect(editor1.getSelectionViews().length).toBe 2
expect(editor2.getSelectionViews().length).toBe 2
expect(editor1.getCursorViews().length).toBe 2
expect(editor2.getCursorViews().length).toBe 2

View File

@@ -1,59 +0,0 @@
path = require 'path'
{Site} = require 'telepath'
{fs} = require 'atom'
Project = require '../src/project'
module.exports =
class Environment
constructor: ({@site, @state, siteId, projectPath}={}) ->
@site ?= new Site(siteId ? 1)
if @state?
@run => @project = deserialize(@state.get('project'))
else
@state = @site.createDocument({})
@project = new Project(projectPath ? path.join(__dirname, 'fixtures'))
@state.set(project: @project.getState())
clone: (params) ->
site = new Site(params.siteId)
new Environment(site: site, state: @state.clone(site))
destroy: ->
@project.destroy()
getState: -> @state
run: (fn) ->
uninstall = @install()
fn()
uninstall()
install: ->
oldSite = window.site
oldProject = window.project
window.site = @site
window.project = @project
->
window.site = oldSite
window.project = oldProject
connect: (otherEnv) ->
new EnvironmentConnection(this, otherEnv)
connectDocuments: (docA, docB, envB) ->
class EnvironmentConnection
constructor: (@envA, @envB) ->
@envA.getState().connect(@envB.getState())
connect: (docA, docB) ->
unless docA.site is @envA.site
throw new Error("Document and environment sites do not match (doc: site #{docA.site.id}, env: site #{@envA.site.id})")
unless docB.site is @envB.site
throw new Error("Document and environment sites do not match (doc: site #{docB.site.id}, env: site #{@envB.site.id})")
connection = docA.connect(docB)
connection.abFilter = (fn) => @envB.run(fn)
connection.baFilter = (fn) => @envA.run(fn)
connection

View File

@@ -1,110 +0,0 @@
path = require 'path'
temp = require 'temp'
{Site} = require 'telepath'
{View} = require 'atom'
PaneContainer = require '../src/pane-container'
Pane = require '../src/pane'
Environment = require './environment'
describe "PaneContainer replication", ->
[env1, env2, envConnection, container1, container2, pane1a, pane1b, pane1c] = []
class TestView extends View
@deserialize: ({name}) -> new TestView(name)
@content: -> @div tabindex: -1
initialize: (@name) -> @text(@name)
serialize: -> { deserializer: 'TestView', @name }
getState: -> @serialize()
getUri: -> path.join(temp.dir, @name)
isEqual: (other) -> @name is other.name
beforeEach ->
registerDeserializer(TestView)
env1 = new Environment(siteId: 1)
env2 = env1.clone(siteId: 2)
envConnection = env1.connect(env2)
doc2 = null
env1.run ->
container1 = new PaneContainer
pane1a = new Pane(new TestView('A'))
container1.setRoot(pane1a)
pane1b = pane1a.splitRight(new TestView('B'))
pane1c = pane1b.splitDown(new TestView('C'))
doc1 = container1.getState()
doc2 = doc1.clone(env2.site)
envConnection.connect(doc1, doc2)
env2.run ->
container2 = deserialize(doc2)
afterEach ->
env1.destroy()
env2.destroy()
unregisterDeserializer(TestView)
it "replicates the inital state of a pane container with splits", ->
expect(container1.find('.row > :eq(0):contains(A)')).toExist()
expect(container1.find('.row > :eq(1)')).toHaveClass 'column'
expect(container1.find('.row > :eq(1) > :eq(0):contains(B)')).toExist()
expect(container1.find('.row > :eq(1) > :eq(1):contains(C)')).toExist()
expect(container2.find('.row > :eq(0):contains(A)')).toExist()
expect(container2.find('.row > :eq(1)')).toHaveClass 'column'
expect(container2.find('.row > :eq(1) > :eq(0):contains(B)')).toExist()
expect(container2.find('.row > :eq(1) > :eq(1):contains(C)')).toExist()
it "replicates the splitting of panes", ->
container1.attachToDom().width(400).height(200)
container2.attachToDom().width(400).height(200)
pane1d = pane1a.splitRight(new TestView('D'))
expect(container1.find('.row > :eq(1):contains(D)')).toExist()
expect(container2.find('.row > :eq(1):contains(D)')).toExist()
expect(container2.find('.row > :eq(1):contains(D)').outerWidth()).toBe container1.find('.row > :eq(1):contains(D)').outerWidth()
pane1d.splitDown(new TestView('E'))
expect(container1.find('.row > :eq(1)')).toHaveClass('column')
expect(container1.find('.row > :eq(1) > :eq(0):contains(D)')).toExist()
expect(container1.find('.row > :eq(1) > :eq(1):contains(E)')).toExist()
expect(container2.find('.row > :eq(1)')).toHaveClass('column')
expect(container2.find('.row > :eq(1) > :eq(0):contains(D)')).toExist()
expect(container2.find('.row > :eq(1) > :eq(1):contains(E)')).toExist()
it "replicates removal of panes", ->
pane1c.remove()
expect(container1.find('.row > :eq(0):contains(A)')).toExist()
expect(container1.find('.row > :eq(1):contains(B)')).toExist()
expect(container2.find('.row > :eq(0):contains(A)')).toExist()
expect(container2.find('.row > :eq(1):contains(B)')).toExist()
pane1b.remove()
expect(container1.find('> :eq(0):contains(A)')).toExist()
expect(container2.find('> :eq(0):contains(A)')).toExist()
pane1a.remove()
expect(container1.children()).not.toExist()
expect(container2.children()).not.toExist()
# FIXME: We need to get this passing again on master
xit "replicates splitting of panes containing edit sessions", ->
env1.run ->
pane1a.showItem(project.openSync('dir/a'))
pane1a.splitDown()
expect(project.getBuffers().length).toBe 1
expect(container1.find('.row > :eq(0) > :eq(0)').view().activeItem.getRelativePath()).toBe 'dir/a'
expect(container1.find('.row > :eq(0) > :eq(1)').view().activeItem.getRelativePath()).toBe 'dir/a'
env2.run ->
expect(container2.find('.row > :eq(0) > :eq(0)').view().activeItem.getRelativePath()).toBe 'dir/a'
expect(container2.find('.row > :eq(0) > :eq(1)').view().activeItem.getRelativePath()).toBe 'dir/a'

View File

@@ -1,40 +0,0 @@
PaneContainer = require '../src/pane-container'
Pane = require '../src/pane'
{Site} = require 'telepath'
describe "Pane replication", ->
[editSession1a, editSession1b, container1, pane1, doc1] = []
[editSession2a, editSession2b, container2, pane2, doc2] = []
beforeEach ->
editSession1a = project.openSync('sample.js')
editSession1b = project.openSync('sample.txt')
container1 = new PaneContainer
pane1 = new Pane(editSession1a, editSession1b)
container1.setRoot(pane1)
doc1 = container1.getState()
doc2 = doc1.clone(new Site(2))
doc1.connect(doc2)
container2 = deserialize(doc2)
pane2 = container2.getRoot()
it "replicates the initial state of the panes", ->
expect(pane2.items).toEqual(pane1.items)
it "replicates addition and removal of pane items", ->
pane1.addItem(project.openSync('css.css'), 1)
expect(pane2.items).toEqual(pane1.items)
pane1.removeItemAtIndex(2)
expect(pane2.items).toEqual(pane1.items)
it "replicates the movement of pane items", ->
pane1.moveItem(editSession1a, 1)
expect(pane2.items).toEqual(pane1.items)
it "replicates which pane item is active", ->
pane1.showNextItem()
expect(pane2.activeItem).toEqual pane1.activeItem
pane1.showNextItem()
expect(pane2.activeItem).toEqual pane1.activeItem

View File

@@ -1,53 +0,0 @@
path = require 'path'
{Site} = require 'telepath'
Project = require '../src/project'
Git = require '../src/git'
describe "Project replication", ->
[doc1, doc2, project1, project2] = []
beforeEach ->
# pretend that home-1/project and home-2/project map to the same git repository url
spyOn(Git, 'open').andReturn
getOriginUrl: -> 'git://server/project.git'
destroy: ->
refreshIndex: ->
refreshStatus: ->
projectHome1 = path.join(__dirname, 'fixtures', 'replication', 'home-1')
projectHome2 = path.join(__dirname, 'fixtures', 'replication', 'home-2')
config.set('core.projectHome', projectHome1)
project1 = new Project(path.join(projectHome1, 'project'))
project1.bufferForPathSync('file-1.txt')
project1.bufferForPathSync('file-1.txt')
expect(project1.getBuffers().length).toBe 1
doc1 = project1.getState()
doc2 = doc1.clone(new Site(2))
connection = doc1.connect(doc2)
# pretend we're bootstrapping a joining window
config.set('core.projectHome', projectHome2)
project2 = deserialize(doc2)
afterEach ->
project1.destroy()
project2.destroy()
it "replicates the initial path and open buffers of the project", ->
expect(project2.getPath()).not.toBe project1.getPath()
expect(project2.getBuffers().length).toBe 1
expect(project2.getBuffers()[0].getRelativePath()).toBe project1.getBuffers()[0].getRelativePath()
expect(project2.getBuffers()[0].getPath()).not.toBe project1.getBuffers()[0].getPath()
it "replicates insertion and removal of open buffers", ->
project2.bufferForPathSync('file-2.txt')
expect(project1.getBuffers().length).toBe 2
expect(project2.getBuffers()[0].getRelativePath()).toBe project1.getBuffers()[0].getRelativePath()
expect(project2.getBuffers()[1].getRelativePath()).toBe project1.getBuffers()[1].getRelativePath()
expect(project2.getBuffers()[0].getRelativePath()).not.toBe project1.getBuffers()[0].getPath()
expect(project2.getBuffers()[1].getRelativePath()).not.toBe project1.getBuffers()[1].getPath()
project1.removeBuffer(project1.bufferForPathSync('file-2.txt'))
expect(project1.getBuffers().length).toBe 1
expect(project2.getBuffers()[0].getRelativePath()).toBe project1.getBuffers()[0].getRelativePath()

View File

@@ -59,13 +59,12 @@ beforeEach ->
atom.syntax.clearGrammarOverrides()
atom.syntax.clearProperties()
if specPackageName
spy = spyOn(atom.packages, 'resolvePackagePath').andCallFake (packageName) ->
if packageName is specPackageName
resolvePackagePath(specPackagePath)
else
resolvePackagePath(packageName)
resolvePackagePath = _.bind(spy.originalValue, atom.packages)
spy = spyOn(atom.packages, 'resolvePackagePath').andCallFake (packageName) ->
if specPackageName and packageName is specPackageName
resolvePackagePath(specPackagePath)
else
resolvePackagePath(packageName)
resolvePackagePath = _.bind(spy.originalValue, atom.packages)
# used to reset keymap after each spec
bindingSetsToRestore = _.clone(keymap.bindingSets)

View File

@@ -1,46 +0,0 @@
{Site} = require 'telepath'
describe "TextBuffer replication", ->
[buffer1, buffer2] = []
beforeEach ->
buffer1 = project.buildBufferSync('sample.js')
doc1 = buffer1.getState()
doc2 = doc1.clone(new Site(2))
doc1.connect(doc2)
buffer2 = deserialize(doc2, {project})
waitsFor ->
buffer1.loaded and buffer2.loaded
runs ->
buffer1.insert([0, 0], 'changed\n')
afterEach ->
buffer1.destroy()
buffer2.destroy()
it "replicates the initial path and text", ->
expect(buffer2.getPath()).toBe buffer1.getPath()
expect(buffer2.getText()).toBe buffer1.getText()
it "replicates changes to the text and emits 'change' events on all replicas", ->
buffer1.on 'changed', handler1 = jasmine.createSpy("buffer1 change handler")
buffer2.on 'changed', handler2 = jasmine.createSpy("buffer2 change handler")
buffer1.change([[1, 4], [1, 6]], 'h')
expect(buffer1.lineForRow(1)).toBe 'var hicksort = function () {'
expect(buffer2.lineForRow(1)).toBe 'var hicksort = function () {'
expect(buffer1.isModified()).toBeTruthy()
expect(buffer2.isModified()).toBeTruthy()
expectedEvent =
oldRange: [[1, 4], [1, 6]]
oldText: "qu"
newRange: [[1, 4], [1, 5]]
newText: "h"
expect(handler1).toHaveBeenCalledWith(expectedEvent)
expect(handler2).toHaveBeenCalledWith(expectedEvent)
expect(handler1.callCount).toBe 1
expect(handler2.callCount).toBe 1

View File

@@ -936,7 +936,7 @@ describe 'TextBuffer', ->
expect(buffer.isModified()).toBeFalsy()
state = buffer.serialize()
state.get('text').insert([0, 0], 'simulate divergence of on-disk contents from serialized contents')
state.get('text').insertTextAtPoint([0, 0], 'simulate divergence of on-disk contents from serialized contents')
buffer2 = deserialize(state, {project})
@@ -1006,27 +1006,3 @@ describe 'TextBuffer', ->
buffer2 = deserialize(state)
expect(buffer2.getPath()).toBeUndefined()
expect(buffer2.getText()).toBe("abc")
describe "when the buffer has remote markers", ->
[buffer2, buffer3] = []
afterEach ->
buffer2.destroy()
buffer3.destroy()
it "does not include them in the serialized state", ->
doc1 = buffer.getState()
doc2 = doc1.clone(new Site(2))
doc1.connect(doc2)
buffer2 = deserialize(doc2, {project})
buffer.markPosition [1, 0]
buffer2.markPosition [2, 0]
expect(buffer.getMarkerCount()).toBe 2
expect(buffer.getMarkers()[0].isRemote()).toBe false
expect(buffer.getMarkers()[1].isRemote()).toBe true
buffer3 = deserialize(buffer.serialize(), {project})
expect(buffer3.getMarkerCount()).toBe 1
expect(buffer3.getMarkers()[0].isRemote()).toBe false
expect(buffer3.getMarkers()[0].getRange()).toEqual [[1, 0], [1, 0]]

View File

@@ -19,6 +19,7 @@ app = remote.require 'app'
{Document} = require 'telepath'
DeserializerManager = require './deserializer-manager'
{Subscriber} = require 'emissary'
SiteShim = require './site-shim'
# Public: Atom global for dealing with packages, themes, menus, and the window.
#
@@ -279,15 +280,19 @@ class Atom
catch error
console.warn "Error parsing window state: #{windowStatePath}", error.stack, error
doc = Document.deserialize(state: documentState) if documentState?
doc = Document.deserialize(documentState) if documentState?
doc ?= Document.create()
@site = doc.site # TODO: Remove this when everything is using telepath models
# TODO: Remove this when everything is using telepath models
if @site?
@site.setRootDocument(doc)
else
@site = new SiteShim(doc)
doc
saveWindowState: ->
windowState = @getWindowState()
if windowStatePath = @getWindowStatePath()
windowState.saveSync(path: windowStatePath)
windowState.saveSync(windowStatePath)
else
@getCurrentWindow().loadSettings.windowState = JSON.stringify(windowState.serialize())

View File

@@ -50,11 +50,10 @@ class DisplayBuffer
@subscribe @buffer, 'markers-updated', @handleBufferMarkersUpdated
@subscribe @buffer, 'marker-created', @handleBufferMarkerCreated
@subscribe @state, 'changed', ({key, newValue}) =>
switch key
when 'softWrap'
@emit 'soft-wrap-changed', newValue
@updateWrappedScreenLines()
@subscribe @state, 'changed', ({newValues}) =>
if newValues.softWrap?
@emit 'soft-wrap-changed', newValues.softWrap
@updateWrappedScreenLines()
@observeConfig 'editor.preferredLineLength', callNow: false, =>
@updateWrappedScreenLines() if @getSoftWrap() and config.get('editor.softWrapAtPreferredLineLength')

View File

@@ -100,12 +100,13 @@ class EditSession
@addCursorAtBufferPosition(position)
@languageMode = new LanguageMode(this, @buffer.getExtension())
@subscribe @state, 'changed', ({key, newValue}) =>
switch key
when 'scrollTop'
@emit 'scroll-top-changed', newValue
when 'scrollLeft'
@emit 'scroll-left-changed', newValue
@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
project.addEditSession(this) if registerEditSession
@@ -138,8 +139,8 @@ class EditSession
return if @destroyed
@destroyed = true
@unsubscribe()
@buffer.release()
selection.destroy() for selection in @getSelections()
@buffer.release()
@displayBuffer.destroy()
@languageMode.destroy()
project?.removeEditSession(this)
@@ -1432,7 +1433,7 @@ class EditSession
# Private:
getSelectionMarkerAttributes: ->
type: 'selection', editSessionId: @id, invalidation: 'never'
type: 'selection', editSessionId: @id, invalidate: 'never'
# Private:
getDebugSnapshot: ->

View File

@@ -17,16 +17,17 @@ class PaneAxis extends View
@state = site.createDocument(deserializer: @className(), children: [])
@addChild(child) for child in args
@state.get('children').on 'changed', ({index, inserted, removed, site}) =>
return if site is @state.site.id
for childState in removed
@state.get('children').on 'changed', ({index, insertedValues, removedValues, siteId}) =>
return if siteId is @state.siteId
for childState in removedValues
@removeChild(@children(":eq(#{index})").view(), updateState: false)
for childState, i in inserted
for childState, i in insertedValues
@addChild(deserialize(childState), index + i, updateState: false)
addChild: (child, index=@children().length, options={}) ->
@insertAt(index, child)
@state.get('children').insert(index, child.getState()) if options.updateState ? true
state = child.getState()
@state.get('children').insert(index, state) if options.updateState ? true
@getContainer()?.adjustPaneDimensions()
removeChild: (child, options={}) ->

View File

@@ -27,11 +27,11 @@ class PaneContainer extends View
else
@state = site.createDocument(deserializer: 'PaneContainer')
@subscribe @state, 'changed', ({key, newValue, site}) =>
return if site is @state.site.id
if key is 'root'
if newValue?
@setRoot(deserialize(newValue))
@subscribe @state, 'changed', ({newValues, siteId}) =>
return if siteId is @state.siteId
if newValues.hasOwnProperty('root')
if rootState = newValues.root
@setRoot(deserialize(rootState))
else
@setRoot(null)

View File

@@ -40,16 +40,17 @@ class Pane extends View
deserializer: 'Pane'
items: @items.map (item) -> item.getState?() ? item.serialize()
@subscribe @state.get('items'), 'changed', ({index, removed, inserted, site}) =>
return if site is @state.site.id
for itemState in removed
@subscribe @state.get('items'), 'changed', ({index, removedValues, insertedValues, siteId}) =>
return if siteId is @state.siteId
for itemState in removedValues
@removeItemAtIndex(index, updateState: false)
for itemState, i in inserted
for itemState, i in insertedValues
@addItem(deserialize(itemState), index + i, updateState: false)
@subscribe @state, 'changed', ({key, newValue, site}) =>
return if site is @state.site.id
@showItemForUri(newValue) if key is 'activeItemUri'
@subscribe @state, 'changed', ({newValues, siteId}) =>
return if site is @state.siteId
if newValues.activeItemUri
@showItemForUri(newValues.activeItemUri)
@viewsByItem = new WeakMap()
activeItemUri = @state.get('activeItemUri')
@@ -198,8 +199,8 @@ class Pane extends View
container = @getContainer()
if @promptToSaveItem(item)
@removeItem(item)
container.itemDestroyed(item)
@removeItem(item)
item.destroy?()
true
else
@@ -262,9 +263,9 @@ class Pane extends View
@saveItem(item) for item in @getItems()
# Public:
removeItem: (item) ->
removeItem: (item, options) ->
index = @items.indexOf(item)
@removeItemAtIndex(index) if index >= 0
@removeItemAtIndex(index, options) if index >= 0
# Public: Just remove the item at the given index.
removeItemAtIndex: (index, options={}) ->
@@ -281,16 +282,15 @@ class Pane extends View
oldIndex = @items.indexOf(item)
@items.splice(oldIndex, 1)
@items.splice(newIndex, 0, item)
@state.get('items').splice(oldIndex, 1)
@state.get('items').splice(newIndex, 0, item.getState?() ? item.serialize())
@state.get('items').insert(newIndex, item.getState?() ? item.serialize())
@trigger 'pane:item-moved', [item, newIndex]
# Public: Moves the given item to another pane.
moveItemToPane: (item, pane, index) ->
@isMovingItem = true
@removeItem(item)
@isMovingItem = false
pane.addItem(item, index)
@removeItem(item, updateState: false)
@isMovingItem = false
# Public: Finds the first item that matches the given uri.
itemForUri: (uri) ->
@@ -387,12 +387,11 @@ class Pane extends View
axis = @buildPaneAxis(axis)
if parent instanceof PaneContainer
@detach()
axis.addChild(this)
parent.setRoot(axis)
else
parent.insertChildBefore(this, axis)
parent.detachChild(this)
axis.addChild(this)
axis.addChild(this)
parent = axis
newPane = new Pane(items...)

View File

@@ -80,12 +80,12 @@ class Project
@state = site.createDocument(deserializer: @constructor.name, version: @constructor.version, buffers: [])
@setPath(pathOrState)
@state.get('buffers').on 'changed', ({inserted, removed, index, site}) =>
return if site is @state.site.id
@state.get('buffers').on 'changed', ({index, insertedValues, removedValues, siteId}) =>
return if siteId is @state.siteId
for removedBuffer in removed
for removedBuffer in removedValues
@removeBufferAtIndex(index, updateState: false)
for insertedBuffer, i in inserted
for insertedBuffer, i in insertedValues
@addBufferAtIndex(deserialize(insertedBuffer, project: this), index + i, updateState: false)
# Private:
@@ -292,7 +292,7 @@ class Project
# Private:
removeBufferAtIndex: (index, options={}) ->
[buffer] = @buffers.splice(index, 1)
@state.get('buffers').remove(index) if options.updateState ? true
@state.get('buffers')?.remove(index) if options.updateState ? true
buffer?.destroy()
# Public: Performs a search across all the files in the project.

11
src/site-shim.coffee Normal file
View File

@@ -0,0 +1,11 @@
module.exports =
class SiteShim
constructor: (document) ->
@setRootDocument(document)
setRootDocument: (@document) ->
@id = @document.siteId
@document.set('looseDocuments', [])
createDocument: (values) ->
@document.get('looseDocuments').push(values)

View File

@@ -51,7 +51,7 @@ class TextBuffer
@useSerializedText = @state.get('isModified') != false
else
{@project, filePath} = optionsOrState
@text = site.createDocument(initialText ? '', shareStrings: true)
@text = new telepath.MutableString(initialText ? '')
@id = guid.create().toString()
@state = site.createDocument
id: @id
@@ -654,7 +654,7 @@ class TextBuffer
change: (oldRange, newText, options={}) ->
oldRange = @clipRange(oldRange)
newText = @normalizeLineEndings(oldRange.start.row, newText) if options.normalizeLineEndings ? true
@text.change(oldRange, newText, options)
@text.setTextInRange(oldRange, newText, options)
normalizeLineEndings: (startRow, text) ->
if lineEnding = @suggestedLineEndingForRow(startRow)