Merge pull request #16229 from atom/wl-async-confirm

Make atom.confirm async
This commit is contained in:
Wliu 2018-01-09 17:19:51 -05:00 committed by GitHub
commit cd123456ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 260 additions and 198 deletions

View File

@ -1,4 +1,4 @@
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
const {it, fit, ffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers')
const _ = require('underscore-plus')
const path = require('path')
const temp = require('temp').track()
@ -518,27 +518,31 @@ describe('AtomEnvironment', () => {
})
})
it('prompts the user to restore the state in a new window, discarding it and adding folder to current window', () => {
spyOn(atom, 'confirm').andReturn(1)
it('prompts the user to restore the state in a new window, discarding it and adding folder to current window', async () => {
jasmine.useRealClock()
spyOn(atom, 'confirm').andCallFake((options, callback) => callback(1))
spyOn(atom.project, 'addPath')
spyOn(atom.workspace, 'open')
const state = Symbol()
atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename])
expect(atom.confirm).toHaveBeenCalled()
expect(atom.project.addPath.callCount).toBe(1)
await conditionPromise(() => atom.project.addPath.callCount === 1)
expect(atom.project.addPath).toHaveBeenCalledWith(__dirname)
expect(atom.workspace.open.callCount).toBe(1)
expect(atom.workspace.open).toHaveBeenCalledWith(__filename)
})
it('prompts the user to restore the state in a new window, opening a new window', () => {
spyOn(atom, 'confirm').andReturn(0)
it('prompts the user to restore the state in a new window, opening a new window', async () => {
jasmine.useRealClock()
spyOn(atom, 'confirm').andCallFake((options, callback) => callback(0))
spyOn(atom, 'open')
const state = Symbol()
atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename])
expect(atom.confirm).toHaveBeenCalled()
await conditionPromise(() => atom.open.callCount === 1)
expect(atom.open).toHaveBeenCalledWith({
pathsToOpen: [__dirname, __filename],
newWindow: true,

View File

@ -35,9 +35,9 @@ describe('CommandInstaller on #darwin', () => {
installer.installShellCommandsInteractively()
expect(appDelegate.confirm).toHaveBeenCalledWith({
expect(appDelegate.confirm.mostRecentCall.args[0]).toEqual({
message: 'Failed to install shell commands',
detailedMessage: 'an error'
detail: 'an error'
})
appDelegate.confirm.reset()
@ -46,9 +46,9 @@ describe('CommandInstaller on #darwin', () => {
installer.installShellCommandsInteractively()
expect(appDelegate.confirm).toHaveBeenCalledWith({
expect(appDelegate.confirm.mostRecentCall.args[0]).toEqual({
message: 'Failed to install shell commands',
detailedMessage: 'another error'
detail: 'another error'
})
})
@ -61,9 +61,9 @@ describe('CommandInstaller on #darwin', () => {
installer.installShellCommandsInteractively()
expect(appDelegate.confirm).toHaveBeenCalledWith({
expect(appDelegate.confirm.mostRecentCall.args[0]).toEqual({
message: 'Commands installed.',
detailedMessage: 'The shell commands `atom` and `apm` are installed.'
detail: 'The shell commands `atom` and `apm` are installed.'
})
})

View File

@ -621,14 +621,14 @@ describe('AtomApplication', function () {
})
// Choosing "Cancel"
mockElectronShowMessageBox({choice: 1})
mockElectronShowMessageBox({response: 1})
electron.app.quit()
await atomApplication.lastBeforeQuitPromise
assert(!electron.app.didQuit())
assert.equal(electron.app.quit.callCount, 1) // Ensure choosing "Cancel" doesn't try to quit the electron app more than once (regression)
// Choosing "Don't save"
mockElectronShowMessageBox({choice: 2})
mockElectronShowMessageBox({response: 2})
electron.app.quit()
await atomApplication.lastBeforeQuitPromise
assert(electron.app.didQuit())
@ -664,9 +664,9 @@ describe('AtomApplication', function () {
electron.app.didQuit = () => didQuit
}
function mockElectronShowMessageBox ({choice}) {
electron.dialog.showMessageBox = () => {
return choice
function mockElectronShowMessageBox ({response}) {
electron.dialog.showMessageBox = (window, options, callback) => {
callback(response)
}
}

View File

@ -5,7 +5,7 @@ describe('PaneContainer', () => {
let confirm, params
beforeEach(() => {
confirm = spyOn(atom.applicationDelegate, 'confirm').andReturn(0)
confirm = spyOn(atom.applicationDelegate, 'confirm').andCallFake((options, callback) => callback(0))
params = {
location: 'center',
config: atom.config,
@ -280,14 +280,14 @@ describe('PaneContainer', () => {
})
it('returns true if the user saves all modified files when prompted', async () => {
confirm.andReturn(0)
confirm.andCallFake((options, callback) => callback(0))
const saved = await container.confirmClose()
expect(confirm).toHaveBeenCalled()
expect(saved).toBeTruthy()
})
it('returns false if the user cancels saving any modified file', async () => {
confirm.andReturn(1)
confirm.andCallFake((options, callback) => callback(1))
const saved = await container.confirmClose()
expect(confirm).toHaveBeenCalled()
expect(saved).toBeFalsy()

View File

@ -564,7 +564,7 @@ describe('Pane', () => {
describe('when the item has a uri', () => {
it('saves the item before destroying it', async () => {
itemURI = 'test'
confirm.andReturn(0)
confirm.andCallFake((options, callback) => callback(0))
const success = await pane.destroyItem(item1)
expect(item1.save).toHaveBeenCalled()
@ -579,7 +579,7 @@ describe('Pane', () => {
itemURI = null
showSaveDialog.andReturn('/selected/path')
confirm.andReturn(0)
confirm.andCallFake((options, callback) => callback(0))
const success = await pane.destroyItem(item1)
expect(showSaveDialog).toHaveBeenCalledWith({})
@ -593,7 +593,7 @@ describe('Pane', () => {
describe("if the [Don't Save] option is selected", () => {
it('removes and destroys the item without saving it', async () => {
confirm.andReturn(2)
confirm.andCallFake((options, callback) => callback(2))
const success = await pane.destroyItem(item1)
expect(item1.save).not.toHaveBeenCalled()
@ -605,7 +605,7 @@ describe('Pane', () => {
describe('if the [Cancel] option is selected', () => {
it('does not save, remove, or destroy the item', async () => {
confirm.andReturn(1)
confirm.andCallFake((options, callback) => callback(1))
const success = await pane.destroyItem(item1)
expect(item1.save).not.toHaveBeenCalled()
@ -1210,7 +1210,7 @@ describe('Pane', () => {
item1.getURI = () => '/test/path'
item1.save = jasmine.createSpy('save')
confirm.andReturn(0)
confirm.andCallFake((options, callback) => callback(0))
await pane.close()
expect(confirm).toHaveBeenCalled()
expect(item1.save).toHaveBeenCalled()
@ -1225,7 +1225,7 @@ describe('Pane', () => {
item1.getURI = () => '/test/path'
item1.save = jasmine.createSpy('save')
confirm.andReturn(1)
confirm.andCallFake((options, callback) => callback(1))
await pane.close()
expect(confirm).toHaveBeenCalled()
@ -1240,7 +1240,7 @@ describe('Pane', () => {
item1.shouldPromptToSave = () => true
item1.saveAs = jasmine.createSpy('saveAs')
confirm.andReturn(0)
confirm.andCallFake((options, callback) => callback(0))
showSaveDialog.andReturn(undefined)
await pane.close()
@ -1270,12 +1270,12 @@ describe('Pane', () => {
it('does not destroy the pane if save fails and user clicks cancel', async () => {
let confirmations = 0
confirm.andCallFake(() => {
confirm.andCallFake((options, callback) => {
confirmations++
if (confirmations === 1) {
return 0 // click save
callback(0) // click save
} else {
return 1
callback(1)
}
}) // click cancel
@ -1290,9 +1290,9 @@ describe('Pane', () => {
item1.saveAs = jasmine.createSpy('saveAs').andReturn(true)
let confirmations = 0
confirm.andCallFake(() => {
confirm.andCallFake((options, callback) => {
confirmations++
return 0
callback(0)
}) // save and then save as
showSaveDialog.andReturn('new/path')
@ -1315,13 +1315,14 @@ describe('Pane', () => {
})
let confirmations = 0
confirm.andCallFake(() => {
confirm.andCallFake((options, callback) => {
confirmations++
if (confirmations < 3) {
return 0 // save, save as, save as
callback(0) // save, save as, save as
} else {
callback(2) // don't save
}
return 2
}) // don't save
})
showSaveDialog.andReturn('new/path')

View File

@ -10,7 +10,7 @@ const _ = require('underscore-plus')
const fstream = require('fstream')
const fs = require('fs-plus')
const AtomEnvironment = require('../src/atom-environment')
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers')
describe('Workspace', () => {
let workspace
@ -659,47 +659,42 @@ describe('Workspace', () => {
})
})
describe('when the file is over user-defined limit', () => {
const shouldPromptForFileOfSize = (size, shouldPrompt) => {
describe('when the file size is over the limit defined in `core.warnOnLargeFileLimit`', () => {
const shouldPromptForFileOfSize = async (size, shouldPrompt) => {
spyOn(fs, 'getSizeSync').andReturn(size * 1048577)
atom.applicationDelegate.confirm.andCallFake(() => selectedButtonIndex)
atom.applicationDelegate.confirm()
var selectedButtonIndex = 1 // cancel
let editor = null
waitsForPromise(() => workspace.open('sample.js').then(e => { editor = e }))
let selectedButtonIndex = 1 // cancel
atom.applicationDelegate.confirm.andCallFake((options, callback) => callback(selectedButtonIndex))
let editor = await workspace.open('sample.js')
if (shouldPrompt) {
runs(() => {
expect(editor).toBeUndefined()
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
expect(editor).toBeUndefined()
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
atom.applicationDelegate.confirm.reset()
selectedButtonIndex = 0
}) // open the file
atom.applicationDelegate.confirm.reset()
selectedButtonIndex = 0 // open the file
waitsForPromise(() => workspace.open('sample.js').then(e => { editor = e }))
editor = await workspace.open('sample.js')
runs(() => {
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
})
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
} else {
runs(() => expect(editor).not.toBeUndefined())
expect(editor).not.toBeUndefined()
}
}
it('prompts the user to make sure they want to open a file this big', () => {
it('prompts before opening the file', async () => {
atom.config.set('core.warnOnLargeFileLimit', 20)
shouldPromptForFileOfSize(20, true)
await shouldPromptForFileOfSize(20, true)
})
it("doesn't prompt on files below the limit", () => {
it("doesn't prompt on files below the limit", async () => {
atom.config.set('core.warnOnLargeFileLimit', 30)
shouldPromptForFileOfSize(20, false)
await shouldPromptForFileOfSize(20, false)
})
it('prompts for smaller files with a lower limit', () => {
it('prompts for smaller files with a lower limit', async () => {
atom.config.set('core.warnOnLargeFileLimit', 5)
shouldPromptForFileOfSize(10, true)
await shouldPromptForFileOfSize(10, true)
})
})
@ -2809,29 +2804,30 @@ describe('Workspace', () => {
describe('.checkoutHeadRevision()', () => {
let editor = null
beforeEach(() => {
beforeEach(async () => {
jasmine.useRealClock()
atom.config.set('editor.confirmCheckoutHeadRevision', false)
waitsForPromise(() => atom.workspace.open('sample-with-comments.js').then(o => { editor = o }))
editor = await atom.workspace.open('sample-with-comments.js')
})
it('reverts to the version of its file checked into the project repository', () => {
it('reverts to the version of its file checked into the project repository', async () => {
editor.setCursorBufferPosition([0, 0])
editor.insertText('---\n')
expect(editor.lineTextForBufferRow(0)).toBe('---')
waitsForPromise(() => atom.workspace.checkoutHeadRevision(editor))
atom.workspace.checkoutHeadRevision(editor)
runs(() => expect(editor.lineTextForBufferRow(0)).toBe(''))
await conditionPromise(() => editor.lineTextForBufferRow(0) === '')
})
describe("when there's no repository for the editor's file", () => {
it("doesn't do anything", () => {
it("doesn't do anything", async () => {
editor = new TextEditor()
editor.setText('stuff')
atom.workspace.checkoutHeadRevision(editor)
waitsForPromise(() => atom.workspace.checkoutHeadRevision(editor))
atom.workspace.checkoutHeadRevision(editor)
})
})
})

View File

@ -175,28 +175,38 @@ class ApplicationDelegate {
return remote.systemPreferences.getUserDefault(key, type)
}
confirm ({message, detailedMessage, buttons}) {
let buttonLabels
if (!buttons) buttons = {}
if (Array.isArray(buttons)) {
buttonLabels = buttons
confirm (options, callback) {
if (typeof callback === 'function') {
// Async version: pass options directly to Electron but set sane defaults
options = Object.assign({type: 'info', normalizeAccessKeys: true}, options)
remote.dialog.showMessageBox(remote.getCurrentWindow(), options, callback)
} else {
buttonLabels = Object.keys(buttons)
}
// Legacy sync version: options can only have `message`,
// `detailedMessage` (optional), and buttons array or object (optional)
let {message, detailedMessage, buttons} = options
const chosen = remote.dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'info',
message,
detail: detailedMessage,
buttons: buttonLabels,
normalizeAccessKeys: true
})
let buttonLabels
if (!buttons) buttons = {}
if (Array.isArray(buttons)) {
buttonLabels = buttons
} else {
buttonLabels = Object.keys(buttons)
}
if (Array.isArray(buttons)) {
return chosen
} else {
const callback = buttons[buttonLabels[chosen]]
return (typeof callback === 'function' ? callback() : undefined)
const chosen = remote.dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'info',
message,
detail: detailedMessage,
buttons: buttonLabels,
normalizeAccessKeys: true
})
if (Array.isArray(buttons)) {
return chosen
} else {
const callback = buttons[buttonLabels[chosen]]
if (typeof callback === 'function') callback()
}
}
}

View File

@ -965,29 +965,63 @@ class AtomEnvironment {
// Essential: A flexible way to open a dialog akin to an alert dialog.
//
// While both async and sync versions are provided, it is recommended to use the async version
// such that the renderer process is not blocked while the dialog box is open.
//
// The async version accepts the same options as Electron's `dialog.showMessageBox`.
// For convenience, it sets `type` to `'info'` and `normalizeAccessKeys` to `true` by default.
//
// If the dialog is closed (via `Esc` key or `X` in the top corner) without selecting a button
// the first button will be clicked unless a "Cancel" or "No" button is provided.
//
// ## Examples
//
// ```coffee
// atom.confirm
// message: 'How you feeling?'
// detailedMessage: 'Be honest.'
// buttons:
// Good: -> window.alert('good to hear')
// Bad: -> window.alert('bummer')
// ```js
// // Async version (recommended)
// atom.confirm({
// message: 'How you feeling?',
// detail: 'Be honest.',
// buttons: ['Good', 'Bad']
// }, response => {
// if (response === 0) {
// window.alert('good to hear')
// } else {
// window.alert('bummer')
// }
// })
//
// ```js
// // Legacy sync version
// const chosen = atom.confirm({
// message: 'How you feeling?',
// detailedMessage: 'Be honest.',
// buttons: {
// Good: () => window.alert('good to hear'),
// Bad: () => window.alert('bummer')
// }
// })
// ```
//
// * `options` An {Object} with the following keys:
// * `options` An options {Object}. If the callback argument is also supplied, see the documentation at
// https://electronjs.org/docs/api/dialog#dialogshowmessageboxbrowserwindow-options-callback for the list of
// available options. Otherwise, only the following keys are accepted:
// * `message` The {String} message to display.
// * `detailedMessage` (optional) The {String} detailed message to display.
// * `buttons` (optional) Either an array of strings or an object where keys are
// button names and the values are callbacks to invoke when clicked.
// * `buttons` (optional) Either an {Array} of {String}s or an {Object} where keys are
// button names and the values are callback {Function}s to invoke when clicked.
// * `callback` (optional) A {Function} that will be called with the index of the chosen option.
// If a callback is supplied, the dialog will be non-blocking. This argument is recommended.
//
// Returns the chosen button index {Number} if the buttons option is an array or the return value of the callback if the buttons option is an object.
confirm (params = {}) {
return this.applicationDelegate.confirm(params)
// Returns the chosen button index {Number} if the buttons option is an array
// or the return value of the callback if the buttons option is an object.
// If a callback function is supplied, returns `undefined`.
confirm (options = {}, callback) {
if (callback) {
// Async: no return value
this.applicationDelegate.confirm(options, callback)
} else {
return this.applicationDelegate.confirm(options)
}
}
/*
@ -1071,7 +1105,7 @@ class AtomEnvironment {
}
}
attemptRestoreProjectStateForPaths (state, projectPaths, filesToOpen = []) {
async attemptRestoreProjectStateForPaths (state, projectPaths, filesToOpen = []) {
const center = this.workspace.getCenter()
const windowIsUnused = () => {
for (let container of this.workspace.getPaneContainers()) {
@ -1090,30 +1124,38 @@ class AtomEnvironment {
this.restoreStateIntoThisEnvironment(state)
return Promise.all(filesToOpen.map(file => this.workspace.open(file)))
} else {
let resolveDiscardStatePromise = null
const discardStatePromise = new Promise((resolve) => {
resolveDiscardStatePromise = resolve
})
const nouns = projectPaths.length === 1 ? 'folder' : 'folders'
const choice = this.confirm({
this.confirm({
message: 'Previous automatically-saved project state detected',
detailedMessage: `There is previously saved state for the selected ${nouns}. ` +
detail: `There is previously saved state for the selected ${nouns}. ` +
`Would you like to add the ${nouns} to this window, permanently discarding the saved state, ` +
`or open the ${nouns} in a new window, restoring the saved state?`,
buttons: [
'&Open in new window and recover state',
'&Add to this window and discard state'
]})
if (choice === 0) {
this.open({
pathsToOpen: projectPaths.concat(filesToOpen),
newWindow: true,
devMode: this.inDevMode(),
safeMode: this.inSafeMode()
})
return Promise.resolve(null)
} else if (choice === 1) {
for (let selectedPath of projectPaths) {
this.project.addPath(selectedPath)
]
}, response => {
if (response === 0) {
this.open({
pathsToOpen: projectPaths.concat(filesToOpen),
newWindow: true,
devMode: this.inDevMode(),
safeMode: this.inSafeMode()
})
resolveDiscardStatePromise(Promise.resolve(null))
} else if (response === 1) {
for (let selectedPath of projectPaths) {
this.project.addPath(selectedPath)
}
resolveDiscardStatePromise(Promise.all(filesToOpen.map(file => this.workspace.open(file))))
}
return Promise.all(filesToOpen.map(file => this.workspace.open(file)))
}
})
return discardStatePromise
}
}

View File

@ -23,8 +23,8 @@ class CommandInstaller {
const showErrorDialog = (error) => {
this.applicationDelegate.confirm({
message: 'Failed to install shell commands',
detailedMessage: error.message
})
detail: error.message
}, () => {})
}
this.installAtomCommand(true, error => {
@ -33,8 +33,8 @@ class CommandInstaller {
if (error) return showErrorDialog(error)
this.applicationDelegate.confirm({
message: 'Commands installed.',
detailedMessage: 'The shell commands `atom` and `apm` are installed.'
})
detail: 'The shell commands `atom` and `apm` are installed.'
}, () => {})
})
})
}

View File

@ -790,57 +790,53 @@ class Pane {
}
promptToSaveItem (item, options = {}) {
if (typeof item.shouldPromptToSave !== 'function' || !item.shouldPromptToSave(options)) {
return Promise.resolve(true)
}
let uri
if (typeof item.getURI === 'function') {
uri = item.getURI()
} else if (typeof item.getUri === 'function') {
uri = item.getUri()
} else {
return Promise.resolve(true)
}
const title = (typeof item.getTitle === 'function' && item.getTitle()) || uri
const saveDialog = (saveButtonText, saveFn, message) => {
const chosen = this.applicationDelegate.confirm({
message,
detailedMessage: 'Your changes will be lost if you close this item without saving.',
buttons: [saveButtonText, 'Cancel', "&Don't Save"]}
)
switch (chosen) {
case 0:
return new Promise(resolve => {
return saveFn(item, error => {
if (error instanceof SaveCancelledError) {
resolve(false)
} else if (error) {
saveDialog(
'Save as',
this.saveItemAs,
`'${title}' could not be saved.\nError: ${this.getMessageForErrorCode(error.code)}`
).then(resolve)
} else {
resolve(true)
}
})
})
case 1:
return Promise.resolve(false)
case 2:
return Promise.resolve(true)
return new Promise((resolve, reject) => {
if (typeof item.shouldPromptToSave !== 'function' || !item.shouldPromptToSave(options)) {
return resolve(true)
}
}
return saveDialog(
'Save',
this.saveItem,
`'${title}' has changes, do you want to save them?`
)
let uri
if (typeof item.getURI === 'function') {
uri = item.getURI()
} else if (typeof item.getUri === 'function') {
uri = item.getUri()
} else {
return resolve(true)
}
const title = (typeof item.getTitle === 'function' && item.getTitle()) || uri
const saveDialog = (saveButtonText, saveFn, message) => {
this.applicationDelegate.confirm({
message,
detail: 'Your changes will be lost if you close this item without saving.',
buttons: [saveButtonText, 'Cancel', "&Don't Save"]
}, response => {
switch (response) {
case 0:
return saveFn(item, error => {
if (error instanceof SaveCancelledError) {
resolve(false)
} else if (error) {
saveDialog(
'Save as',
this.saveItemAs,
`'${title}' could not be saved.\nError: ${this.getMessageForErrorCode(error.code)}`
)
} else {
resolve(true)
}
})
case 1:
return resolve(false)
case 2:
return resolve(true)
}
})
}
saveDialog('Save', this.saveItem, `'${title}' has changes, do you want to save them?`)
})
}
// Public: Save the active item.

View File

@ -1160,16 +1160,17 @@ module.exports = class Workspace extends Model {
// * `uri` A {String} containing a URI.
//
// Returns a {Promise} that resolves to the {TextEditor} (or other item) for the given URI.
createItemForURI (uri, options) {
async createItemForURI (uri, options) {
if (uri != null) {
for (let opener of this.getOpeners()) {
for (const opener of this.getOpeners()) {
const item = opener(uri, options)
if (item != null) return Promise.resolve(item)
if (item != null) return item
}
}
try {
return this.openTextFile(uri, options)
const item = await this.openTextFile(uri, options)
return item
} catch (error) {
switch (error.code) {
case 'CANCELLED':
@ -1199,7 +1200,7 @@ module.exports = class Workspace extends Model {
}
}
openTextFile (uri, options) {
async openTextFile (uri, options) {
const filePath = this.project.resolvePath(uri)
if (filePath != null) {
@ -1214,23 +1215,38 @@ module.exports = class Workspace extends Model {
}
const fileSize = fs.getSizeSync(filePath)
if (fileSize >= (this.config.get('core.warnOnLargeFileLimit') * 1048576)) {
const choice = this.applicationDelegate.confirm({
let [resolveConfirmFileOpenPromise, rejectConfirmFileOpenPromise] = []
const confirmFileOpenPromise = new Promise((resolve, reject) => {
resolveConfirmFileOpenPromise = resolve
rejectConfirmFileOpenPromise = reject
})
if (fileSize >= (this.config.get('core.warnOnLargeFileLimit') * 1048576)) { // 40MB by default
this.applicationDelegate.confirm({
message: 'Atom will be unresponsive during the loading of very large files.',
detailedMessage: 'Do you still want to load this file?',
detail: 'Do you still want to load this file?',
buttons: ['Proceed', 'Cancel']
}, response => {
if (response === 1) {
rejectConfirmFileOpenPromise()
} else {
resolveConfirmFileOpenPromise()
}
})
if (choice === 1) {
const error = new Error()
error.code = 'CANCELLED'
throw error
}
} else {
resolveConfirmFileOpenPromise()
}
return this.project.bufferForPath(filePath, options)
.then(buffer => {
return this.textEditorRegistry.build(Object.assign({buffer, autoHeight: false}, options))
})
try {
await confirmFileOpenPromise
const buffer = await this.project.bufferForPath(filePath, options)
return this.textEditorRegistry.build(Object.assign({buffer, autoHeight: false}, options))
} catch (e) {
const error = new Error()
error.code = 'CANCELLED'
throw error
}
}
handleGrammarUsed (grammar) {
@ -1987,25 +2003,22 @@ module.exports = class Workspace extends Model {
checkoutHeadRevision (editor) {
if (editor.getPath()) {
const checkoutHead = () => {
return this.project.repositoryForDirectory(new Directory(editor.getDirectoryPath()))
.then(repository => repository && repository.checkoutHeadForEditor(editor))
const checkoutHead = async () => {
const repository = await this.project.repositoryForDirectory(new Directory(editor.getDirectoryPath()))
if (repository) repository.checkoutHeadForEditor(editor)
}
if (this.config.get('editor.confirmCheckoutHeadRevision')) {
this.applicationDelegate.confirm({
message: 'Confirm Checkout HEAD Revision',
detailedMessage: `Are you sure you want to discard all changes to "${editor.getFileName()}" since the last Git commit?`,
buttons: {
OK: checkoutHead,
Cancel: null
}
detail: `Are you sure you want to discard all changes to "${editor.getFileName()}" since the last Git commit?`,
buttons: ['OK', 'Cancel']
}, response => {
if (response === 0) checkoutHead()
})
} else {
return checkoutHead()
checkoutHead()
}
} else {
return Promise.resolve(false)
}
}
}