mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2025-01-05 22:04:34 +03:00
Merge branch 'master' into ns-switch-to-display-layers
# Conflicts: # src/text-editor.coffee
This commit is contained in:
commit
3b46d7f50b
@ -6,6 +6,6 @@
|
|||||||
"url": "https://github.com/atom/atom.git"
|
"url": "https://github.com/atom/atom.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"atom-package-manager": "1.9.3"
|
"atom-package-manager": "1.10.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
package.json
14
package.json
@ -36,7 +36,7 @@
|
|||||||
"key-path-helpers": "^0.4.0",
|
"key-path-helpers": "^0.4.0",
|
||||||
"less-cache": "0.23",
|
"less-cache": "0.23",
|
||||||
"line-top-index": "0.2.0",
|
"line-top-index": "0.2.0",
|
||||||
"marked": "^0.3.4",
|
"marked": "^0.3.5",
|
||||||
"normalize-package-data": "^2.0.0",
|
"normalize-package-data": "^2.0.0",
|
||||||
"nslog": "^3",
|
"nslog": "^3",
|
||||||
"ohnogit": "0.0.11",
|
"ohnogit": "0.0.11",
|
||||||
@ -77,7 +77,7 @@
|
|||||||
"autocomplete-atom-api": "0.10.0",
|
"autocomplete-atom-api": "0.10.0",
|
||||||
"autocomplete-css": "0.11.1",
|
"autocomplete-css": "0.11.1",
|
||||||
"autocomplete-html": "0.7.2",
|
"autocomplete-html": "0.7.2",
|
||||||
"autocomplete-plus": "2.30.0",
|
"autocomplete-plus": "2.31.0",
|
||||||
"autocomplete-snippets": "1.10.0",
|
"autocomplete-snippets": "1.10.0",
|
||||||
"autoflow": "0.27.0",
|
"autoflow": "0.27.0",
|
||||||
"autosave": "0.23.1",
|
"autosave": "0.23.1",
|
||||||
@ -87,7 +87,7 @@
|
|||||||
"command-palette": "0.38.0",
|
"command-palette": "0.38.0",
|
||||||
"deprecation-cop": "0.54.1",
|
"deprecation-cop": "0.54.1",
|
||||||
"dev-live-reload": "0.47.0",
|
"dev-live-reload": "0.47.0",
|
||||||
"encoding-selector": "0.21.0",
|
"encoding-selector": "0.22.0",
|
||||||
"exception-reporting": "0.38.1",
|
"exception-reporting": "0.38.1",
|
||||||
"fuzzy-finder": "1.0.5",
|
"fuzzy-finder": "1.0.5",
|
||||||
"git-diff": "1.0.1",
|
"git-diff": "1.0.1",
|
||||||
@ -97,20 +97,20 @@
|
|||||||
"image-view": "0.57.0",
|
"image-view": "0.57.0",
|
||||||
"incompatible-packages": "0.26.1",
|
"incompatible-packages": "0.26.1",
|
||||||
"keybinding-resolver": "0.35.0",
|
"keybinding-resolver": "0.35.0",
|
||||||
"line-ending-selector": "0.4.1",
|
"line-ending-selector": "0.5.0",
|
||||||
"link": "0.31.1",
|
"link": "0.31.1",
|
||||||
"markdown-preview": "0.158.0",
|
"markdown-preview": "0.158.0",
|
||||||
"metrics": "0.53.1",
|
"metrics": "0.53.1",
|
||||||
"notifications": "0.63.2",
|
"notifications": "0.63.2",
|
||||||
"open-on-github": "1.1.0",
|
"open-on-github": "1.1.0",
|
||||||
"package-generator": "1.0.0",
|
"package-generator": "1.0.0",
|
||||||
"settings-view": "0.235.1",
|
"settings-view": "0.236.0",
|
||||||
"snippets": "1.0.2",
|
"snippets": "1.0.2",
|
||||||
"spell-check": "0.67.1",
|
"spell-check": "0.67.1",
|
||||||
"status-bar": "1.2.6",
|
"status-bar": "1.2.6",
|
||||||
"styleguide": "0.45.2",
|
"styleguide": "0.45.2",
|
||||||
"symbols-view": "0.112.0",
|
"symbols-view": "0.113.0",
|
||||||
"tabs": "0.93.1",
|
"tabs": "0.93.2",
|
||||||
"timecop": "0.33.1",
|
"timecop": "0.33.1",
|
||||||
"tree-view": "0.206.2",
|
"tree-view": "0.206.2",
|
||||||
"update-package-dependencies": "0.10.0",
|
"update-package-dependencies": "0.10.0",
|
||||||
|
@ -74,6 +74,13 @@ describe "CommandRegistry", ->
|
|||||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||||
expect(calls).toEqual ['.foo.bar', '.bar', '.foo']
|
expect(calls).toEqual ['.foo.bar', '.bar', '.foo']
|
||||||
|
|
||||||
|
it "orders inline listeners by reverse registration order", ->
|
||||||
|
calls = []
|
||||||
|
registry.add child, 'command', -> calls.push('child1')
|
||||||
|
registry.add child, 'command', -> calls.push('child2')
|
||||||
|
child.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||||
|
expect(calls).toEqual ['child2', 'child1']
|
||||||
|
|
||||||
it "stops bubbling through ancestors when .stopPropagation() is called on the event", ->
|
it "stops bubbling through ancestors when .stopPropagation() is called on the event", ->
|
||||||
calls = []
|
calls = []
|
||||||
|
|
||||||
|
@ -917,6 +917,82 @@ describe "Pane", ->
|
|||||||
expect(item1.save).not.toHaveBeenCalled()
|
expect(item1.save).not.toHaveBeenCalled()
|
||||||
expect(pane.isDestroyed()).toBe false
|
expect(pane.isDestroyed()).toBe false
|
||||||
|
|
||||||
|
describe "when item fails to save", ->
|
||||||
|
[pane, item1, item2] = []
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
pane = new Pane({items: [new Item("A"), new Item("B")], applicationDelegate: atom.applicationDelegate, config: atom.config})
|
||||||
|
[item1, item2] = pane.getItems()
|
||||||
|
|
||||||
|
item1.shouldPromptToSave = -> true
|
||||||
|
item1.getURI = -> "/test/path"
|
||||||
|
|
||||||
|
item1.save = jasmine.createSpy("save").andCallFake ->
|
||||||
|
error = new Error("EACCES, permission denied '/test/path'")
|
||||||
|
error.path = '/test/path'
|
||||||
|
error.code = 'EACCES'
|
||||||
|
throw error
|
||||||
|
|
||||||
|
it "does not destroy the pane if save fails and user clicks cancel", ->
|
||||||
|
confirmations = 0
|
||||||
|
confirm.andCallFake ->
|
||||||
|
confirmations++
|
||||||
|
if confirmations is 1
|
||||||
|
return 0 # click save
|
||||||
|
else
|
||||||
|
return 1 # click cancel
|
||||||
|
|
||||||
|
pane.close()
|
||||||
|
|
||||||
|
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
|
||||||
|
expect(confirmations).toBe(2)
|
||||||
|
expect(item1.save).toHaveBeenCalled()
|
||||||
|
expect(pane.isDestroyed()).toBe false
|
||||||
|
|
||||||
|
it "does destroy the pane if the user saves the file under a new name", ->
|
||||||
|
item1.saveAs = jasmine.createSpy("saveAs").andReturn(true)
|
||||||
|
|
||||||
|
confirmations = 0
|
||||||
|
confirm.andCallFake ->
|
||||||
|
confirmations++
|
||||||
|
return 0 # save and then save as
|
||||||
|
|
||||||
|
showSaveDialog.andReturn("new/path")
|
||||||
|
|
||||||
|
pane.close()
|
||||||
|
|
||||||
|
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
|
||||||
|
expect(confirmations).toBe(2)
|
||||||
|
expect(atom.applicationDelegate.showSaveDialog).toHaveBeenCalled()
|
||||||
|
expect(item1.save).toHaveBeenCalled()
|
||||||
|
expect(item1.saveAs).toHaveBeenCalled()
|
||||||
|
expect(pane.isDestroyed()).toBe true
|
||||||
|
|
||||||
|
it "asks again if the saveAs also fails", ->
|
||||||
|
item1.saveAs = jasmine.createSpy("saveAs").andCallFake ->
|
||||||
|
error = new Error("EACCES, permission denied '/test/path'")
|
||||||
|
error.path = '/test/path'
|
||||||
|
error.code = 'EACCES'
|
||||||
|
throw error
|
||||||
|
|
||||||
|
confirmations = 0
|
||||||
|
confirm.andCallFake ->
|
||||||
|
confirmations++
|
||||||
|
if confirmations < 3
|
||||||
|
return 0 # save, save as, save as
|
||||||
|
return 2 # don't save
|
||||||
|
|
||||||
|
showSaveDialog.andReturn("new/path")
|
||||||
|
|
||||||
|
pane.close()
|
||||||
|
|
||||||
|
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
|
||||||
|
expect(confirmations).toBe(3)
|
||||||
|
expect(atom.applicationDelegate.showSaveDialog).toHaveBeenCalled()
|
||||||
|
expect(item1.save).toHaveBeenCalled()
|
||||||
|
expect(item1.saveAs).toHaveBeenCalled()
|
||||||
|
expect(pane.isDestroyed()).toBe true
|
||||||
|
|
||||||
describe "::destroy()", ->
|
describe "::destroy()", ->
|
||||||
[container, pane1, pane2] = []
|
[container, pane1, pane2] = []
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ describe "TextEditorRegistry", ->
|
|||||||
it "gets added to the list of registered editors", ->
|
it "gets added to the list of registered editors", ->
|
||||||
editor = {}
|
editor = {}
|
||||||
registry.add(editor)
|
registry.add(editor)
|
||||||
|
expect(editor.registered).toBe true
|
||||||
expect(registry.editors.size).toBe 1
|
expect(registry.editors.size).toBe 1
|
||||||
expect(registry.editors.has(editor)).toBe(true)
|
expect(registry.editors.has(editor)).toBe(true)
|
||||||
|
|
||||||
@ -19,6 +20,16 @@ describe "TextEditorRegistry", ->
|
|||||||
expect(registry.editors.size).toBe 1
|
expect(registry.editors.size).toBe 1
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
expect(registry.editors.size).toBe 0
|
expect(registry.editors.size).toBe 0
|
||||||
|
expect(editor.registered).toBe false
|
||||||
|
|
||||||
|
it "can be removed", ->
|
||||||
|
editor = {}
|
||||||
|
registry.add(editor)
|
||||||
|
expect(registry.editors.size).toBe 1
|
||||||
|
success = registry.remove(editor)
|
||||||
|
expect(success).toBe true
|
||||||
|
expect(registry.editors.size).toBe 0
|
||||||
|
expect(editor.registered).toBe false
|
||||||
|
|
||||||
describe "when the registry is observed", ->
|
describe "when the registry is observed", ->
|
||||||
it "calls the callback for current and future editors until unsubscribed", ->
|
it "calls the callback for current and future editors until unsubscribed", ->
|
||||||
|
@ -244,11 +244,14 @@ class CommandRegistry
|
|||||||
(@selectorBasedListenersByCommandName[event.type] ? [])
|
(@selectorBasedListenersByCommandName[event.type] ? [])
|
||||||
.filter (listener) -> currentTarget.webkitMatchesSelector(listener.selector)
|
.filter (listener) -> currentTarget.webkitMatchesSelector(listener.selector)
|
||||||
.sort (a, b) -> a.compare(b)
|
.sort (a, b) -> a.compare(b)
|
||||||
listeners = listeners.concat(selectorBasedListeners)
|
listeners = selectorBasedListeners.concat(listeners)
|
||||||
|
|
||||||
matched = true if listeners.length > 0
|
matched = true if listeners.length > 0
|
||||||
|
|
||||||
for listener in listeners
|
# Call inline listeners first in reverse registration order,
|
||||||
|
# and selector-based listeners by specificity and reverse
|
||||||
|
# registration order.
|
||||||
|
for listener in listeners by -1
|
||||||
break if immediatePropagationStopped
|
break if immediatePropagationStopped
|
||||||
listener.callback.call(currentTarget, dispatchedEvent)
|
listener.callback.call(currentTarget, dispatchedEvent)
|
||||||
|
|
||||||
@ -271,8 +274,8 @@ class SelectorBasedListener
|
|||||||
@sequenceNumber = SequenceCount++
|
@sequenceNumber = SequenceCount++
|
||||||
|
|
||||||
compare: (other) ->
|
compare: (other) ->
|
||||||
other.specificity - @specificity or
|
@specificity - other.specificity or
|
||||||
other.sequenceNumber - @sequenceNumber
|
@sequenceNumber - other.sequenceNumber
|
||||||
|
|
||||||
class InlineListener
|
class InlineListener
|
||||||
constructor: (@callback) ->
|
constructor: (@callback) ->
|
||||||
|
@ -577,15 +577,23 @@ class Pane extends Model
|
|||||||
else
|
else
|
||||||
return true
|
return true
|
||||||
|
|
||||||
chosen = @applicationDelegate.confirm
|
saveDialog = (saveButtonText, saveFn, message) =>
|
||||||
message: "'#{item.getTitle?() ? uri}' has changes, do you want to save them?"
|
chosen = @applicationDelegate.confirm
|
||||||
detailedMessage: "Your changes will be lost if you close this item without saving."
|
message: message
|
||||||
buttons: ["Save", "Cancel", "Don't Save"]
|
detailedMessage: "Your changes will be lost if you close this item without saving."
|
||||||
|
buttons: [saveButtonText, "Cancel", "Don't save"]
|
||||||
|
switch chosen
|
||||||
|
when 0 then saveFn(item, saveError)
|
||||||
|
when 1 then false
|
||||||
|
when 2 then true
|
||||||
|
|
||||||
switch chosen
|
saveError = (error) =>
|
||||||
when 0 then @saveItem(item, -> true)
|
if error
|
||||||
when 1 then false
|
saveDialog("Save as", @saveItemAs, "'#{item.getTitle?() ? uri}' could not be saved.\nError: #{@getMessageForErrorCode(error.code)}")
|
||||||
when 2 then true
|
else
|
||||||
|
true
|
||||||
|
|
||||||
|
saveDialog("Save", @saveItem, "'#{item.getTitle?() ? uri}' has changes, do you want to save them?")
|
||||||
|
|
||||||
# Public: Save the active item.
|
# Public: Save the active item.
|
||||||
saveActiveItem: (nextAction) ->
|
saveActiveItem: (nextAction) ->
|
||||||
@ -602,9 +610,11 @@ class Pane extends Model
|
|||||||
# Public: Save the given item.
|
# Public: Save the given item.
|
||||||
#
|
#
|
||||||
# * `item` The item to save.
|
# * `item` The item to save.
|
||||||
# * `nextAction` (optional) {Function} which will be called after the item is
|
# * `nextAction` (optional) {Function} which will be called with no argument
|
||||||
# successfully saved.
|
# after the item is successfully saved, or with the error if it failed.
|
||||||
saveItem: (item, nextAction) ->
|
# The return value will be that of `nextAction` or `undefined` if it was not
|
||||||
|
# provided
|
||||||
|
saveItem: (item, nextAction) =>
|
||||||
if typeof item?.getURI is 'function'
|
if typeof item?.getURI is 'function'
|
||||||
itemURI = item.getURI()
|
itemURI = item.getURI()
|
||||||
else if typeof item?.getUri is 'function'
|
else if typeof item?.getUri is 'function'
|
||||||
@ -613,9 +623,12 @@ class Pane extends Model
|
|||||||
if itemURI?
|
if itemURI?
|
||||||
try
|
try
|
||||||
item.save?()
|
item.save?()
|
||||||
|
nextAction?()
|
||||||
catch error
|
catch error
|
||||||
@handleSaveError(error, item)
|
if nextAction
|
||||||
nextAction?()
|
nextAction(error)
|
||||||
|
else
|
||||||
|
@handleSaveError(error, item)
|
||||||
else
|
else
|
||||||
@saveItemAs(item, nextAction)
|
@saveItemAs(item, nextAction)
|
||||||
|
|
||||||
@ -623,9 +636,11 @@ class Pane extends Model
|
|||||||
# path they select.
|
# path they select.
|
||||||
#
|
#
|
||||||
# * `item` The item to save.
|
# * `item` The item to save.
|
||||||
# * `nextAction` (optional) {Function} which will be called after the item is
|
# * `nextAction` (optional) {Function} which will be called with no argument
|
||||||
# successfully saved.
|
# after the item is successfully saved, or with the error if it failed.
|
||||||
saveItemAs: (item, nextAction) ->
|
# The return value will be that of `nextAction` or `undefined` if it was not
|
||||||
|
# provided
|
||||||
|
saveItemAs: (item, nextAction) =>
|
||||||
return unless item?.saveAs?
|
return unless item?.saveAs?
|
||||||
|
|
||||||
saveOptions = item.getSaveDialogOptions?() ? {}
|
saveOptions = item.getSaveDialogOptions?() ? {}
|
||||||
@ -634,9 +649,12 @@ class Pane extends Model
|
|||||||
if newItemPath
|
if newItemPath
|
||||||
try
|
try
|
||||||
item.saveAs(newItemPath)
|
item.saveAs(newItemPath)
|
||||||
|
nextAction?()
|
||||||
catch error
|
catch error
|
||||||
@handleSaveError(error, item)
|
if nextAction
|
||||||
nextAction?()
|
nextAction(error)
|
||||||
|
else
|
||||||
|
@handleSaveError(error, item)
|
||||||
|
|
||||||
# Public: Save all items.
|
# Public: Save all items.
|
||||||
saveItems: ->
|
saveItems: ->
|
||||||
|
@ -26,8 +26,20 @@ class TextEditorRegistry
|
|||||||
# editor is destroyed.
|
# editor is destroyed.
|
||||||
add: (editor) ->
|
add: (editor) ->
|
||||||
@editors.add(editor)
|
@editors.add(editor)
|
||||||
|
editor.registered = true
|
||||||
|
|
||||||
@emitter.emit 'did-add-editor', editor
|
@emitter.emit 'did-add-editor', editor
|
||||||
new Disposable => @editors.delete(editor)
|
new Disposable => @remove(editor)
|
||||||
|
|
||||||
|
# Remove a `TextEditor`.
|
||||||
|
#
|
||||||
|
# * `editor` The editor to remove.
|
||||||
|
#
|
||||||
|
# Returns a {Boolean} indicating whether the editor was successfully removed.
|
||||||
|
remove: (editor) ->
|
||||||
|
removed = @editors.delete(editor)
|
||||||
|
editor.registered = false
|
||||||
|
removed
|
||||||
|
|
||||||
# Invoke the given callback with all the current and future registered
|
# Invoke the given callback with all the current and future registered
|
||||||
# `TextEditors`.
|
# `TextEditors`.
|
||||||
|
@ -76,6 +76,7 @@ class TextEditor extends Model
|
|||||||
defaultCharWidth: null
|
defaultCharWidth: null
|
||||||
height: null
|
height: null
|
||||||
width: null
|
width: null
|
||||||
|
registered: false
|
||||||
|
|
||||||
Object.defineProperty @prototype, "element",
|
Object.defineProperty @prototype, "element",
|
||||||
get: -> @getElement()
|
get: -> @getElement()
|
||||||
@ -202,7 +203,7 @@ class TextEditor extends Model
|
|||||||
tokenizedBuffer: tokenizedBufferState
|
tokenizedBuffer: tokenizedBufferState
|
||||||
largeFileMode: @largeFileMode
|
largeFileMode: @largeFileMode
|
||||||
displayLayerId: @displayLayer.id
|
displayLayerId: @displayLayer.id
|
||||||
registered: atom.textEditors.editors.has this
|
registered: @registered
|
||||||
|
|
||||||
subscribeToBuffer: ->
|
subscribeToBuffer: ->
|
||||||
@buffer.retain()
|
@buffer.retain()
|
||||||
|
@ -1092,7 +1092,7 @@ class Workspace extends Model
|
|||||||
if editor.getPath()
|
if editor.getPath()
|
||||||
checkoutHead = =>
|
checkoutHead = =>
|
||||||
@project.repositoryForDirectory(new Directory(editor.getDirectoryPath()))
|
@project.repositoryForDirectory(new Directory(editor.getDirectoryPath()))
|
||||||
.then (repository) =>
|
.then (repository) ->
|
||||||
repository?.async.checkoutHeadForEditor(editor)
|
repository?.async.checkoutHeadForEditor(editor)
|
||||||
|
|
||||||
if @config.get('editor.confirmCheckoutHeadRevision')
|
if @config.get('editor.confirmCheckoutHeadRevision')
|
||||||
|
Loading…
Reference in New Issue
Block a user