mirror of
https://github.com/atom/atom.git
synced 2026-01-26 23:38:48 -05:00
Merge branch 'master' into atom-textarea
This commit is contained in:
4
circle.yml
Normal file
4
circle.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
general:
|
||||
branches:
|
||||
only:
|
||||
- io-circle-ci
|
||||
@@ -73,8 +73,10 @@
|
||||
'cmd-alt-right': 'pane:show-next-item'
|
||||
'ctrl-pageup': 'pane:show-previous-item'
|
||||
'ctrl-pagedown': 'pane:show-next-item'
|
||||
'ctrl-tab': 'pane:show-next-item'
|
||||
'ctrl-shift-tab': 'pane:show-previous-item'
|
||||
'ctrl-tab': 'pane:show-next-recently-used-item'
|
||||
'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack'
|
||||
'ctrl-shift-tab': 'pane:show-previous-recently-used-item'
|
||||
'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack'
|
||||
'cmd-=': 'window:increase-font-size'
|
||||
'cmd-+': 'window:increase-font-size'
|
||||
'cmd--': 'window:decrease-font-size'
|
||||
|
||||
@@ -46,8 +46,10 @@
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
'shift-backspace': 'core:backspace'
|
||||
'ctrl-tab': 'pane:show-next-item'
|
||||
'ctrl-shift-tab': 'pane:show-previous-item'
|
||||
'ctrl-tab': 'pane:show-next-recently-used-item'
|
||||
'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack'
|
||||
'ctrl-shift-tab': 'pane:show-previous-recently-used-item'
|
||||
'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack'
|
||||
'ctrl-pageup': 'pane:show-previous-item'
|
||||
'ctrl-pagedown': 'pane:show-next-item'
|
||||
'ctrl-up': 'core:move-up'
|
||||
|
||||
@@ -52,8 +52,10 @@
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
'shift-backspace': 'core:backspace'
|
||||
'ctrl-tab': 'pane:show-next-item'
|
||||
'ctrl-shift-tab': 'pane:show-previous-item'
|
||||
'ctrl-tab': 'pane:show-next-recently-used-item'
|
||||
'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack'
|
||||
'ctrl-shift-tab': 'pane:show-previous-recently-used-item'
|
||||
'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack'
|
||||
'ctrl-pageup': 'pane:show-previous-item'
|
||||
'ctrl-pagedown': 'pane:show-next-item'
|
||||
'ctrl-shift-up': 'core:move-up'
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"less-cache": "0.23",
|
||||
"line-top-index": "0.2.0",
|
||||
"marked": "^0.3.4",
|
||||
"nodegit": "0.9.0",
|
||||
"nodegit": "0.11.5",
|
||||
"normalize-package-data": "^2.0.0",
|
||||
"nslog": "^3",
|
||||
"oniguruma": "^5",
|
||||
@@ -83,7 +83,7 @@
|
||||
"autosave": "0.23.1",
|
||||
"background-tips": "0.26.0",
|
||||
"bookmarks": "0.38.2",
|
||||
"bracket-matcher": "0.80.0",
|
||||
"bracket-matcher": "0.80.1",
|
||||
"command-palette": "0.38.0",
|
||||
"deprecation-cop": "0.54.1",
|
||||
"dev-live-reload": "0.47.0",
|
||||
@@ -112,7 +112,7 @@
|
||||
"symbols-view": "0.111.1",
|
||||
"tabs": "0.90.2",
|
||||
"timecop": "0.33.1",
|
||||
"tree-view": "0.201.4",
|
||||
"tree-view": "0.201.5",
|
||||
"update-package-dependencies": "0.10.0",
|
||||
"welcome": "0.34.0",
|
||||
"whitespace": "0.32.2",
|
||||
|
||||
@@ -35,8 +35,7 @@ IF "%EXPECT_OUTPUT%"=="YES" (
|
||||
"%~dp0\..\..\atom.exe" --pid=%PID% %*
|
||||
rem If the wait flag is set, don't exit this process until Atom tells it to.
|
||||
goto waitLoop
|
||||
)
|
||||
ELSE (
|
||||
) ELSE (
|
||||
"%~dp0\..\..\atom.exe" %*
|
||||
)
|
||||
) ELSE (
|
||||
|
||||
@@ -18,8 +18,8 @@ describe "Pane", ->
|
||||
onDidDestroy: (fn) -> @emitter.on('did-destroy', fn)
|
||||
destroy: -> @destroyed = true; @emitter.emit('did-destroy')
|
||||
isDestroyed: -> @destroyed
|
||||
isPending: -> @pending
|
||||
pending: false
|
||||
onDidTerminatePendingState: (callback) -> @emitter.on 'terminate-pending-state', callback
|
||||
terminatePendingState: -> @emitter.emit 'terminate-pending-state'
|
||||
|
||||
beforeEach ->
|
||||
confirm = spyOn(atom.applicationDelegate, 'confirm')
|
||||
@@ -136,10 +136,8 @@ describe "Pane", ->
|
||||
pane = new Pane(paneParams(items: []))
|
||||
itemA = new Item("A")
|
||||
itemB = new Item("B")
|
||||
itemA.pending = true
|
||||
itemB.pending = true
|
||||
pane.addItem(itemA)
|
||||
pane.addItem(itemB)
|
||||
pane.addItem(itemA, undefined, false, true)
|
||||
pane.addItem(itemB, undefined, false, true)
|
||||
expect(itemA.isDestroyed()).toBe true
|
||||
|
||||
describe "::activateItem(item)", ->
|
||||
@@ -172,21 +170,83 @@ describe "Pane", ->
|
||||
beforeEach ->
|
||||
itemC = new Item("C")
|
||||
itemD = new Item("D")
|
||||
itemC.pending = true
|
||||
itemD.pending = true
|
||||
|
||||
it "replaces the active item if it is pending", ->
|
||||
pane.activateItem(itemC)
|
||||
pane.activateItem(itemC, true)
|
||||
expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'C', 'B']
|
||||
pane.activateItem(itemD)
|
||||
pane.activateItem(itemD, true)
|
||||
expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'D', 'B']
|
||||
|
||||
it "adds the item after the active item if it is not pending", ->
|
||||
pane.activateItem(itemC)
|
||||
pane.activateItem(itemC, true)
|
||||
pane.activateItemAtIndex(2)
|
||||
pane.activateItem(itemD)
|
||||
pane.activateItem(itemD, true)
|
||||
expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D']
|
||||
|
||||
describe "::setPendingItem", ->
|
||||
pane = null
|
||||
|
||||
beforeEach ->
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
it "changes the pending item", ->
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
pane.setPendingItem("fake item")
|
||||
expect(pane.getPendingItem()).toEqual "fake item"
|
||||
|
||||
describe "::onItemDidTerminatePendingState callback", ->
|
||||
pane = null
|
||||
callbackCalled = false
|
||||
|
||||
beforeEach ->
|
||||
pane = atom.workspace.getActivePane()
|
||||
callbackCalled = false
|
||||
|
||||
it "is called when the pending item changes", ->
|
||||
pane.setPendingItem("fake item one")
|
||||
pane.onItemDidTerminatePendingState (item) ->
|
||||
callbackCalled = true
|
||||
expect(item).toEqual "fake item one"
|
||||
pane.setPendingItem("fake item two")
|
||||
expect(callbackCalled).toBeTruthy()
|
||||
|
||||
it "has access to the new pending item via ::getPendingItem", ->
|
||||
pane.setPendingItem("fake item one")
|
||||
pane.onItemDidTerminatePendingState (item) ->
|
||||
callbackCalled = true
|
||||
expect(pane.getPendingItem()).toEqual "fake item two"
|
||||
pane.setPendingItem("fake item two")
|
||||
expect(callbackCalled).toBeTruthy()
|
||||
|
||||
describe "::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()", ->
|
||||
it "sets the active item to the next/previous item in the itemStack, looping around at either end", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")]))
|
||||
[item1, item2, item3, item4, item5] = pane.getItems()
|
||||
pane.itemStack = [item3, item1, item2, item5, item4]
|
||||
|
||||
pane.activateItem(item4)
|
||||
expect(pane.getActiveItem()).toBe item4
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item5
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
pane.activatePreviousRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item5
|
||||
pane.activatePreviousRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item4
|
||||
pane.activatePreviousRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
pane.activatePreviousRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item4
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
pane.moveActiveItemToTopOfStack()
|
||||
expect(pane.getActiveItem()).toBe item5
|
||||
expect(pane.itemStack[4]).toBe item5
|
||||
|
||||
describe "::activateNextItem() and ::activatePreviousItem()", ->
|
||||
it "sets the active item to the next/previous item, looping around at either end", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
|
||||
@@ -253,7 +313,7 @@ describe "Pane", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
|
||||
it "removes the item from the items list and destroyes it", ->
|
||||
it "removes the item from the items list and destroys it", ->
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.destroyItem(item2)
|
||||
expect(item2 in pane.getItems()).toBe false
|
||||
@@ -264,6 +324,23 @@ describe "Pane", ->
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
it "removes the item from the itemStack", ->
|
||||
pane.itemStack = [item2, item3, item1]
|
||||
|
||||
pane.activateItem(item1)
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.destroyItem(item3)
|
||||
expect(pane.itemStack).toEqual [item2, item1]
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
|
||||
pane.destroyItem(item1)
|
||||
expect(pane.itemStack).toEqual [item2]
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
|
||||
pane.destroyItem(item2)
|
||||
expect(pane.itemStack).toEqual []
|
||||
expect(pane.getActiveItem()).toBeUndefined()
|
||||
|
||||
it "invokes ::onWillDestroyItem() observers before destroying the item", ->
|
||||
events = []
|
||||
pane.onWillDestroyItem (event) ->
|
||||
@@ -806,6 +883,67 @@ describe "Pane", ->
|
||||
pane2.destroy()
|
||||
expect(container.root).toBe pane1
|
||||
|
||||
describe "pending state", ->
|
||||
editor1 = null
|
||||
pane = null
|
||||
eventCount = null
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt', pending: true).then (o) ->
|
||||
editor1 = o
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
runs ->
|
||||
eventCount = 0
|
||||
editor1.onDidTerminatePendingState -> eventCount++
|
||||
|
||||
it "does not open file in pending state by default", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) ->
|
||||
editor1 = o
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
runs ->
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
|
||||
it "opens file in pending state if 'pending' option is true", ->
|
||||
expect(pane.getPendingItem()).toEqual editor1
|
||||
|
||||
it "terminates pending state if ::terminatePendingState is invoked", ->
|
||||
editor1.terminatePendingState()
|
||||
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "terminates pending state when buffer is changed", ->
|
||||
editor1.insertText('I\'ll be back!')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "only calls terminate handler once when text is modified twice", ->
|
||||
editor1.insertText('Some text')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
editor1.save()
|
||||
|
||||
editor1.insertText('More text')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "only calls clearPendingItem if there is a pending item to clear", ->
|
||||
spyOn(pane, "clearPendingItem").andCallThrough()
|
||||
|
||||
editor1.terminatePendingState()
|
||||
editor1.terminatePendingState()
|
||||
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
expect(pane.clearPendingItem.callCount).toBe 1
|
||||
|
||||
describe "serialization", ->
|
||||
pane = null
|
||||
|
||||
@@ -837,3 +975,30 @@ describe "Pane", ->
|
||||
pane.focus()
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.focused).toBe true
|
||||
|
||||
it "can serialize and deserialize the order of the items in the itemStack", ->
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.itemStack = [item3, item1, item2]
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.itemStack).toEqual pane.itemStack
|
||||
expect(newPane.itemStack[2]).toEqual item2
|
||||
|
||||
it "builds the itemStack if the itemStack is not serialized", ->
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.getItems()).toEqual newPane.itemStack
|
||||
|
||||
it "rebuilds the itemStack if items.length does not match itemStack.length", ->
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.itemStack = [item2, item3]
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.getItems()).toEqual newPane.itemStack
|
||||
|
||||
it "does not serialize the reference to the items in the itemStack for pane items that will not be serialized", ->
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.itemStack = [item2, item1, item3]
|
||||
unserializable = {}
|
||||
pane.activateItem(unserializable)
|
||||
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.itemStack).toEqual [item2, item1, item3]
|
||||
|
||||
@@ -55,16 +55,6 @@ describe "TextEditor", ->
|
||||
|
||||
expect(editor.tokenizedLineForScreenRow(0).invisibles.eol).toBe '?'
|
||||
|
||||
it "restores pending tabs in pending state", ->
|
||||
expect(editor.isPending()).toBe false
|
||||
editor2 = TextEditor.deserialize(editor.serialize(), atom)
|
||||
expect(editor2.isPending()).toBe false
|
||||
|
||||
pendingEditor = atom.workspace.buildTextEditor(pending: true)
|
||||
expect(pendingEditor.isPending()).toBe true
|
||||
editor3 = TextEditor.deserialize(pendingEditor.serialize(), atom)
|
||||
expect(editor3.isPending()).toBe true
|
||||
|
||||
describe "when the editor is constructed with the largeFileMode option set to true", ->
|
||||
it "loads the editor but doesn't tokenize", ->
|
||||
editor = null
|
||||
@@ -5827,53 +5817,3 @@ describe "TextEditor", ->
|
||||
screenRange: marker1.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
describe "pending state", ->
|
||||
editor1 = null
|
||||
eventCount = null
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt', pending: true).then (o) -> editor1 = o
|
||||
|
||||
runs ->
|
||||
eventCount = 0
|
||||
editor1.onDidTerminatePendingState -> eventCount++
|
||||
|
||||
it "does not open file in pending state by default", ->
|
||||
expect(editor.isPending()).toBe false
|
||||
|
||||
it "opens file in pending state if 'pending' option is true", ->
|
||||
expect(editor1.isPending()).toBe true
|
||||
|
||||
it "terminates pending state if ::terminatePendingState is invoked", ->
|
||||
editor1.terminatePendingState()
|
||||
|
||||
expect(editor1.isPending()).toBe false
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "terminates pending state when buffer is changed", ->
|
||||
editor1.insertText('I\'ll be back!')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
expect(editor1.isPending()).toBe false
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "only calls terminate handler once when text is modified twice", ->
|
||||
editor1.insertText('Some text')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
editor1.save()
|
||||
|
||||
editor1.insertText('More text')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
expect(editor1.isPending()).toBe false
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "only calls terminate handler once when terminatePendingState is called twice", ->
|
||||
editor1.terminatePendingState()
|
||||
editor1.terminatePendingState()
|
||||
|
||||
expect(editor1.isPending()).toBe false
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
@@ -588,19 +588,22 @@ describe "Workspace", ->
|
||||
describe "when the file is already open in pending state", ->
|
||||
it "should terminate the pending state", ->
|
||||
editor = null
|
||||
pane = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', pending: true).then (o) -> editor = o
|
||||
|
||||
atom.workspace.open('sample.js', pending: true).then (o) ->
|
||||
editor = o
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
runs ->
|
||||
expect(editor.isPending()).toBe true
|
||||
|
||||
expect(pane.getPendingItem()).toEqual editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
atom.workspace.open('sample.js')
|
||||
|
||||
runs ->
|
||||
expect(editor.isPending()).toBe false
|
||||
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
|
||||
describe "::reopenItem()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
pane = workspace.getActivePane()
|
||||
@@ -1551,11 +1554,12 @@ describe "Workspace", ->
|
||||
|
||||
describe "when the core.allowPendingPaneItems option is falsey", ->
|
||||
it "does not open item with `pending: true` option as pending", ->
|
||||
editor = null
|
||||
pane = null
|
||||
atom.config.set('core.allowPendingPaneItems', false)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', pending: true).then (o) -> editor = o
|
||||
atom.workspace.open('sample.js', pending: true).then ->
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
runs ->
|
||||
expect(editor.isPending()).toBeFalsy()
|
||||
expect(pane.getPendingItem()).toBeFalsy()
|
||||
|
||||
@@ -11,9 +11,11 @@ exeName = path.basename(process.execPath)
|
||||
if process.env.SystemRoot
|
||||
system32Path = path.join(process.env.SystemRoot, 'System32')
|
||||
regPath = path.join(system32Path, 'reg.exe')
|
||||
powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe')
|
||||
setxPath = path.join(system32Path, 'setx.exe')
|
||||
else
|
||||
regPath = 'reg.exe'
|
||||
powershellPath = 'powershell.exe'
|
||||
setxPath = 'setx.exe'
|
||||
|
||||
# Registry keys used for context menu
|
||||
@@ -44,11 +46,31 @@ spawn = (command, args, callback) ->
|
||||
error?.code ?= code
|
||||
error?.stdout ?= stdout
|
||||
callback?(error, stdout)
|
||||
# This is necessary if using Powershell 2 on Windows 7 to get the events to raise
|
||||
# http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs
|
||||
spawnedProcess.stdin.end()
|
||||
|
||||
|
||||
# Spawn reg.exe and callback when it completes
|
||||
spawnReg = (args, callback) ->
|
||||
spawn(regPath, args, callback)
|
||||
|
||||
# Spawn powershell.exe and callback when it completes
|
||||
spawnPowershell = (args, callback) ->
|
||||
# set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding
|
||||
# http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell
|
||||
# to address https://github.com/atom/atom/issues/5063
|
||||
args[0] = """
|
||||
[Console]::OutputEncoding=[System.Text.Encoding]::UTF8
|
||||
$output=#{args[0]}
|
||||
[Console]::WriteLine($output)
|
||||
"""
|
||||
args.unshift('-command')
|
||||
args.unshift('RemoteSigned')
|
||||
args.unshift('-ExecutionPolicy')
|
||||
args.unshift('-noprofile')
|
||||
spawn(powershellPath, args, callback)
|
||||
|
||||
# Spawn setx.exe and callback when it completes
|
||||
spawnSetx = (args, callback) ->
|
||||
spawn(setxPath, args, callback)
|
||||
@@ -82,46 +104,14 @@ installContextMenu = (callback) ->
|
||||
installMenu backgroundKeyPath, '%V', ->
|
||||
installFileHandler(callback)
|
||||
|
||||
isAscii = (text) ->
|
||||
index = 0
|
||||
while index < text.length
|
||||
return false if text.charCodeAt(index) > 127
|
||||
index++
|
||||
true
|
||||
|
||||
# Get the user's PATH environment variable registry value.
|
||||
getPath = (callback) ->
|
||||
spawnReg ['query', environmentKeyPath, '/v', 'Path'], (error, stdout) ->
|
||||
spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) ->
|
||||
if error?
|
||||
if error.code is 1
|
||||
# FIXME Don't overwrite path when reading value is disabled
|
||||
# https://github.com/atom/atom/issues/5092
|
||||
if stdout.indexOf('ERROR: Registry editing has been disabled by your administrator.') isnt -1
|
||||
return callback(error)
|
||||
return callback(error)
|
||||
|
||||
# The query failed so the Path does not exist yet in the registry
|
||||
return callback(null, '')
|
||||
else
|
||||
return callback(error)
|
||||
|
||||
# Registry query output is in the form:
|
||||
#
|
||||
# HKEY_CURRENT_USER\Environment
|
||||
# Path REG_SZ C:\a\folder\on\the\path;C\another\folder
|
||||
#
|
||||
|
||||
lines = stdout.split(/[\r\n]+/).filter (line) -> line
|
||||
segments = lines[lines.length - 1]?.split(' ')
|
||||
if segments[1] is 'Path' and segments.length >= 3
|
||||
pathEnv = segments?[3..].join(' ')
|
||||
if isAscii(pathEnv)
|
||||
callback(null, pathEnv)
|
||||
else
|
||||
# FIXME Don't corrupt non-ASCII PATH values
|
||||
# https://github.com/atom/atom/issues/5063
|
||||
callback(new Error('PATH contains non-ASCII values'))
|
||||
else
|
||||
callback(new Error('Registry query for PATH failed'))
|
||||
pathOutput = stdout.replace(/^\s+|\s+$/g, '')
|
||||
callback(null, pathOutput)
|
||||
|
||||
# Uninstall the Open with Atom explorer context menu items via the registry.
|
||||
uninstallContextMenu = (callback) ->
|
||||
|
||||
122
src/pane.coffee
122
src/pane.coffee
@@ -1,5 +1,5 @@
|
||||
{find, compact, extend, last} = require 'underscore-plus'
|
||||
{Emitter} = require 'event-kit'
|
||||
{CompositeDisposable, Emitter} = require 'event-kit'
|
||||
Model = require './model'
|
||||
PaneAxis = require './pane-axis'
|
||||
TextEditor = require './text-editor'
|
||||
@@ -8,6 +8,11 @@ TextEditor = require './text-editor'
|
||||
# Panes can contain multiple items, one of which is *active* at a given time.
|
||||
# The view corresponding to the active item is displayed in the interface. In
|
||||
# the default configuration, tabs are also displayed for each item.
|
||||
#
|
||||
# Each pane may also contain one *pending* item. When a pending item is added
|
||||
# to a pane, it will replace the currently pending item, if any, instead of
|
||||
# simply being added. In the default configuration, the text in the tab for
|
||||
# pending items is shown in italics.
|
||||
module.exports =
|
||||
class Pane extends Model
|
||||
container: undefined
|
||||
@@ -15,7 +20,7 @@ class Pane extends Model
|
||||
focused: false
|
||||
|
||||
@deserialize: (state, {deserializers, applicationDelegate, config, notifications}) ->
|
||||
{items, activeItemURI, activeItemUri} = state
|
||||
{items, itemStackIndices, activeItemURI, activeItemUri} = state
|
||||
activeItemURI ?= activeItemUri
|
||||
state.items = compact(items.map (itemState) -> deserializers.deserialize(itemState))
|
||||
state.activeItem = find state.items, (item) ->
|
||||
@@ -37,20 +42,25 @@ class Pane extends Model
|
||||
} = params
|
||||
|
||||
@emitter = new Emitter
|
||||
@itemSubscriptions = new WeakMap
|
||||
@subscriptionsPerItem = new WeakMap
|
||||
@items = []
|
||||
@itemStack = []
|
||||
|
||||
@addItems(compact(params?.items ? []))
|
||||
@setActiveItem(@items[0]) unless @getActiveItem()?
|
||||
@addItemsToStack(params?.itemStackIndices ? [])
|
||||
@setFlexScale(params?.flexScale ? 1)
|
||||
|
||||
serialize: ->
|
||||
if typeof @activeItem?.getURI is 'function'
|
||||
activeItemURI = @activeItem.getURI()
|
||||
itemsToBeSerialized = compact(@items.map((item) -> item if typeof item.serialize is 'function'))
|
||||
itemStackIndices = (itemsToBeSerialized.indexOf(item) for item in @itemStack when typeof item.serialize is 'function')
|
||||
|
||||
deserializer: 'Pane'
|
||||
id: @id
|
||||
items: compact(@items.map((item) -> item.serialize?()))
|
||||
items: itemsToBeSerialized.map((item) -> item.serialize())
|
||||
itemStackIndices: itemStackIndices
|
||||
activeItemURI: activeItemURI
|
||||
focused: @focused
|
||||
flexScale: @flexScale
|
||||
@@ -260,8 +270,8 @@ class Pane extends Model
|
||||
getPanes: -> [this]
|
||||
|
||||
unsubscribeFromItem: (item) ->
|
||||
@itemSubscriptions.get(item)?.dispose()
|
||||
@itemSubscriptions.delete(item)
|
||||
@subscriptionsPerItem.get(item)?.dispose()
|
||||
@subscriptionsPerItem.delete(item)
|
||||
|
||||
###
|
||||
Section: Items
|
||||
@@ -278,12 +288,30 @@ class Pane extends Model
|
||||
# Returns a pane item.
|
||||
getActiveItem: -> @activeItem
|
||||
|
||||
setActiveItem: (activeItem) ->
|
||||
setActiveItem: (activeItem, options) ->
|
||||
{modifyStack} = options if options?
|
||||
unless activeItem is @activeItem
|
||||
@addItemToStack(activeItem) unless modifyStack is false
|
||||
@activeItem = activeItem
|
||||
@emitter.emit 'did-change-active-item', @activeItem
|
||||
@activeItem
|
||||
|
||||
# Build the itemStack after deserializing
|
||||
addItemsToStack: (itemStackIndices) ->
|
||||
if @items.length > 0
|
||||
if itemStackIndices.length is 0 or itemStackIndices.length isnt @items.length or itemStackIndices.indexOf(-1) >= 0
|
||||
itemStackIndices = (i for i in [0..@items.length-1])
|
||||
for itemIndex in itemStackIndices
|
||||
@addItemToStack(@items[itemIndex])
|
||||
return
|
||||
|
||||
# Add item (or move item) to the end of the itemStack
|
||||
addItemToStack: (newItem) ->
|
||||
return unless newItem?
|
||||
index = @itemStack.indexOf(newItem)
|
||||
@itemStack.splice(index, 1) unless index is -1
|
||||
@itemStack.push(newItem)
|
||||
|
||||
# Return an {TextEditor} if the pane item is an {TextEditor}, or null otherwise.
|
||||
getActiveEditor: ->
|
||||
@activeItem if @activeItem instanceof TextEditor
|
||||
@@ -296,6 +324,29 @@ class Pane extends Model
|
||||
itemAtIndex: (index) ->
|
||||
@items[index]
|
||||
|
||||
# Makes the next item in the itemStack active.
|
||||
activateNextRecentlyUsedItem: ->
|
||||
if @items.length > 1
|
||||
@itemStackIndex = @itemStack.length - 1 unless @itemStackIndex?
|
||||
@itemStackIndex = @itemStack.length if @itemStackIndex is 0
|
||||
@itemStackIndex = @itemStackIndex - 1
|
||||
nextRecentlyUsedItem = @itemStack[@itemStackIndex]
|
||||
@setActiveItem(nextRecentlyUsedItem, modifyStack: false)
|
||||
|
||||
# Makes the previous item in the itemStack active.
|
||||
activatePreviousRecentlyUsedItem: ->
|
||||
if @items.length > 1
|
||||
if @itemStackIndex + 1 is @itemStack.length or not @itemStackIndex?
|
||||
@itemStackIndex = -1
|
||||
@itemStackIndex = @itemStackIndex + 1
|
||||
previousRecentlyUsedItem = @itemStack[@itemStackIndex]
|
||||
@setActiveItem(previousRecentlyUsedItem, modifyStack: false)
|
||||
|
||||
# Moves the active item to the end of the itemStack once the ctrl key is lifted
|
||||
moveActiveItemToTopOfStack: ->
|
||||
delete @itemStackIndex
|
||||
@addItemToStack(@activeItem)
|
||||
|
||||
# Public: Makes the next item active.
|
||||
activateNextItem: ->
|
||||
index = @getActiveItemIndex()
|
||||
@@ -342,13 +393,17 @@ class Pane extends Model
|
||||
|
||||
# Public: Make the given item *active*, causing it to be displayed by
|
||||
# the pane's view.
|
||||
activateItem: (item) ->
|
||||
#
|
||||
# * `pending` (optional) {Boolean} indicating that the item should be added
|
||||
# in a pending state if it does not yet exist in the pane. Existing pending
|
||||
# items in a pane are replaced with new pending items when they are opened.
|
||||
activateItem: (item, pending=false) ->
|
||||
if item?
|
||||
if @activeItem?.isPending?()
|
||||
if @getPendingItem() is @activeItem
|
||||
index = @getActiveItemIndex()
|
||||
else
|
||||
index = @getActiveItemIndex() + 1
|
||||
@addItem(item, index, false)
|
||||
@addItem(item, index, false, pending)
|
||||
@setActiveItem(item)
|
||||
|
||||
# Public: Add the given item to the pane.
|
||||
@@ -357,28 +412,50 @@ class Pane extends Model
|
||||
# view.
|
||||
# * `index` (optional) {Number} indicating the index at which to add the item.
|
||||
# If omitted, the item is added after the current active item.
|
||||
# * `pending` (optional) {Boolean} indicating that the item should be
|
||||
# added in a pending state. Existing pending items in a pane are replaced with
|
||||
# new pending items when they are opened.
|
||||
#
|
||||
# Returns the added item.
|
||||
addItem: (item, index=@getActiveItemIndex() + 1, moved=false) ->
|
||||
addItem: (item, index=@getActiveItemIndex() + 1, moved=false, pending=false) ->
|
||||
throw new Error("Pane items must be objects. Attempted to add item #{item}.") unless item? and typeof item is 'object'
|
||||
throw new Error("Adding a pane item with URI '#{item.getURI?()}' that has already been destroyed") if item.isDestroyed?()
|
||||
|
||||
return if item in @items
|
||||
|
||||
if item.isPending?()
|
||||
for existingItem, i in @items
|
||||
if existingItem.isPending?()
|
||||
@destroyItem(existingItem)
|
||||
break
|
||||
pendingItem = @getPendingItem()
|
||||
@destroyItem(pendingItem) if pendingItem?
|
||||
@setPendingItem(item) if pending
|
||||
|
||||
if typeof item.onDidDestroy is 'function'
|
||||
@itemSubscriptions.set item, item.onDidDestroy => @removeItem(item, false)
|
||||
itemSubscriptions = new CompositeDisposable
|
||||
itemSubscriptions.add item.onDidDestroy => @removeItem(item, false)
|
||||
if typeof item.onDidTerminatePendingState is "function"
|
||||
itemSubscriptions.add item.onDidTerminatePendingState =>
|
||||
@clearPendingItem() if @getPendingItem() is item
|
||||
itemSubscriptions.add item.onDidDestroy => @removeItem(item, false)
|
||||
@subscriptionsPerItem.set item, itemSubscriptions
|
||||
|
||||
@items.splice(index, 0, item)
|
||||
@emitter.emit 'did-add-item', {item, index, moved}
|
||||
@setActiveItem(item) unless @getActiveItem()?
|
||||
item
|
||||
|
||||
setPendingItem: (item) =>
|
||||
if @pendingItem isnt item
|
||||
mostRecentPendingItem = @pendingItem
|
||||
@pendingItem = item
|
||||
@emitter.emit 'item-did-terminate-pending-state', mostRecentPendingItem
|
||||
|
||||
getPendingItem: =>
|
||||
@pendingItem or null
|
||||
|
||||
clearPendingItem: =>
|
||||
@setPendingItem(null)
|
||||
|
||||
onItemDidTerminatePendingState: (callback) =>
|
||||
@emitter.on 'item-did-terminate-pending-state', callback
|
||||
|
||||
# Public: Add the given items to the pane.
|
||||
#
|
||||
# * `items` An {Array} of items to add. Items can be views or models with
|
||||
@@ -396,7 +473,8 @@ class Pane extends Model
|
||||
removeItem: (item, moved) ->
|
||||
index = @items.indexOf(item)
|
||||
return if index is -1
|
||||
|
||||
@pendingItem = null if @getPendingItem() is item
|
||||
@removeItemFromStack(item)
|
||||
@emitter.emit 'will-remove-item', {item, index, destroyed: not moved, moved}
|
||||
@unsubscribeFromItem(item)
|
||||
|
||||
@@ -412,6 +490,14 @@ class Pane extends Model
|
||||
@container?.didDestroyPaneItem({item, index, pane: this}) unless moved
|
||||
@destroy() if @items.length is 0 and @config.get('core.destroyEmptyPanes')
|
||||
|
||||
# Remove the given item from the itemStack.
|
||||
#
|
||||
# * `item` The item to remove.
|
||||
# * `index` {Number} indicating the index to which to remove the item from the itemStack.
|
||||
removeItemFromStack: (item) ->
|
||||
index = @itemStack.indexOf(item)
|
||||
@itemStack.splice(index, 1) unless index is -1
|
||||
|
||||
# Public: Move the given item to the given index.
|
||||
#
|
||||
# * `item` The item to move.
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
module.exports = ({commandRegistry, commandInstaller, config}) ->
|
||||
commandRegistry.add 'atom-workspace',
|
||||
'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem()
|
||||
'pane:show-previous-recently-used-item': -> @getModel().getActivePane().activatePreviousRecentlyUsedItem()
|
||||
'pane:move-active-item-to-top-of-stack': -> @getModel().getActivePane().moveActiveItemToTopOfStack()
|
||||
'pane:show-next-item': -> @getModel().getActivePane().activateNextItem()
|
||||
'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem()
|
||||
'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0)
|
||||
|
||||
@@ -97,7 +97,7 @@ class TextEditor extends Model
|
||||
softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation,
|
||||
@mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config,
|
||||
@notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry,
|
||||
@project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles, @autoHeight, @ignoreScrollPastEnd
|
||||
@project, @assert, @applicationDelegate, grammarName, ignoreInvisibles, @autoHeight, @ignoreScrollPastEnd
|
||||
} = params
|
||||
|
||||
throw new Error("Must pass a config parameter when constructing TextEditors") unless @config?
|
||||
@@ -118,6 +118,7 @@ class TextEditor extends Model
|
||||
@selections = []
|
||||
@autoHeight ?= true
|
||||
@ignoreScrollPastEnd ?= false
|
||||
@hasTerminatedPendingState = false
|
||||
|
||||
buffer ?= new TextBuffer
|
||||
@displayBuffer ?= new DisplayBuffer({
|
||||
@@ -161,7 +162,6 @@ class TextEditor extends Model
|
||||
firstVisibleScreenColumn: @getFirstVisibleScreenColumn()
|
||||
displayBuffer: @displayBuffer.serialize()
|
||||
selectionsMarkerLayerId: @selectionsMarkerLayer.id
|
||||
pending: @isPending()
|
||||
|
||||
subscribeToBuffer: ->
|
||||
@buffer.retain()
|
||||
@@ -173,12 +173,18 @@ class TextEditor extends Model
|
||||
@disposables.add @buffer.onDidChangeEncoding =>
|
||||
@emitter.emit 'did-change-encoding', @getEncoding()
|
||||
@disposables.add @buffer.onDidDestroy => @destroy()
|
||||
if @pending
|
||||
@disposables.add @buffer.onDidChangeModified =>
|
||||
@terminatePendingState() if @buffer.isModified()
|
||||
@disposables.add @buffer.onDidChangeModified =>
|
||||
@terminatePendingState() if not @hasTerminatedPendingState and @buffer.isModified()
|
||||
|
||||
@preserveCursorPositionOnBufferReload()
|
||||
|
||||
terminatePendingState: ->
|
||||
@emitter.emit 'did-terminate-pending-state' if not @hasTerminatedPendingState
|
||||
@hasTerminatedPendingState = true
|
||||
|
||||
onDidTerminatePendingState: (callback) ->
|
||||
@emitter.on 'did-terminate-pending-state', callback
|
||||
|
||||
subscribeToDisplayBuffer: ->
|
||||
@disposables.add @selectionsMarkerLayer.onDidCreateMarker @addSelection.bind(this)
|
||||
@disposables.add @displayBuffer.onDidChangeGrammar @handleGrammarChange.bind(this)
|
||||
@@ -585,13 +591,6 @@ class TextEditor extends Model
|
||||
getEditorWidthInChars: ->
|
||||
@displayBuffer.getEditorWidthInChars()
|
||||
|
||||
onDidTerminatePendingState: (callback) ->
|
||||
@emitter.on 'did-terminate-pending-state', callback
|
||||
|
||||
terminatePendingState: ->
|
||||
return if not @pending
|
||||
@pending = false
|
||||
@emitter.emit 'did-terminate-pending-state'
|
||||
|
||||
###
|
||||
Section: File Details
|
||||
@@ -676,9 +675,6 @@ class TextEditor extends Model
|
||||
# Essential: Returns {Boolean} `true` if this editor has no content.
|
||||
isEmpty: -> @buffer.isEmpty()
|
||||
|
||||
# Returns {Boolean} `true` if this editor is pending and `false` if it is permanent.
|
||||
isPending: -> Boolean(@pending)
|
||||
|
||||
# Copies the current file path to the native clipboard.
|
||||
copyPathToClipboard: (relative = false) ->
|
||||
if filePath = @getPath()
|
||||
|
||||
@@ -409,6 +409,9 @@ class Workspace extends Model
|
||||
# containing pane. Defaults to `true`.
|
||||
# * `activateItem` A {Boolean} indicating whether to call {Pane::activateItem}
|
||||
# on containing pane. Defaults to `true`.
|
||||
# * `pending` A {Boolean} indicating whether or not the item should be opened
|
||||
# in a pending state. Existing pending items in a pane are replaced with
|
||||
# new pending items when they are opened.
|
||||
# * `searchAllPanes` A {Boolean}. If `true`, the workspace will attempt to
|
||||
# activate an existing item for the given URI on any pane.
|
||||
# If `false`, only the active pane will be searched for
|
||||
@@ -483,7 +486,7 @@ class Workspace extends Model
|
||||
|
||||
if uri?
|
||||
if item = pane.itemForURI(uri)
|
||||
item.terminatePendingState?() if item.isPending?() and not options.pending
|
||||
pane.clearPendingItem() if not options.pending and pane.getPendingItem() is item
|
||||
item ?= opener(uri, options) for opener in @getOpeners() when not item
|
||||
|
||||
try
|
||||
@@ -506,7 +509,7 @@ class Workspace extends Model
|
||||
return item if pane.isDestroyed()
|
||||
|
||||
@itemOpened(item)
|
||||
pane.activateItem(item) if activateItem
|
||||
pane.activateItem(item, options.pending) if activateItem
|
||||
pane.activate() if activatePane
|
||||
|
||||
initialLine = initialColumn = 0
|
||||
|
||||
Reference in New Issue
Block a user