Merge branch 'master' into as-tiled-gutter

This commit is contained in:
Antonio Scandurra
2015-06-11 18:38:31 +02:00
22 changed files with 355 additions and 66 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "atom",
"productName": "Atom",
"version": "0.208.0",
"version": "0.209.0",
"description": "A hackable text editor for the 21st Century.",
"main": "./src/browser/main.js",
"repository": {
@@ -32,7 +32,7 @@
"delegato": "^1",
"emissary": "^1.3.3",
"event-kit": "^1.2.0",
"first-mate": "^4.1.6",
"first-mate": "^4.1.7",
"fs-plus": "^2.8.0",
"fstream": "0.1.24",
"fuzzaldrin": "^2.1",
@@ -57,14 +57,14 @@
"scandal": "2.0.3",
"scoped-property-store": "^0.17.0",
"scrollbar-style": "^3.1",
"season": "^5.1.4",
"season": "^5.3",
"semver": "^4.3.3",
"serializable": "^1",
"service-hub": "^0.5.0",
"space-pen": "3.8.2",
"stacktrace-parser": "0.1.1",
"temp": "0.8.1",
"text-buffer": "6.3.2",
"text-buffer": "6.3.6",
"theorist": "^1.0.2",
"typescript-simple": "1.0.0",
"underscore-plus": "^1.6.6",
@@ -120,17 +120,17 @@
"status-bar": "0.74.0",
"styleguide": "0.44.0",
"symbols-view": "0.98.0",
"tabs": "0.76.0",
"tabs": "0.80.0",
"timecop": "0.31.0",
"tree-view": "0.172.0",
"update-package-dependencies": "0.10.0",
"welcome": "0.28.0",
"welcome": "0.29.0",
"whitespace": "0.30.0",
"wrap-guide": "0.35.0",
"language-c": "0.45.0",
"language-clojure": "0.15.0",
"language-coffee-script": "0.41.0",
"language-csharp": "0.5.0",
"language-csharp": "0.6.0",
"language-css": "0.30.0",
"language-gfm": "0.77.0",
"language-git": "0.10.0",

View File

@@ -1,3 +1,4 @@
#!/bin/sh
"$0/../../app/apm/bin/node.exe" "$0/../../app/apm/lib/cli.js" "$@"
directory=$(dirname "$0")
"$directory/../app/apm/bin/node.exe" "$directory/../app/apm/lib/cli.js" "$@"

View File

@@ -15,8 +15,10 @@ while getopts ":fhtvw-:" opt; do
esac
done
directory=$(dirname "$0")
if [ $EXPECT_OUTPUT ]; then
"$0/../../../atom.exe" "$@"
"$directory/../../atom.exe" "$@"
else
"$0/../../app/apm/bin/node.exe" "$0/../atom.js" "$@"
"$directory/../app/apm/bin/node.exe" "$directory/atom.js" "$@"
fi

View File

@@ -0,0 +1,4 @@
{
"name": "package-with-a-totally-different-name",
"version": "1.0.0"
}

View File

@@ -1,5 +1,5 @@
{
"name": "package-with-invalid-selectors",
"name": "package-with-invalid-activation-commands",
"version": "1.0.0",
"activationCommands": {
"<>": [

View File

@@ -1 +1 @@
'main': 'main-module.coffee'
'main': 'main-module.coffee'

View File

@@ -105,3 +105,13 @@ describe "Package", ->
theme.onDidDeactivate spy = jasmine.createSpy()
theme.deactivate()
expect(spy).toHaveBeenCalled()
describe ".loadMetadata()", ->
[packagePath, pack, metadata] = []
beforeEach ->
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-different-directory-name')
metadata = Package.loadMetadata(packagePath, true)
it "uses the package name defined in package.json", ->
expect(metadata.name).toBe 'package-with-a-totally-different-name'

View File

@@ -154,3 +154,9 @@ describe "PaneContainerElement", ->
container.destroy()
expect(element.resizeStopped.callCount).toBe 1
expect(document.removeEventListener.callCount).toBe 2
it "does not throw an error when resized to fit content in a detached state", ->
container.getActivePane().splitRight()
element = getResizeElement(0)
element.remove()
expect(-> element.resizeToFitContent()).not.toThrow()

View File

@@ -70,3 +70,29 @@ describe "Task", ->
task.terminate()
expect(stdout.listeners('data').length).toBe 0
expect(stderr.listeners('data').length).toBe 0
describe "::cancel()", ->
it "dispatches 'task:cancelled' when invoked on an active task", ->
task = new Task(require.resolve('./fixtures/task-spec-handler'))
cancelledEventSpy = jasmine.createSpy('eventSpy')
task.on('task:cancelled', cancelledEventSpy)
completedEventSpy = jasmine.createSpy('eventSpy')
task.on('task:completed', completedEventSpy)
expect(task.cancel()).toBe(true)
expect(cancelledEventSpy).toHaveBeenCalled()
expect(completedEventSpy).not.toHaveBeenCalled()
it "does not dispatch 'task:cancelled' when invoked on an inactive task", ->
handlerResult = null
task = Task.once require.resolve('./fixtures/task-spec-handler'), (result) ->
handlerResult = result
waitsFor ->
handlerResult?
runs ->
cancelledEventSpy = jasmine.createSpy('eventSpy')
task.on('task:cancelled', cancelledEventSpy)
expect(task.cancel()).toBe(false)
expect(cancelledEventSpy).not.toHaveBeenCalled()

View File

@@ -1327,6 +1327,10 @@ describe "TextEditorPresenter", ->
marker8 = editor.markBufferRange([[2, 2], [2, 2]])
highlight8 = editor.decorateMarker(marker8, type: 'highlight', class: 'h')
# partially off-screen above, empty
marker9 = editor.markBufferRange([[0, 0], [2, 0]], invalidate: 'touch')
highlight9 = editor.decorateMarker(marker9, type: 'highlight', class: 'h')
presenter = buildPresenter(explicitHeight: 30, scrollTop: 20, tileSize: 2)
expectUndefinedStateForHighlight(presenter, highlight1)
@@ -1388,6 +1392,7 @@ describe "TextEditorPresenter", ->
expectUndefinedStateForHighlight(presenter, highlight7)
expectUndefinedStateForHighlight(presenter, highlight8)
expectUndefinedStateForHighlight(presenter, highlight9)
it "is empty until all of the required measurements are assigned", ->
editor.setSelectedBufferRanges([

View File

@@ -938,6 +938,92 @@ describe "Workspace", ->
.then ->
expect(resultPaths).toEqual([file2])
describe "when a custom directory searcher is registered", ->
fakeSearch = null
# Function that is invoked once all of the fields on fakeSearch are set.
onFakeSearchCreated = null
class FakeSearch
constructor: (@options) ->
# Note that hoisting resolve and reject in this way is generally frowned upon.
@promise = new Promise (resolve, reject) =>
@hoistedResolve = resolve
@hoistedReject = reject
onFakeSearchCreated?(this)
then: (args...) ->
@promise.then.apply(@promise, args)
cancel: ->
@cancelled = true
# According to the spec for a DirectorySearcher, invoking `cancel()` should
# resolve the thenable rather than reject it.
@hoistedResolve()
beforeEach ->
fakeSearch = null
onFakeSearchCreated = null
atom.packages.serviceHub.provide('atom.directory-searcher', '0.1.0', {
canSearchDirectory: (directory) -> directory.getPath() is dir1
search: (directory, regex, options) -> fakeSearch = new FakeSearch(options)
})
it "can override the DefaultDirectorySearcher on a per-directory basis", ->
foreignFilePath = 'ssh://foreign-directory:8080/hello.txt'
numPathsSearchedInDir2 = 1
numPathsToPretendToSearchInCustomDirectorySearcher = 10
searchResult =
filePath: foreignFilePath,
matches: [
{
lineText: 'Hello world',
lineTextOffset: 0,
matchText: 'Hello',
range: [[0, 0], [0, 5]],
},
]
onFakeSearchCreated = (fakeSearch) ->
fakeSearch.options.didMatch(searchResult)
fakeSearch.options.didSearchPaths(numPathsToPretendToSearchInCustomDirectorySearcher)
fakeSearch.hoistedResolve()
resultPaths = []
onPathsSearched = jasmine.createSpy('onPathsSearched')
waitsForPromise ->
atom.workspace.scan /aaaa/, {onPathsSearched}, ({filePath}) ->
resultPaths.push(filePath)
runs ->
expect(resultPaths.sort()).toEqual([foreignFilePath, file2].sort())
# onPathsSearched should be called once by each DirectorySearcher. The order is not
# guaranteed, so we can only verify the total number of paths searched is correct
# after the second call.
expect(onPathsSearched.callCount).toBe(2)
expect(onPathsSearched.mostRecentCall.args[0]).toBe(
numPathsToPretendToSearchInCustomDirectorySearcher + numPathsSearchedInDir2)
it "can be cancelled when the object returned by scan() has its cancel() method invoked", ->
thenable = atom.workspace.scan /aaaa/, ->
expect(fakeSearch.cancelled).toBe(undefined)
thenable.cancel()
expect(fakeSearch.cancelled).toBe(true)
resultOfPromiseSearch = null
waitsForPromise ->
thenable.then (promiseResult) -> resultOfPromiseSearch = promiseResult
runs ->
expect(resultOfPromiseSearch).toBe('cancelled')
it "will have the side-effect of failing the overall search if it fails", ->
cancelableSearch = atom.workspace.scan /aaaa/, ->
fakeSearch.hoistedReject()
didReject = false
waitsForPromise ->
cancelableSearch.catch -> didReject = true
runs ->
expect(didReject).toBe(true)
describe "::replace(regex, replacementText, paths, iterator)", ->
[filePath, commentFilePath, sampleContent, sampleCommentContent] = []

View File

@@ -472,9 +472,13 @@ class Atom extends Model
ipc.send('call-window-method', 'restart')
# Extended: Returns a {Boolean} true when the current window is maximized.
isMaximixed: ->
isMaximized: ->
@getCurrentWindow().isMaximized()
isMaximixed: ->
deprecate "Use atom.isMaximized() instead"
@isMaximized()
maximize: ->
ipc.send('call-window-method', 'maximize')
@@ -501,9 +505,9 @@ class Atom extends Model
displayWindow: ->
dimensions = @restoreWindowDimensions()
@show()
@focus()
setImmediate =>
@focus()
@setFullScreen(true) if @workspace?.fullScreen
@maximize() if dimensions?.maximized and process.platform isnt 'darwin'

View File

@@ -87,7 +87,7 @@ class AtomWindow
hash: encodeURIComponent(JSON.stringify(loadSettings))
getLoadSettings: ->
if @browserWindow.webContents.loaded
if @browserWindow.webContents?.loaded
hash = url.parse(@browserWindow.webContents.getUrl()).hash.substr(1)
JSON.parse(decodeURIComponent(hash))
@@ -166,7 +166,6 @@ class AtomWindow
openLocations: (locationsToOpen) ->
if @loaded
@focus()
@sendMessage 'open-locations', locationsToOpen
else
@browserWindow.once 'window:loaded', => @openLocations(locationsToOpen)

View File

@@ -139,7 +139,7 @@ addCommandsToPath = (callback) ->
atomShCommandPath = path.join(binFolder, 'atom')
relativeAtomShPath = path.relative(binFolder, path.join(appFolder, 'resources', 'cli', 'atom.sh'))
atomShCommand = "#!/bin/sh\r\n\"$0/../#{relativeAtomShPath.replace(/\\/g, '/')}\" \"$@\""
atomShCommand = "#!/bin/sh\r\n\"$(dirname \"$0\")/#{relativeAtomShPath.replace(/\\/g, '/')}\" \"$@\""
apmCommandPath = path.join(binFolder, 'apm.cmd')
relativeApmPath = path.relative(binFolder, path.join(process.resourcesPath, 'app', 'apm', 'bin', 'apm.cmd'))
@@ -147,7 +147,7 @@ addCommandsToPath = (callback) ->
apmShCommandPath = path.join(binFolder, 'apm')
relativeApmShPath = path.relative(binFolder, path.join(appFolder, 'resources', 'cli', 'apm.sh'))
apmShCommand = "#!/bin/sh\r\n\"$0/../#{relativeApmShPath.replace(/\\/g, '/')}\" \"$@\""
apmShCommand = "#!/bin/sh\r\n\"$(dirname \"$0\")/#{relativeApmShPath.replace(/\\/g, '/')}\" \"$@\""
fs.writeFile atomCommandPath, atomCommand, ->
fs.writeFile atomShCommandPath, atomShCommand, ->

View File

@@ -0,0 +1,95 @@
Task = require './task'
# Public: Searches local files for lines matching a specified regex.
#
# Implements thenable so it can be used with `Promise.all()`.
class DirectorySearch
constructor: (rootPaths, regex, options) ->
scanHandlerOptions =
ignoreCase: regex.ignoreCase
inclusions: options.inclusions
includeHidden: options.includeHidden
excludeVcsIgnores: options.excludeVcsIgnores
exclusions: options.exclusions
follow: options.follow
@task = new Task(require.resolve('./scan-handler'))
@task.on 'scan:result-found', options.didMatch
@task.on 'scan:file-error', options.didError
@task.on 'scan:paths-searched', options.didSearchPaths
@promise = new Promise (resolve, reject) =>
@task.on('task:cancelled', reject)
@task.start(rootPaths, regex.source, scanHandlerOptions, resolve)
# Public: Implementation of `then()` to satisfy the *thenable* contract.
# This makes it possible to use a `DirectorySearch` with `Promise.all()`.
#
# Returns `Promise`.
then: (args...) ->
@promise.then.apply(@promise, args)
# Public: Cancels the search.
cancel: ->
# This will cause @promise to reject.
@task.cancel()
null
# Default provider for the `atom.directory-searcher` service.
module.exports =
class DefaultDirectorySearcher
# Public: Determines whether this object supports search for a `Directory`.
#
# * `directory` {Directory} whose search needs might be supported by this object.
#
# Returns a `boolean` indicating whether this object can search this `Directory`.
canSearchDirectory: (directory) -> true
# Public: Performs a text search for files in the specified `Directory`, subject to the
# specified parameters.
#
# Results are streamed back to the caller by invoking methods on the specified `options`,
# such as `didMatch` and `didError`.
#
# * `directories` {Array} of {Directory} objects to search, all of which have been accepted by
# this searcher's `canSearchDirectory()` predicate.
# * `regex` {RegExp} to search with.
# * `options` {Object} with the following properties:
# * `didMatch` {Function} call with a search result structured as follows:
# * `searchResult` {Object} with the following keys:
# * `filePath` {String} absolute path to the matching file.
# * `matches` {Array} with object elements with the following keys:
# * `lineText` {String} The full text of the matching line (without a line terminator character).
# * `lineTextOffset` {Number} (This always seems to be 0?)
# * `matchText` {String} The text that matched the `regex` used for the search.
# * `range` {Range} Identifies the matching region in the file. (Likely as an array of numeric arrays.)
# * `didError` {Function} call with an Error if there is a problem during the search.
# * `didSearchPaths` {Function} periodically call with the number of paths searched thus far.
# * `inclusions` {Array} of glob patterns (as strings) to search within. Note that this
# array may be empty, indicating that all files should be searched.
#
# Each item in the array is a file/directory pattern, e.g., `src` to search in the "src"
# directory or `*.js` to search all JavaScript files. In practice, this often comes from the
# comma-delimited list of patterns in the bottom text input of the ProjectFindView dialog.
# * `ignoreHidden` {boolean} whether to ignore hidden files.
# * `excludeVcsIgnores` {boolean} whether to exclude VCS ignored paths.
# * `exclusions` {Array} similar to inclusions
# * `follow` {boolean} whether symlinks should be followed.
#
# Returns a *thenable* `DirectorySearch` that includes a `cancel()` method. If `cancel()` is
# invoked before the `DirectorySearch` is determined, it will resolve the `DirectorySearch`.
search: (directories, regex, options) ->
rootPaths = directories.map (directory) -> directory.getPath()
isCancelled = false
directorySearch = new DirectorySearch(rootPaths, regex, options)
promise = new Promise (resolve, reject) ->
directorySearch.then resolve, ->
if isCancelled
resolve()
else
reject()
return {
then: promise.then.bind(promise)
cancel: ->
isCancelled = true
directorySearch.cancel()
}

View File

@@ -45,7 +45,8 @@ class Package
throw error unless ignoreErrors
metadata ?= {}
metadata.name = packageName
unless typeof metadata.name is 'string' and metadata.name.length > 0
metadata.name = packageName
if includeDeprecatedAPIs and metadata.stylesheetMain?
deprecate("Use the `mainStyleSheet` key instead of `stylesheetMain` in the `package.json` of `#{packageName}`", {packageName})

View File

@@ -17,8 +17,8 @@ class PaneResizeHandleElement extends HTMLElement
resizeToFitContent: ->
# clear flex-grow css style of both pane
@previousSibling.model.setFlexScale(1)
@nextSibling.model.setFlexScale(1)
@previousSibling?.model.setFlexScale(1)
@nextSibling?.model.setFlexScale(1)
resizeStarted: (e) ->
e.stopPropagation()

View File

@@ -84,7 +84,7 @@ class Selection extends Model
# Public: Modifies the buffer {Range} for the selection.
#
# * `screenRange` The new {Range} to select.
# * `bufferRange` The new {Range} to select.
# * `options` (optional) {Object} with the keys:
# * `preserveFolds` if `true`, the fold settings are preserved after the
# selection moves.

View File

@@ -150,7 +150,7 @@ class Task
#
# No more events are emitted once this method is called.
terminate: ->
return unless @childProcess?
return false unless @childProcess?
@childProcess.removeAllListeners()
@childProcess.stdout.removeAllListeners()
@@ -158,4 +158,10 @@ class Task
@childProcess.kill()
@childProcess = null
undefined
true
cancel: ->
didForcefullyTerminate = @terminate()
if didForcefullyTerminate
@emit('task:cancelled')
didForcefullyTerminate

View File

@@ -1255,13 +1255,6 @@ class TextEditorPresenter
range = marker.getScreenRange()
if decoration.isDestroyed() or not marker.isValid() or range.isEmpty() or not range.intersectsRowRange(@startRow, @endRow - 1)
tileStartRow = @tileForRow(range.start.row)
tileEndRow = @tileForRow(range.end.row)
for tile in [tileStartRow..tileEndRow] by @tileSize
delete @state.content.tiles[tile]?.highlights[decoration.id]
@emitDidUpdateState()
return
if range.start.row < @startRow
@@ -1271,11 +1264,7 @@ class TextEditorPresenter
range.end.row = @endRow
range.end.column = 0
if range.isEmpty()
tileState = @state.content.tiles[@tileForRow(range.start.row)]
delete tileState.highlights[decoration.id]
@emitDidUpdateState()
return
return if range.isEmpty()
flash = decoration.consumeNextFlash()
@@ -1309,8 +1298,6 @@ class TextEditorPresenter
@visibleHighlights[tileStartRow] ?= {}
@visibleHighlights[tileStartRow][decoration.id] = true
@emitDidUpdateState()
true
repositionRegionWithinTile: (region, tileStartRow) ->

View File

@@ -7,6 +7,7 @@ Serializable = require 'serializable'
{Emitter, Disposable, CompositeDisposable} = require 'event-kit'
Grim = require 'grim'
fs = require 'fs-plus'
DefaultDirectorySearcher = require './default-directory-searcher'
Model = require './model'
TextEditor = require './text-editor'
PaneContainer = require './pane-container'
@@ -46,6 +47,13 @@ class Workspace extends Model
@paneContainer ?= new PaneContainer()
@paneContainer.onDidDestroyPaneItem(@didDestroyPaneItem)
@directorySearchers = []
@defaultDirectorySearcher = new DefaultDirectorySearcher()
atom.packages.serviceHub.consume(
'atom.directory-searcher',
'^0.1.0',
(provider) => @directorySearchers.unshift(provider))
@panelContainers =
top: new PanelContainer({location: 'top'})
left: new PanelContainer({location: 'left'})
@@ -791,36 +799,65 @@ class Workspace extends Model
# * `regex` {RegExp} to search with.
# * `options` (optional) {Object} (default: {})
# * `paths` An {Array} of glob patterns to search within
# * `onPathsSearched` (optional) {Function}
# * `iterator` {Function} callback on each file found
#
# Returns a `Promise`.
# Returns a `Promise` with a `cancel()` method that will cancel all
# of the underlying searches that were started as part of this scan.
scan: (regex, options={}, iterator) ->
if _.isFunction(options)
iterator = options
options = {}
deferred = Q.defer()
searchOptions =
ignoreCase: regex.ignoreCase
inclusions: options.paths
includeHidden: true
excludeVcsIgnores: atom.config.get('core.excludeVcsIgnoredPaths')
exclusions: atom.config.get('core.ignoredNames')
follow: atom.config.get('core.followSymlinks')
task = Task.once require.resolve('./scan-handler'), atom.project.getPaths(), regex.source, searchOptions, ->
deferred.resolve()
task.on 'scan:result-found', (result) ->
iterator(result) unless atom.project.isPathModified(result.filePath)
task.on 'scan:file-error', (error) ->
iterator(null, error)
# Find a searcher for every Directory in the project. Each searcher that is matched
# will be associated with an Array of Directory objects in the Map.
directoriesForSearcher = new Map()
for directory in atom.project.getDirectories()
searcher = @defaultDirectorySearcher
for directorySearcher in @directorySearchers
if directorySearcher.canSearchDirectory(directory)
searcher = directorySearcher
break
directories = directoriesForSearcher.get(searcher)
unless directories
directories = []
directoriesForSearcher.set(searcher, directories)
directories.push(directory)
# Define the onPathsSearched callback.
if _.isFunction(options.onPathsSearched)
task.on 'scan:paths-searched', (numberOfPathsSearched) ->
options.onPathsSearched(numberOfPathsSearched)
# Maintain a map of directories to the number of search results. When notified of a new count,
# replace the entry in the map and update the total.
onPathsSearchedOption = options.onPathsSearched
totalNumberOfPathsSearched = 0
numberOfPathsSearchedForSearcher = new Map()
onPathsSearched = (searcher, numberOfPathsSearched) ->
oldValue = numberOfPathsSearchedForSearcher.get(searcher)
if oldValue
totalNumberOfPathsSearched -= oldValue
numberOfPathsSearchedForSearcher.set(searcher, numberOfPathsSearched)
totalNumberOfPathsSearched += numberOfPathsSearched
onPathsSearchedOption(totalNumberOfPathsSearched)
else
onPathsSearched = ->
# Kick off all of the searches and unify them into one Promise.
allSearches = []
directoriesForSearcher.forEach (directories, searcher) ->
searchOptions =
inclusions: options.paths or []
includeHidden: true
excludeVcsIgnores: atom.config.get('core.excludeVcsIgnoredPaths')
exclusions: atom.config.get('core.ignoredNames')
follow: atom.config.get('core.followSymlinks')
didMatch: (result) ->
iterator(result) unless atom.project.isPathModified(result.filePath)
didError: (error) ->
iterator(null, error)
didSearchPaths: (count) -> onPathsSearched(searcher, count)
directorySearcher = searcher.search(directories, regex, searchOptions)
allSearches.push(directorySearcher)
searchPromise = Promise.all(allSearches)
for buffer in atom.project.getBuffers() when buffer.isModified()
filePath = buffer.getPath()
@@ -829,11 +866,31 @@ class Workspace extends Model
buffer.scan regex, (match) -> matches.push match
iterator {filePath, matches} if matches.length > 0
promise = deferred.promise
promise.cancel = ->
task.terminate()
deferred.resolve('cancelled')
promise
# Make sure the Promise that is returned to the client is cancelable. To be consistent
# with the existing behavior, instead of cancel() rejecting the promise, it should
# resolve it with the special value 'cancelled'. At least the built-in find-and-replace
# package relies on this behavior.
isCancelled = false
cancellablePromise = new Promise (resolve, reject) ->
onSuccess = ->
if isCancelled
resolve('cancelled')
else
resolve(null)
searchPromise.then(onSuccess, reject)
cancellablePromise.cancel = ->
isCancelled = true
# Note that cancelling all of the members of allSearches will cause all of the searches
# to resolve, which causes searchPromise to resolve, which is ultimately what causes
# cancellablePromise to resolve.
promise.cancel() for promise in allSearches
# Although this method claims to return a `Promise`, the `ResultsPaneView.onSearch()`
# method in the find-and-replace package expects the object returned by this method to have a
# `done()` method. Include a done() method until find-and-replace can be updated.
cancellablePromise.done = (onSuccessOrFailure) ->
cancellablePromise.then(onSuccessOrFailure, onSuccessOrFailure)
cancellablePromise
# Public: Performs a replace across all the specified files in the project.
#

View File

@@ -25,7 +25,7 @@ atom-pane-container {
width: 100%;
height: 8px;
margin-top: -4px;
cursor: ns-resize;
cursor: row-resize;
border-bottom: none;
}
}
@@ -37,7 +37,7 @@ atom-pane-container {
width: 8px;
height: 100%;
margin-left: -4px;
cursor: ew-resize;
cursor: col-resize;
border-right: none;
}
}