Merge pull request #17021 from atom/aw/d-d-d-double-open

Synchronously track URIs in the process of opening
This commit is contained in:
Ash Wilson 2018-03-26 14:08:15 -04:00 committed by GitHub
commit d4185b2b01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 165 additions and 109 deletions

View File

@ -274,6 +274,21 @@ describe('Workspace', () => {
})
})
it('discovers existing editors that are still opening', () => {
let editor0 = null
let editor1 = null
waitsForPromise(() => Promise.all([
workspace.open('spartacus.txt').then(o0 => { editor0 = o0 }),
workspace.open('spartacus.txt').then(o1 => { editor1 = o1 }),
]))
runs(() => {
expect(editor0).toEqual(editor1)
expect(workspace.getActivePane().items).toEqual([editor0])
})
})
it("uses the location specified by the model's `getDefaultLocation()` method", () => {
const item = {
getDefaultLocation: jasmine.createSpy().andReturn('right'),
@ -361,6 +376,28 @@ describe('Workspace', () => {
})
})
it('discovers existing editors that are still opening in an inactive pane', () => {
let editor0 = null
let editor1 = null
const pane0 = workspace.getActivePane()
const pane1 = workspace.getActivePane().splitRight()
pane0.activate()
const promise0 = workspace.open('spartacus.txt', {searchAllPanes: true}).then(o0 => { editor0 = o0 })
pane1.activate()
const promise1 = workspace.open('spartacus.txt', {searchAllPanes: true}).then(o1 => { editor1 = o1 })
waitsForPromise(() => Promise.all([promise0, promise1]))
runs(() => {
expect(editor0).toBeDefined()
expect(editor1).toBeDefined()
expect(editor0).toEqual(editor1)
expect(workspace.getActivePane().items).toEqual([editor0])
})
})
it('activates the pane in the dock with the matching item', () => {
const dock = atom.workspace.getRightDock()
const ITEM_URI = 'atom://test'

View File

@ -225,6 +225,8 @@ module.exports = class Workspace extends Model {
modal: new PanelContainer({viewRegistry: this.viewRegistry, location: 'modal'})
}
this.incoming = new Map()
this.subscribeToEvents()
}
@ -921,133 +923,150 @@ module.exports = class Workspace extends Model {
if (typeof item.getURI === 'function') uri = item.getURI()
}
if (!atom.config.get('core.allowPendingPaneItems')) {
options.pending = false
}
// Avoid adding URLs as recent documents to work-around this Spotlight crash:
// https://github.com/atom/atom/issues/10071
if (uri && (!url.parse(uri).protocol || process.platform === 'win32')) {
this.applicationDelegate.addRecentDocument(uri)
}
let pane, itemExistsInWorkspace
// Try to find an existing item in the workspace.
if (item || uri) {
if (options.pane) {
pane = options.pane
} else if (options.searchAllPanes) {
pane = item ? this.paneForItem(item) : this.paneForURI(uri)
let resolveItem = () => {}
if (uri) {
const incomingItem = this.incoming.get(uri)
if (!incomingItem) {
this.incoming.set(uri, new Promise(resolve => { resolveItem = resolve }))
} else {
// If an item with the given URI is already in the workspace, assume
// that item's pane container is the preferred location for that URI.
let container
if (uri) container = this.paneContainerForURI(uri)
if (!container) container = this.getActivePaneContainer()
await incomingItem
}
}
// The `split` option affects where we search for the item.
pane = container.getActivePane()
switch (options.split) {
case 'left':
pane = pane.findLeftmostSibling()
break
case 'right':
pane = pane.findRightmostSibling()
break
case 'up':
pane = pane.findTopmostSibling()
break
case 'down':
pane = pane.findBottommostSibling()
break
}
try {
if (!atom.config.get('core.allowPendingPaneItems')) {
options.pending = false
}
if (pane) {
if (item) {
itemExistsInWorkspace = pane.getItems().includes(item)
// Avoid adding URLs as recent documents to work-around this Spotlight crash:
// https://github.com/atom/atom/issues/10071
if (uri && (!url.parse(uri).protocol || process.platform === 'win32')) {
this.applicationDelegate.addRecentDocument(uri)
}
let pane, itemExistsInWorkspace
// Try to find an existing item in the workspace.
if (item || uri) {
if (options.pane) {
pane = options.pane
} else if (options.searchAllPanes) {
pane = item ? this.paneForItem(item) : this.paneForURI(uri)
} else {
item = pane.itemForURI(uri)
itemExistsInWorkspace = item != null
// If an item with the given URI is already in the workspace, assume
// that item's pane container is the preferred location for that URI.
let container
if (uri) container = this.paneContainerForURI(uri)
if (!container) container = this.getActivePaneContainer()
// The `split` option affects where we search for the item.
pane = container.getActivePane()
switch (options.split) {
case 'left':
pane = pane.findLeftmostSibling()
break
case 'right':
pane = pane.findRightmostSibling()
break
case 'up':
pane = pane.findTopmostSibling()
break
case 'down':
pane = pane.findBottommostSibling()
break
}
}
if (pane) {
if (item) {
itemExistsInWorkspace = pane.getItems().includes(item)
} else {
item = pane.itemForURI(uri)
itemExistsInWorkspace = item != null
}
}
}
}
// If we already have an item at this stage, we won't need to do an async
// lookup of the URI, so we yield the event loop to ensure this method
// is consistently asynchronous.
if (item) await Promise.resolve()
// If we already have an item at this stage, we won't need to do an async
// lookup of the URI, so we yield the event loop to ensure this method
// is consistently asynchronous.
if (item) await Promise.resolve()
if (!itemExistsInWorkspace) {
item = item || await this.createItemForURI(uri, options)
if (!item) return
if (!itemExistsInWorkspace) {
item = item || await this.createItemForURI(uri, options)
if (!item) return
if (options.pane) {
pane = options.pane
if (options.pane) {
pane = options.pane
} else {
let location = options.location
if (!location && !options.split && uri && this.enablePersistence) {
location = await this.itemLocationStore.load(uri)
}
if (!location && typeof item.getDefaultLocation === 'function') {
location = item.getDefaultLocation()
}
const allowedLocations = typeof item.getAllowedLocations === 'function' ? item.getAllowedLocations() : ALL_LOCATIONS
location = allowedLocations.includes(location) ? location : allowedLocations[0]
const container = this.paneContainers[location] || this.getCenter()
pane = container.getActivePane()
switch (options.split) {
case 'left':
pane = pane.findLeftmostSibling()
break
case 'right':
pane = pane.findOrCreateRightmostSibling()
break
case 'up':
pane = pane.findTopmostSibling()
break
case 'down':
pane = pane.findOrCreateBottommostSibling()
break
}
}
}
if (!options.pending && (pane.getPendingItem() === item)) {
pane.clearPendingItem()
}
this.itemOpened(item)
if (options.activateItem === false) {
pane.addItem(item, {pending: options.pending})
} else {
let location = options.location
if (!location && !options.split && uri && this.enablePersistence) {
location = await this.itemLocationStore.load(uri)
}
if (!location && typeof item.getDefaultLocation === 'function') {
location = item.getDefaultLocation()
}
pane.activateItem(item, {pending: options.pending})
}
const allowedLocations = typeof item.getAllowedLocations === 'function' ? item.getAllowedLocations() : ALL_LOCATIONS
location = allowedLocations.includes(location) ? location : allowedLocations[0]
if (options.activatePane !== false) {
pane.activate()
}
const container = this.paneContainers[location] || this.getCenter()
pane = container.getActivePane()
switch (options.split) {
case 'left':
pane = pane.findLeftmostSibling()
break
case 'right':
pane = pane.findOrCreateRightmostSibling()
break
case 'up':
pane = pane.findTopmostSibling()
break
case 'down':
pane = pane.findOrCreateBottommostSibling()
break
let initialColumn = 0
let initialLine = 0
if (!Number.isNaN(options.initialLine)) {
initialLine = options.initialLine
}
if (!Number.isNaN(options.initialColumn)) {
initialColumn = options.initialColumn
}
if (initialLine >= 0 || initialColumn >= 0) {
if (typeof item.setCursorBufferPosition === 'function') {
item.setCursorBufferPosition([initialLine, initialColumn])
}
}
}
if (!options.pending && (pane.getPendingItem() === item)) {
pane.clearPendingItem()
}
this.itemOpened(item)
if (options.activateItem === false) {
pane.addItem(item, {pending: options.pending})
} else {
pane.activateItem(item, {pending: options.pending})
}
if (options.activatePane !== false) {
pane.activate()
}
let initialColumn = 0
let initialLine = 0
if (!Number.isNaN(options.initialLine)) {
initialLine = options.initialLine
}
if (!Number.isNaN(options.initialColumn)) {
initialColumn = options.initialColumn
}
if (initialLine >= 0 || initialColumn >= 0) {
if (typeof item.setCursorBufferPosition === 'function') {
item.setCursorBufferPosition([initialLine, initialColumn])
const index = pane.getActiveItemIndex()
this.emitter.emit('did-open', {uri, pane, item, index})
if (uri) {
this.incoming.delete(uri)
}
} finally {
resolveItem()
}
const index = pane.getActiveItemIndex()
this.emitter.emit('did-open', {uri, pane, item, index})
return item
}