Merge branch 'master' into ku-crash-recovery

This commit is contained in:
Katrina Uychaco 2016-02-15 21:40:59 -08:00
commit bf264c6a6a
22 changed files with 243 additions and 121 deletions

View File

@ -365,7 +365,7 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and
| `blocked` | [search][search-atom-repo-label-blocked] | [search][search-atom-org-label-blocked] | Issues blocked on other issues. |
| `duplicate` | [search][search-atom-repo-label-duplicate] | [search][search-atom-org-label-duplicate] | Issues which are duplicates of other issues, i.e. they have been reported before. |
| `wontfix` | [search][search-atom-repo-label-wontfix] | [search][search-atom-org-label-wontfix] | The Atom core team has decided not to fix these issues for now, either because they're working as intended or for some other reason. |
| `invalid` | [search][search-atom-repo-label-invalid] | [search][search-atom-org-label-invalid] | Issues which are't valid (e.g. user errors). |
| `invalid` | [search][search-atom-repo-label-invalid] | [search][search-atom-org-label-invalid] | Issues which aren't valid (e.g. user errors). |
| `package-idea` | [search][search-atom-repo-label-package-idea] | [search][search-atom-org-label-package-idea] | Feature request which might be good candidates for new packages, instead of extending Atom or core Atom packages. |
| `wrong-repo` | [search][search-atom-repo-label-wrong-repo] | [search][search-atom-org-label-wrong-repo] | Issues reported on the wrong repository (e.g. a bug related to the [Settings View package](https://github.com/atom/settings-view) was reported on [Atom core](https://github.com/atom/atom)). |

View File

@ -28,7 +28,7 @@
"fs-plus": "^2.8.0",
"fstream": "0.1.24",
"fuzzaldrin": "^2.1",
"git-utils": "^4.1.0",
"git-utils": "^4.1.2",
"grim": "1.5.0",
"jasmine-json": "~0.0",
"jasmine-tagged": "^1.1.4",
@ -54,7 +54,7 @@
"service-hub": "^0.7.0",
"source-map-support": "^0.3.2",
"temp": "0.8.1",
"text-buffer": "8.2.2-2",
"text-buffer": "8.3.0-0",
"typescript-simple": "1.0.0",
"underscore-plus": "^1.6.6",
"yargs": "^3.23.0"
@ -66,56 +66,56 @@
"atom-light-ui": "0.43.0",
"base16-tomorrow-dark-theme": "1.1.0",
"base16-tomorrow-light-theme": "1.1.1",
"one-dark-ui": "1.1.9",
"one-light-ui": "1.1.9",
"one-dark-ui": "1.2.0",
"one-light-ui": "1.2.0",
"one-dark-syntax": "1.2.0",
"one-light-syntax": "1.2.0",
"solarized-dark-syntax": "1.0.0",
"solarized-light-syntax": "1.0.0",
"about": "1.3.0",
"archive-view": "0.61.0",
"about": "1.3.1",
"archive-view": "0.61.1",
"autocomplete-atom-api": "0.10.0",
"autocomplete-css": "0.11.0",
"autocomplete-html": "0.7.2",
"autocomplete-plus": "2.25.0",
"autocomplete-plus": "2.27.1",
"autocomplete-snippets": "1.10.0",
"autoflow": "0.27.0",
"autosave": "0.23.0",
"autosave": "0.23.1",
"background-tips": "0.26.0",
"bookmarks": "0.38.2",
"bracket-matcher": "0.79.0",
"bracket-matcher": "0.79.1",
"command-palette": "0.38.0",
"deprecation-cop": "0.54.0",
"deprecation-cop": "0.54.1",
"dev-live-reload": "0.47.0",
"encoding-selector": "0.21.0",
"exception-reporting": "0.37.0",
"find-and-replace": "0.197.1",
"fuzzy-finder": "0.94.0",
"git-diff": "0.57.0",
"find-and-replace": "0.197.2",
"fuzzy-finder": "0.94.1",
"git-diff": "0.57.1",
"go-to-line": "0.30.0",
"grammar-selector": "0.48.0",
"grammar-selector": "0.48.1",
"image-view": "0.56.0",
"incompatible-packages": "0.25.0",
"incompatible-packages": "0.25.1",
"keybinding-resolver": "0.33.0",
"line-ending-selector": "0.3.0",
"line-ending-selector": "0.3.1",
"link": "0.31.0",
"markdown-preview": "0.157.2",
"markdown-preview": "0.157.3",
"metrics": "0.53.1",
"notifications": "0.62.1",
"open-on-github": "0.41.0",
"package-generator": "0.41.0",
"settings-view": "0.232.3",
"notifications": "0.62.2",
"open-on-github": "0.41.1",
"package-generator": "0.41.1",
"settings-view": "0.232.4",
"snippets": "1.0.1",
"spell-check": "0.65.0",
"status-bar": "0.83.0",
"styleguide": "0.45.1",
"symbols-view": "0.110.1",
"tabs": "0.90.0",
"timecop": "0.33.0",
"tree-view": "0.201.0",
"spell-check": "0.66.1",
"status-bar": "0.83.1",
"styleguide": "0.45.2",
"symbols-view": "0.111.1",
"tabs": "0.90.2",
"timecop": "0.33.1",
"tree-view": "0.201.2",
"update-package-dependencies": "0.10.0",
"welcome": "0.33.0",
"whitespace": "0.32.1",
"welcome": "0.33.1",
"whitespace": "0.32.2",
"wrap-guide": "0.38.1",
"language-c": "0.51.1",
"language-clojure": "0.19.1",
@ -125,7 +125,7 @@
"language-gfm": "0.84.0",
"language-git": "0.12.1",
"language-go": "0.42.0",
"language-html": "0.44.0",
"language-html": "0.44.1",
"language-hyperlink": "0.16.0",
"language-java": "0.17.0",
"language-javascript": "0.110.0",
@ -138,7 +138,7 @@
"language-php": "0.37.0",
"language-property-list": "0.8.0",
"language-python": "0.43.0",
"language-ruby": "0.68.0",
"language-ruby": "0.68.1",
"language-ruby-on-rails": "0.25.0",
"language-sass": "0.45.0",
"language-shellscript": "0.21.0",
@ -147,7 +147,7 @@
"language-text": "0.7.0",
"language-todo": "0.27.0",
"language-toml": "0.18.0",
"language-xml": "0.34.2",
"language-xml": "0.34.3",
"language-yaml": "0.25.1"
},
"private": true,

View File

@ -1,6 +0,0 @@
module.exports = function (state) {
return {
wasDeserializedBy: 'Deserializer1',
state: state
}
}

View File

@ -1,6 +0,0 @@
module.exports = function (state) {
return {
wasDeserializedBy: 'Deserializer2',
state: state
}
}

View File

@ -1,3 +1,17 @@
module.exports = {
activate: function() {}
activate () {},
deserializeMethod1 (state) {
return {
wasDeserializedBy: 'deserializeMethod1',
state: state
}
},
deserializeMethod2 (state) {
return {
wasDeserializedBy: 'deserializeMethod2',
state: state
}
}
}

View File

@ -3,7 +3,7 @@
"version": "1.0.0",
"main": "./index",
"deserializers": {
"Deserializer1": "./deserializer-1.js",
"Deserializer2": "./deserializer-2.js"
"Deserializer1": "deserializeMethod1",
"Deserializer2": "deserializeMethod2"
}
}

View File

@ -1,3 +0,0 @@
module.exports = function (state) {
return {state: state}
}

View File

@ -1,3 +1,25 @@
'use strict'
module.exports = {
activate: function() {}
activate () {},
theDeserializerMethod (state) {
return {state: state}
},
viewProviderMethod1 (model) {
if (model.worksWithViewProvider1) {
let element = document.createElement('div')
element.dataset['createdBy'] = 'view-provider-1'
return element
}
},
viewProviderMethod2 (model) {
if (model.worksWithViewProvider2) {
let element = document.createElement('div')
element.dataset['createdBy'] = 'view-provider-2'
return element
}
}
}

View File

@ -3,10 +3,10 @@
"main": "./index",
"version": "1.0.0",
"deserializers": {
"DeserializerFromPackageWithViewProviders": "./deserializer"
"DeserializerFromPackageWithViewProviders": "theDeserializerMethod"
},
"viewProviders": [
"./view-provider-1",
"./view-provider-2"
"viewProviderMethod1",
"viewProviderMethod2"
]
}

View File

@ -1,9 +0,0 @@
'use strict'
module.exports = function (model) {
if (model.worksWithViewProvider1) {
let element = document.createElement('div')
element.dataset['createdBy'] = 'view-provider-1'
return element
}
}

View File

@ -1,9 +0,0 @@
'use strict'
module.exports = function (model) {
if (model.worksWithViewProvider2) {
let element = document.createElement('div')
element.dataset['createdBy'] = 'view-provider-2'
return element
}
}

View File

@ -338,10 +338,10 @@ describe('GitRepositoryAsync', () => {
})
describe('.refreshStatus()', () => {
let newPath, modifiedPath, cleanPath
let newPath, modifiedPath, cleanPath, workingDirectory
beforeEach(() => {
const workingDirectory = copyRepository()
workingDirectory = copyRepository()
repo = GitRepositoryAsync.open(workingDirectory)
modifiedPath = path.join(workingDirectory, 'file.txt')
newPath = path.join(workingDirectory, 'untracked.txt')
@ -362,7 +362,7 @@ describe('GitRepositoryAsync', () => {
describe('in a repository with submodules', () => {
beforeEach(() => {
const workingDirectory = copySubmoduleRepository()
workingDirectory = copySubmoduleRepository()
repo = GitRepositoryAsync.open(workingDirectory)
modifiedPath = path.join(workingDirectory, 'jstips', 'README.md')
newPath = path.join(workingDirectory, 'You-Dont-Need-jQuery', 'untracked.txt')
@ -380,6 +380,48 @@ describe('GitRepositoryAsync', () => {
expect(repo.isStatusModified(await repo.getCachedPathStatus(modifiedPath))).toBe(true)
})
})
it('caches the proper statuses when a subdir is open', async () => {
const subDir = path.join(workingDirectory, 'dir')
fs.mkdirSync(subDir)
const filePath = path.join(subDir, 'b.txt')
fs.writeFileSync(filePath, '')
atom.project.setPaths([subDir])
await atom.workspace.open('b.txt')
const repo = atom.project.getRepositories()[0].async
await repo.refreshStatus()
const status = await repo.getCachedPathStatus(filePath)
expect(repo.isStatusModified(status)).toBe(false)
expect(repo.isStatusNew(status)).toBe(false)
})
it('caches the proper statuses when multiple project are open', async () => {
const otherWorkingDirectory = copyRepository()
atom.project.setPaths([workingDirectory, otherWorkingDirectory])
await atom.workspace.open('b.txt')
const repo = atom.project.getRepositories()[0].async
await repo.refreshStatus()
const subDir = path.join(workingDirectory, 'dir')
fs.mkdirSync(subDir)
const filePath = path.join(subDir, 'b.txt')
fs.writeFileSync(filePath, 'some content!')
const status = await repo.getCachedPathStatus(filePath)
expect(repo.isStatusModified(status)).toBe(true)
expect(repo.isStatusNew(status)).toBe(false)
})
})
describe('.isProjectAtRoot()', () => {

View File

@ -205,7 +205,7 @@ describe "GitRepository", ->
expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe true
describe ".refreshStatus()", ->
[newPath, modifiedPath, cleanPath, originalModifiedPathText] = []
[newPath, modifiedPath, cleanPath, originalModifiedPathText, workingDirectory] = []
beforeEach ->
workingDirectory = copyRepository()
@ -231,6 +231,64 @@ describe "GitRepository", ->
expect(repo.isStatusNew(repo.getCachedPathStatus(newPath))).toBeTruthy()
expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy()
it 'caches the proper statuses when a subdir is open', ->
subDir = path.join(workingDirectory, 'dir')
fs.mkdirSync(subDir)
filePath = path.join(subDir, 'b.txt')
fs.writeFileSync(filePath, '')
atom.project.setPaths([subDir])
waitsForPromise ->
atom.workspace.open('b.txt')
statusHandler = null
runs ->
repo = atom.project.getRepositories()[0]
statusHandler = jasmine.createSpy('statusHandler')
repo.onDidChangeStatuses statusHandler
repo.refreshStatus()
waitsFor ->
statusHandler.callCount > 0
runs ->
status = repo.getCachedPathStatus(filePath)
expect(repo.isStatusModified(status)).toBe false
expect(repo.isStatusNew(status)).toBe false
it 'caches the proper statuses when multiple project are open', ->
otherWorkingDirectory = copyRepository()
atom.project.setPaths([workingDirectory, otherWorkingDirectory])
waitsForPromise ->
atom.workspace.open('b.txt')
statusHandler = null
runs ->
repo = atom.project.getRepositories()[0]
statusHandler = jasmine.createSpy('statusHandler')
repo.onDidChangeStatuses statusHandler
repo.refreshStatus()
waitsFor ->
statusHandler.callCount > 0
runs ->
subDir = path.join(workingDirectory, 'dir')
fs.mkdirSync(subDir)
filePath = path.join(subDir, 'b.txt')
fs.writeFileSync(filePath, '')
status = repo.getCachedPathStatus(filePath)
expect(repo.isStatusModified(status)).toBe true
expect(repo.isStatusNew(status)).toBe false
describe "buffer events", ->
[editor] = []

View File

@ -88,18 +88,16 @@ describe "PackageManager", ->
state1 = {deserializer: 'Deserializer1', a: 'b'}
expect(atom.deserializers.deserialize(state1)).toEqual {
wasDeserializedBy: 'Deserializer1'
wasDeserializedBy: 'deserializeMethod1'
state: state1
}
state2 = {deserializer: 'Deserializer2', c: 'd'}
expect(atom.deserializers.deserialize(state2)).toEqual {
wasDeserializedBy: 'Deserializer2'
wasDeserializedBy: 'deserializeMethod2'
state: state2
}
expect(pack.mainModule).toBeNull()
describe "when there are view providers specified in the package's package.json", ->
model1 = {worksWithViewProvider1: true}
model2 = {worksWithViewProvider2: true}

View File

@ -92,10 +92,10 @@ describe "TextEditorPresenter", ->
waitsForStateToUpdate = (presenter, fn) ->
waitsFor "presenter state to update", 1000, (done) ->
fn?()
disposable = presenter.onDidUpdateState ->
disposable.dispose()
process.nextTick(done)
fn?()
tiledContentContract = (stateFn) ->
it "contains states for tiles that are visible on screen", ->
@ -1336,9 +1336,9 @@ describe "TextEditorPresenter", ->
presenter = buildPresenter()
blockDecoration2 = addBlockDecorationBeforeScreenRow(3)
blockDecoration3 = addBlockDecorationBeforeScreenRow(7)
blockDecoration4 = addBlockDecorationAfterScreenRow(7)
blockDecoration4 = null
waitsForStateToUpdate presenter
waitsForStateToUpdate presenter, blockDecoration4 = addBlockDecorationAfterScreenRow(7)
runs ->
expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual([blockDecoration1])
expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual([])
@ -1472,9 +1472,9 @@ describe "TextEditorPresenter", ->
decoration1 = editor.decorateMarker(marker1, type: 'line', class: 'a')
presenter = buildPresenter()
marker2 = editor.addMarkerLayer(maintainHistory: true).markBufferRange([[4, 0], [6, 2]], invalidate: 'touch')
decoration2 = editor.decorateMarker(marker2, type: 'line', class: 'b')
decoration2 = null
waitsForStateToUpdate presenter
waitsForStateToUpdate presenter, -> decoration2 = editor.decorateMarker(marker2, type: 'line', class: 'b')
runs ->
expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull()
expect(lineStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b']
@ -2150,31 +2150,40 @@ describe "TextEditorPresenter", ->
}
# becoming empty
waitsForStateToUpdate presenter, -> editor.getSelections()[1].clear(autoscroll: false)
runs ->
editor.getSelections()[1].clear(autoscroll: false)
waitsForStateToUpdate presenter
runs ->
expectUndefinedStateForSelection(presenter, 1)
# becoming non-empty
waitsForStateToUpdate presenter, -> editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false)
runs ->
editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false)
waitsForStateToUpdate presenter
runs ->
expectValues stateForSelectionInTile(presenter, 1, 2), {
regions: [{top: 0, left: 4 * 10, width: 2 * 10, height: 10}]
}
# moving out of view
waitsForStateToUpdate presenter, -> editor.getSelections()[1].setBufferRange([[3, 4], [3, 6]], autoscroll: false)
runs ->
editor.getSelections()[1].setBufferRange([[3, 4], [3, 6]], autoscroll: false)
waitsForStateToUpdate presenter
runs ->
expectUndefinedStateForSelection(presenter, 1)
# adding
waitsForStateToUpdate presenter, -> editor.addSelectionForBufferRange([[1, 4], [1, 6]], autoscroll: false)
runs -> editor.addSelectionForBufferRange([[1, 4], [1, 6]], autoscroll: false)
waitsForStateToUpdate presenter
runs ->
expectValues stateForSelectionInTile(presenter, 2, 0), {
regions: [{top: 10, left: 4 * 10, width: 2 * 10, height: 10}]
}
# moving added selection
waitsForStateToUpdate presenter, -> editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]], autoscroll: false)
runs ->
editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]], autoscroll: false)
waitsForStateToUpdate presenter
destroyedSelection = null
runs ->
@ -2208,8 +2217,9 @@ describe "TextEditorPresenter", ->
presenter = buildPresenter(explicitHeight: 30, scrollTop: 20, tileSize: 2)
marker = editor.markBufferPosition([2, 2])
highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a')
highlight = null
waitsForStateToUpdate presenter, ->
highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a')
marker.setBufferRange([[2, 2], [5, 2]])
highlight.flash('b', 500)
runs ->
@ -2969,9 +2979,8 @@ describe "TextEditorPresenter", ->
presenter.setBlockDecorationDimensions(blockDecoration4, 0, 35)
presenter.setBlockDecorationDimensions(blockDecoration4, 0, 40)
presenter.setBlockDecorationDimensions(blockDecoration5, 0, 50)
presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60)
waitsForStateToUpdate presenter
waitsForStateToUpdate presenter, -> presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60)
runs ->
expect(lineNumberStateForScreenRow(presenter, 0).blockDecorationsHeight).toBe(10)
expect(lineNumberStateForScreenRow(presenter, 1).blockDecorationsHeight).toBe(0)
@ -3460,9 +3469,9 @@ describe "TextEditorPresenter", ->
gutterName: 'test-gutter-2'
class: 'test-class'
marker4 = editor.markBufferRange([[0, 0], [1, 0]])
decoration4 = editor.decorateMarker(marker4, decorationParams)
decoration4 = null
waitsForStateToUpdate presenter
waitsForStateToUpdate presenter, -> decoration4 = editor.decorateMarker(marker4, decorationParams)
runs ->
expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'})

View File

@ -139,6 +139,15 @@ describe "TextEditor", ->
expect(editor2.getSoftTabs()).toBe true
expect(editor2.getEncoding()).toBe 'macroman'
atom.config.set('editor.tabLength', -1)
expect(editor2.getTabLength()).toBe 1
atom.config.set('editor.tabLength', 2)
expect(editor2.getTabLength()).toBe 2
atom.config.set('editor.tabLength', 17)
expect(editor2.getTabLength()).toBe 17
atom.config.set('editor.tabLength', 128)
expect(editor2.getTabLength()).toBe 128
it "uses scoped `core.fileEncoding` values", ->
editor1 = null
editor2 = null

View File

@ -171,7 +171,7 @@ module.exports =
tabLength:
type: 'integer'
default: 2
enum: [1, 2, 3, 4, 6, 8]
minimum: 1
description: 'Number of spaces used to represent a tab.'
softWrap:
type: 'boolean'

View File

@ -456,7 +456,10 @@ export default class GitRepositoryAsync {
// information.
getDirectoryStatus (directoryPath) {
return this.relativizeToWorkingDirectory(directoryPath)
.then(relativePath => this._getStatus([relativePath]))
.then(relativePath => {
const pathspec = relativePath + '/**'
return this._getStatus([pathspec])
})
.then(statuses => {
return Promise.all(statuses.map(s => s.statusBit())).then(bits => {
return bits
@ -800,7 +803,7 @@ export default class GitRepositoryAsync {
}
return Promise.all(projectPathsPromises)
.then(paths => paths.filter(p => p.length > 0))
.then(paths => paths.map(p => p.length > 0 ? p + '/**' : '*'))
.then(projectPaths => {
return this._getStatus(projectPaths.length > 0 ? projectPaths : null)
})
@ -1032,7 +1035,7 @@ export default class GitRepositoryAsync {
return this.getRepo()
.then(repo => {
const opts = {
flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS | Git.Status.OPT.DISABLE_PATHSPEC_MATCH
flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS
}
if (paths) {

View File

@ -484,7 +484,7 @@ class GitRepository
relativeProjectPaths = @project?.getPaths()
.map (path) => @relativize(path)
.filter (path) -> path.length > 0
.map (path) -> if path.length > 0 then path + '/**' else '*'
@statusTask?.terminate()
@statusTask = Task.once @handlerPath, @getPath(), relativeProjectPaths, ({statuses, upstream, branch, submodules}) =>

View File

@ -84,7 +84,7 @@ class Package
@loadKeymaps()
@loadMenus()
@loadStylesheets()
@loadDeserializers()
@registerDeserializerMethods()
@configSchemaRegisteredOnLoad = @registerConfigSchemaFromMetadata()
@settingsPromise = @loadSettings()
if @shouldRequireMainModuleOnLoad() and not @mainModule?
@ -277,24 +277,24 @@ class Package
@stylesheets = @getStylesheetPaths().map (stylesheetPath) =>
[stylesheetPath, @themeManager.loadStylesheet(stylesheetPath, true)]
loadDeserializers: ->
registerDeserializerMethods: ->
if @metadata.deserializers?
for name, implementationPath of @metadata.deserializers
do =>
deserializePath = path.join(@path, implementationPath)
deserializeFunction = null
atom.deserializers.add
name: name,
deserialize: =>
@registerViewProviders()
deserializeFunction ?= require(deserializePath)
deserializeFunction.apply(this, arguments)
Object.keys(@metadata.deserializers).forEach (deserializerName) =>
methodName = @metadata.deserializers[deserializerName]
atom.deserializers.add
name: deserializerName,
deserialize: (state, atomEnvironment) =>
@registerViewProviders()
@requireMainModule()
@mainModule[methodName](state, atomEnvironment)
return
registerViewProviders: ->
if @metadata.viewProviders? and not @registeredViewProviders
for implementationPath in @metadata.viewProviders
@viewRegistry.addViewProvider(require(path.join(@path, implementationPath)))
@requireMainModule()
@metadata.viewProviders.forEach (methodName) =>
@viewRegistry.addViewProvider (model) =>
@mainModule[methodName](model)
@registeredViewProviders = true
getStylesheetsPath: ->

View File

@ -713,7 +713,7 @@ class Pane extends Model
if @parent.orientation is 'vertical'
bottommostSibling = last(@parent.children)
if bottommostSibling instanceof PaneAxis
@splitRight()
@splitDown()
else
bottommostSibling
else

View File

@ -394,11 +394,11 @@ class Workspace extends Model
# initially. Defaults to `0`.
# * `initialColumn` A {Number} indicating which column to move the cursor to
# initially. Defaults to `0`.
# * `split` Either 'left', 'right', 'top' or 'bottom'.
# * `split` Either 'left', 'right', 'up' or 'down'.
# If 'left', the item will be opened in leftmost pane of the current active pane's row.
# If 'right', the item will be opened in the rightmost pane of the current active pane's row.
# If 'up', the item will be opened in topmost pane of the current active pane's row.
# If 'down', the item will be opened in the bottommost pane of the current active pane's row.
# If 'right', the item will be opened in the rightmost pane of the current active pane's row. If only one pane exists in the row, a new pane will be created.
# If 'up', the item will be opened in topmost pane of the current active pane's column.
# If 'down', the item will be opened in the bottommost pane of the current active pane's column. If only one pane exists in the column, a new pane will be created.
# * `activatePane` A {Boolean} indicating whether to call {Pane::activate} on
# containing pane. Defaults to `true`.
# * `activateItem` A {Boolean} indicating whether to call {Pane::activateItem}