mirror of
https://github.com/atom/atom.git
synced 2026-01-22 21:38:10 -05:00
Merge remote-tracking branch 'origin/master' into squirrel-installer
Conflicts: build/Gruntfile.coffee
This commit is contained in:
1
.node-version
Normal file
1
.node-version
Normal file
@@ -0,0 +1 @@
|
||||
v0.10.21
|
||||
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.92.0"
|
||||
"atom-package-manager": "0.93.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ describe "editorView.", ->
|
||||
describe "empty-vs-set-innerHTML.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editorView.getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getLastVisibleScreenRow()
|
||||
firstRow = editorView.getModel().getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getModel().getLastVisibleScreenRow()
|
||||
|
||||
benchmark "build-gutter-html.", 1000, ->
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
@@ -97,8 +97,8 @@ describe "editorView.", ->
|
||||
describe "multiple-lines.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editorView.getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getLastVisibleScreenRow()
|
||||
firstRow = editorView.getModel().getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getModel().getLastVisibleScreenRow()
|
||||
|
||||
benchmark "cache-entire-visible-area", 100, ->
|
||||
for i in [firstRow..lastRow]
|
||||
|
||||
56
benchmark/browser-process-startup.coffee
Executable file
56
benchmark/browser-process-startup.coffee
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
{spawn, exec} = require 'child_process'
|
||||
fs = require 'fs'
|
||||
os = require 'os'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
temp = require 'temp'
|
||||
|
||||
directoryToOpen = temp.mkdirSync('browser-process-startup-')
|
||||
socketPath = path.join(os.tmpdir(), 'atom.sock')
|
||||
numberOfRuns = 10
|
||||
|
||||
deleteSocketFile = ->
|
||||
try
|
||||
fs.unlinkSync(socketPath) if fs.existsSync(socketPath)
|
||||
catch error
|
||||
console.error(error)
|
||||
|
||||
launchAtom = (callback) ->
|
||||
deleteSocketFile()
|
||||
|
||||
cmd = 'atom'
|
||||
args = ['--safe', '--new-window', '--foreground', directoryToOpen]
|
||||
atomProcess = spawn(cmd, args)
|
||||
|
||||
output = ''
|
||||
startupTimes = []
|
||||
dataListener = (data) ->
|
||||
output += data
|
||||
if match = /App load time: (\d+)/.exec(output)
|
||||
startupTime = parseInt(match[1])
|
||||
atomProcess.stderr.removeListener 'data', dataListener
|
||||
atomProcess.kill()
|
||||
exec 'pkill -9 Atom', (error) ->
|
||||
console.error(error) if error?
|
||||
callback(startupTime)
|
||||
|
||||
atomProcess.stderr.on 'data', dataListener
|
||||
|
||||
startupTimes = []
|
||||
collector = (startupTime) ->
|
||||
startupTimes.push(startupTime)
|
||||
if startupTimes.length < numberOfRuns
|
||||
launchAtom(collector)
|
||||
else
|
||||
maxTime = _.max(startupTimes)
|
||||
minTime = _.min(startupTimes)
|
||||
totalTime = startupTimes.reduce (previousValue=0, currentValue) -> previousValue + currentValue
|
||||
console.log "Startup Runs: #{startupTimes.length}"
|
||||
console.log "First run time: #{startupTimes[0]}ms"
|
||||
console.log "Max time: #{maxTime}ms"
|
||||
console.log "Min time: #{minTime}ms"
|
||||
console.log "Average time: #{Math.round(totalTime/startupTimes.length)}ms"
|
||||
|
||||
launchAtom(collector)
|
||||
@@ -225,9 +225,18 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg'])
|
||||
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
|
||||
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'dump-symbols', 'set-version', 'check-licenses', 'lint', 'test', 'create-installer', 'codesign', 'publish-build'])
|
||||
grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
|
||||
|
||||
ciTasks = ['output-disk-space', 'download-atom-shell', 'build']
|
||||
ciTasks.push('dump-symbols') if process.platform isnt 'win32'
|
||||
ciTasks.push('mkdeb') if process.platform is 'linux'
|
||||
ciTasks.push('set-version', 'check-licenses', 'lint')
|
||||
ciTasks.push('test') if process.platform isnt 'linux'
|
||||
ciTasks.push('codesign')
|
||||
ciTasks.push('create-installer') if process.platform is 'win32'
|
||||
ciTasks.push('publish-build')
|
||||
grunt.registerTask('ci', ciTasks)
|
||||
|
||||
defaultTasks = ['download-atom-shell', 'build', 'set-version']
|
||||
defaultTasks.push 'install' unless process.platform is 'linux'
|
||||
grunt.registerTask('default', defaultTasks)
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~0.2.9",
|
||||
"donna": "~1.0",
|
||||
"tello": "~0.2",
|
||||
"donna": "1.0.1",
|
||||
"tello": "1.0.3",
|
||||
"formidable": "~1.0.14",
|
||||
"fs-plus": "2.x",
|
||||
"github-releases": "~0.2.0",
|
||||
@@ -27,7 +27,7 @@
|
||||
"harmony-collections": "~0.3.8",
|
||||
"json-front-matter": "~0.1.3",
|
||||
"legal-eagle": "~0.4.0",
|
||||
"minidump": "~0.7",
|
||||
"minidump": "~0.8",
|
||||
"normalize-package-data": "0.2.12",
|
||||
"npm": "~1.4.5",
|
||||
"rcedit": "~0.1.2",
|
||||
|
||||
@@ -11,6 +11,7 @@ module.exports = (grunt) ->
|
||||
modulesPath = path.resolve(__dirname, '..', '..', 'node_modules')
|
||||
classes = {}
|
||||
fs.traverseTreeSync modulesPath, (modulePath) ->
|
||||
return false if modulePath.match(/node_modules/g).length > 1 # dont need the dependencies of the dependencies
|
||||
return true unless path.basename(modulePath) is 'package.json'
|
||||
return true unless fs.isFileSync(modulePath)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ module.exports = (gruntObject) ->
|
||||
grunt.registerTask 'prepare-docs', 'Move api.json to atom-api.json', ->
|
||||
docsOutputDir = grunt.config.get('docsOutputDir')
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json')
|
||||
cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json')
|
||||
|
||||
grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', ->
|
||||
done = @async()
|
||||
@@ -45,16 +45,32 @@ module.exports = (gruntObject) ->
|
||||
uploadAssets(release, buildDir, assets, done)
|
||||
|
||||
getAssets = ->
|
||||
if process.platform is 'darwin'
|
||||
[
|
||||
{assetName: 'atom-mac.zip', sourcePath: 'Atom.app'}
|
||||
{assetName: 'atom-mac-symbols.zip', sourcePath: 'Atom.breakpad.syms'}
|
||||
{assetName: 'atom-api.json', sourcePath: 'atom-api.json'}
|
||||
]
|
||||
else
|
||||
[
|
||||
{assetName: 'atom-windows.zip', sourcePath: 'Atom'}
|
||||
]
|
||||
switch process.platform
|
||||
when 'darwin'
|
||||
[
|
||||
{assetName: 'atom-mac.zip', sourcePath: 'Atom.app'}
|
||||
{assetName: 'atom-mac-symbols.zip', sourcePath: 'Atom.breakpad.syms'}
|
||||
{assetName: 'atom-api.json', sourcePath: 'atom-api.json'}
|
||||
]
|
||||
when 'win32'
|
||||
[
|
||||
{assetName: 'atom-windows.zip', sourcePath: 'Atom'}
|
||||
]
|
||||
when 'linux'
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
sourcePath = fs.listSync(buildDir, ['.deb'])[0]
|
||||
if process.arch is 'ia32g'
|
||||
arch = 'i386'
|
||||
else
|
||||
arch = 'amd64'
|
||||
assetName = "atom-#{arch}.deb"
|
||||
|
||||
{cp} = require('./task-helpers')(grunt)
|
||||
cp sourcePath, path.join(buildDir, assetName)
|
||||
|
||||
[
|
||||
{assetName, sourcePath}
|
||||
]
|
||||
|
||||
logError = (message, error, details) ->
|
||||
grunt.log.error(message)
|
||||
|
||||
@@ -33,26 +33,41 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
|
||||
If you have problems with permissions don't forget to prefix with `sudo`
|
||||
|
||||
From the cloned repository directory:
|
||||
1. Clone the Atom repository:
|
||||
|
||||
1. Build:
|
||||
```sh
|
||||
git clone https://github.com/atom/atom
|
||||
cd atom
|
||||
```
|
||||
|
||||
```sh
|
||||
$ script/build
|
||||
```
|
||||
This will create the atom application at `$TMPDIR/atom-build/Atom`.
|
||||
2. Install the `atom` and `apm` commands to `/usr/local/bin` by executing:
|
||||
2. Checkout the latest Atom release:
|
||||
|
||||
```sh
|
||||
$ sudo script/grunt install
|
||||
```
|
||||
3. *Optionally*, you may generate a `.deb` package at `$TMPDIR/atom-build`:
|
||||
```sh
|
||||
git fetch
|
||||
git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
```
|
||||
|
||||
```sh
|
||||
$ script/grunt mkdeb
|
||||
```
|
||||
3. Build Atom:
|
||||
|
||||
Use the newly installed atom by restarting any running atom instances.
|
||||
```sh
|
||||
script/build
|
||||
```
|
||||
|
||||
This will create the atom application at `$TMPDIR/atom-build/Atom`.
|
||||
|
||||
4. Install the `atom` and `apm` commands to `/usr/local/bin` by executing:
|
||||
|
||||
```sh
|
||||
sudo script/grunt install
|
||||
```
|
||||
|
||||
5. *Optionally*, you may generate a `.deb` package at `$TMPDIR/atom-build`:
|
||||
|
||||
```sh
|
||||
script/grunt mkdeb
|
||||
```
|
||||
|
||||
Use the newly installed Atom by fully quitting Atom and then reopening.
|
||||
|
||||
## Advanced Options
|
||||
|
||||
@@ -88,7 +103,7 @@ this is the reason for this error you can issue
|
||||
and restart Atom. If Atom now works fine, you can make this setting permanent:
|
||||
|
||||
```sh
|
||||
echo 32768 > /proc/sys/fs/inotify/max_user_watches
|
||||
echo 32768 | sudo tee -a /proc/sys/fs/inotify/max_user_watches
|
||||
```
|
||||
|
||||
See also https://github.com/atom/atom/issues/2082.
|
||||
@@ -100,6 +115,15 @@ have Node.js installed, or node isn't identified as Node.js on your machine.
|
||||
If it's the latter, entering `sudo ln -s /usr/bin/nodejs /usr/bin/node` into
|
||||
your terminal may fix the issue.
|
||||
|
||||
#### You can also use Alternatives
|
||||
|
||||
On some variants (mostly Debian based distros) it's preferable for you to use
|
||||
Alternatives so that changes to the binary paths can be fixed or altered easily:
|
||||
|
||||
```sh
|
||||
sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 1 --slave /usr/bin/js js /usr/bin/nodejs
|
||||
```
|
||||
|
||||
### Linux build error reports in atom/atom
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Alinux&type=Issues)
|
||||
to get a list of reports about build errors on Linux.
|
||||
|
||||
@@ -53,7 +53,7 @@ module.exports =
|
||||
|
||||
convert: ->
|
||||
# This assumes the active pane item is an editor
|
||||
editor = atom.workspace.activePaneItem
|
||||
editor = atom.workspace.getActivePaneItem()
|
||||
editor.insertText('Hello, World!')
|
||||
```
|
||||
|
||||
@@ -131,7 +131,7 @@ inserting 'Hello, World!' convert the selected text to ASCII art.
|
||||
```coffeescript
|
||||
convert: ->
|
||||
# This assumes the active pane item is an editor
|
||||
editor = atom.workspace.activePaneItem
|
||||
editor = atom.workspace.getActivePaneItem()
|
||||
selection = editor.getLastSelection()
|
||||
|
||||
figlet = require 'figlet'
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
# atom.workspaceView.eachEditorView (editorView) ->
|
||||
# editor = editorView.getEditor()
|
||||
# if path.extname(editor.getPath()) is '.md'
|
||||
# editor.setSoftWrap(true)
|
||||
# editor.setSoftWrapped(true)
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
# 'enter': 'editor:newline'
|
||||
#
|
||||
# '.workspace':
|
||||
# 'ctrl-P': 'core:move-up'
|
||||
# 'ctrl-shift-p': 'core:move-up'
|
||||
# 'ctrl-p': 'core:move-down'
|
||||
#
|
||||
# You can find more information about keymaps in these guides:
|
||||
# * https://atom.io/docs/latest/customizing-atom#customizing-key-bindings
|
||||
# * https://atom.io/docs/latest/advanced/keymaps
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
'cmd-shift-right': 'editor:select-to-end-of-line'
|
||||
'alt-backspace': 'editor:delete-to-beginning-of-word'
|
||||
'alt-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-a': 'editor:move-to-beginning-of-line'
|
||||
'ctrl-a': 'editor:move-to-first-character-of-line'
|
||||
'ctrl-e': 'editor:move-to-end-of-line'
|
||||
'ctrl-k': 'editor:cut-to-end-of-line'
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
{ type: 'separator' }
|
||||
{ label: 'Install Shell Commands', command: 'window:install-shell-commands' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Services', submenu: [] }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Hide Atom', command: 'application:hide' }
|
||||
{ label: 'Hide Others', command: 'application:hide-other-applications' }
|
||||
{ label: 'Show All', command: 'application:unhide-all-applications' }
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
{ label: 'Open Your Keymap', command: 'application:open-your-keymap' }
|
||||
{ label: 'Open Your Snippets', command: 'application:open-your-snippets' }
|
||||
{ label: 'Open Your Stylesheet', command: 'application:open-your-stylesheet' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
66
package.json
66
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.125.0",
|
||||
"version": "0.130.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -17,106 +17,106 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.15.9",
|
||||
"atomShellVersion": "0.16.2",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^2.0.5",
|
||||
"atom-keymap": "^2.1.1",
|
||||
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"clear-cut": "0.4.0",
|
||||
"coffee-script": "1.7.0",
|
||||
"coffeestack": "0.7.0",
|
||||
"delegato": "^1",
|
||||
"emissary": "^1.2.2",
|
||||
"first-mate": "^2.0.5",
|
||||
"emissary": "^1.3.1",
|
||||
"event-kit": "0.7.2",
|
||||
"first-mate": "^2.1.1",
|
||||
"fs-plus": "^2.2.6",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "^1.1",
|
||||
"fuzzaldrin": "^2.1",
|
||||
"git-utils": "^2.1.4",
|
||||
"grim": "0.12.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-tagged": "^1.1.2",
|
||||
"less-cache": "0.13.0",
|
||||
"less-cache": "0.14.0",
|
||||
"mixto": "^1",
|
||||
"mkdirp": "0.3.5",
|
||||
"nslog": "^1.0.1",
|
||||
"oniguruma": "^3.0.4",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "^2.0.10",
|
||||
"pathwatcher": "^2.1.2",
|
||||
"property-accessors": "^1",
|
||||
"q": "^1.0.1",
|
||||
"random-words": "0.0.1",
|
||||
"react-atom-fork": "^0.11.1",
|
||||
"reactionary-atom-fork": "^1.0.0",
|
||||
"runas": "1.0.1",
|
||||
"scandal": "1.0.0",
|
||||
"scandal": "1.0.2",
|
||||
"scoped-property-store": "^0.9.0",
|
||||
"scrollbar-style": "^1.0.2",
|
||||
"season": "^1.0.2",
|
||||
"semver": "1.1.4",
|
||||
"serializable": "^1",
|
||||
"space-pen": "3.4.6",
|
||||
"space-pen": "3.4.7",
|
||||
"temp": "0.7.0",
|
||||
"text-buffer": "^3.1.0",
|
||||
"text-buffer": "^3.2.4",
|
||||
"theorist": "^1.0.2",
|
||||
"underscore-plus": "^1.5.1",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.19.0",
|
||||
"atom-dark-ui": "0.34.0",
|
||||
"atom-dark-ui": "0.35.0",
|
||||
"atom-light-syntax": "0.20.0",
|
||||
"atom-light-ui": "0.29.0",
|
||||
"atom-light-ui": "0.30.0",
|
||||
"base16-tomorrow-dark-theme": "0.21.0",
|
||||
"base16-tomorrow-light-theme": "0.4.0",
|
||||
"solarized-dark-syntax": "0.22.0",
|
||||
"solarized-light-syntax": "0.12.0",
|
||||
"archive-view": "0.36.0",
|
||||
"autocomplete": "0.31.0",
|
||||
"archive-view": "0.37.0",
|
||||
"autocomplete": "0.32.0",
|
||||
"autoflow": "0.18.0",
|
||||
"autosave": "0.15.0",
|
||||
"autosave": "0.17.0",
|
||||
"background-tips": "0.16.0",
|
||||
"bookmarks": "0.28.0",
|
||||
"bracket-matcher": "0.54.0",
|
||||
"bracket-matcher": "0.55.0",
|
||||
"command-palette": "0.24.0",
|
||||
"deprecation-cop": "0.10.0",
|
||||
"dev-live-reload": "0.34.0",
|
||||
"exception-reporting": "0.20.0",
|
||||
"feedback": "0.33.0",
|
||||
"find-and-replace": "0.132.0",
|
||||
"fuzzy-finder": "0.57.0",
|
||||
"find-and-replace": "0.138.0",
|
||||
"fuzzy-finder": "0.58.0",
|
||||
"git-diff": "0.39.0",
|
||||
"go-to-line": "0.25.0",
|
||||
"grammar-selector": "0.29.0",
|
||||
"image-view": "0.36.0",
|
||||
"incompatible-packages": "0.9.0",
|
||||
"keybinding-resolver": "0.19.0",
|
||||
"keybinding-resolver": "0.20.0",
|
||||
"link": "0.25.0",
|
||||
"markdown-preview": "0.101.0",
|
||||
"metrics": "0.33.0",
|
||||
"markdown-preview": "0.103.0",
|
||||
"metrics": "0.34.0",
|
||||
"open-on-github": "0.30.0",
|
||||
"package-generator": "0.31.0",
|
||||
"release-notes": "0.36.0",
|
||||
"settings-view": "0.141.0",
|
||||
"snippets": "0.51.0",
|
||||
"settings-view": "0.142.0",
|
||||
"snippets": "0.52.0",
|
||||
"spell-check": "0.42.0",
|
||||
"status-bar": "0.44.0",
|
||||
"styleguide": "0.30.0",
|
||||
"symbols-view": "0.63.0",
|
||||
"tabs": "0.50.0",
|
||||
"tabs": "0.51.0",
|
||||
"timecop": "0.22.0",
|
||||
"tree-view": "0.114.0",
|
||||
"tree-view": "0.125.0",
|
||||
"update-package-dependencies": "0.6.0",
|
||||
"welcome": "0.18.0",
|
||||
"whitespace": "0.25.0",
|
||||
"wrap-guide": "0.21.0",
|
||||
|
||||
"wrap-guide": "0.22.0",
|
||||
"language-c": "0.28.0",
|
||||
"language-coffee-script": "0.30.0",
|
||||
"language-css": "0.17.0",
|
||||
"language-gfm": "0.48.0",
|
||||
"language-gfm": "0.50.0",
|
||||
"language-git": "0.9.0",
|
||||
"language-go": "0.17.0",
|
||||
"language-html": "0.25.0",
|
||||
"language-html": "0.26.0",
|
||||
"language-hyperlink": "0.12.0",
|
||||
"language-java": "0.11.0",
|
||||
"language-javascript": "0.39.0",
|
||||
@@ -126,10 +126,10 @@
|
||||
"language-mustache": "0.10.0",
|
||||
"language-objective-c": "0.11.0",
|
||||
"language-perl": "0.9.0",
|
||||
"language-php": "0.15.0",
|
||||
"language-php": "0.16.0",
|
||||
"language-property-list": "0.7.0",
|
||||
"language-python": "0.19.0",
|
||||
"language-ruby": "0.35.0",
|
||||
"language-ruby": "0.37.0",
|
||||
"language-ruby-on-rails": "0.18.0",
|
||||
"language-sass": "0.21.0",
|
||||
"language-shellscript": "0.8.0",
|
||||
@@ -138,7 +138,7 @@
|
||||
"language-text": "0.6.0",
|
||||
"language-todo": "0.11.0",
|
||||
"language-toml": "0.12.0",
|
||||
"language-xml": "0.19.0",
|
||||
"language-xml": "0.20.0",
|
||||
"language-yaml": "0.17.0"
|
||||
},
|
||||
"private": true,
|
||||
|
||||
@@ -5,4 +5,5 @@ Exec=<%= installDir %>/share/atom/atom %U
|
||||
Icon=<%= iconName %>
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=GNOME;GTK;Utility;TextEditor;
|
||||
Categories=GNOME;GTK;Utility;TextEditor;Development;
|
||||
MimeType=text/plain;
|
||||
|
||||
@@ -5,9 +5,6 @@ var path = require('path');
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
if (process.platform == 'linux')
|
||||
throw new Error('cibuild can not run on linux yet!');
|
||||
|
||||
var homeDir = process.platform == 'win32' ? process.env.USERPROFILE : process.env.HOME;
|
||||
|
||||
function loadEnvironmentVariables(filePath) {
|
||||
@@ -27,7 +24,7 @@ function loadEnvironmentVariables(filePath) {
|
||||
function readEnvironmentVariables() {
|
||||
if (process.platform === 'win32')
|
||||
loadEnvironmentVariables(path.resolve('/jenkins/config/atomcredentials'));
|
||||
else {
|
||||
else if (process.platform === 'darwin') {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials');
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain');
|
||||
}
|
||||
|
||||
13
script/cibuild-atom-linux
Executable file
13
script/cibuild-atom-linux
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export ATOM_ACCESS_TOKEN=$BUILD_ATOM_LINUX_ACCESS_TOKEN
|
||||
|
||||
if [ -d /usr/local/share/nodenv ]; then
|
||||
export NODENV_ROOT=/usr/local/share/nodenv
|
||||
export PATH=/usr/local/share/nodenv/bin:/usr/local/share/nodenv/shims:$PATH
|
||||
export NODENV_VERSION="v0.10.21"
|
||||
fi
|
||||
|
||||
script/cibuild
|
||||
@@ -31,7 +31,7 @@ function verifyNode(cb) {
|
||||
var nodeMajorVersion = +versionArray[0];
|
||||
var nodeMinorVersion = +versionArray[1];
|
||||
if (nodeMajorVersion === 0 && nodeMinorVersion < 10) {
|
||||
error = "node v0.10 is required to build Atom.";
|
||||
error = "node v0.10 is required to build Atom, node " + nodeVersion + " is installed.";
|
||||
cb(error);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -241,15 +241,15 @@ describe "the `atom` global", ->
|
||||
two = atom.themes.stringToId(two)
|
||||
three = atom.themes.stringToId(three)
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(one)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
|
||||
atom.packages.activatePackage("package-with-stylesheets-manifest")
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
expect($('#jasmine-content').css('font-size')).toBe '1px'
|
||||
|
||||
describe "when the metadata does not contain a 'stylesheets' manifest", ->
|
||||
@@ -263,14 +263,14 @@ describe "the `atom` global", ->
|
||||
two = atom.themes.stringToId(two)
|
||||
three = atom.themes.stringToId(three)
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(one)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
|
||||
atom.packages.activatePackage("package-with-stylesheets")
|
||||
expect(atom.themes.stylesheetElementForId(one)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull()
|
||||
expect($('#jasmine-content').css('font-size')).toBe '3px'
|
||||
|
||||
describe "grammar loading", ->
|
||||
@@ -350,7 +350,7 @@ describe "the `atom` global", ->
|
||||
atom.packages.deactivatePackage("package-that-throws-on-activate")
|
||||
expect(badPack.mainModule.serialize).not.toHaveBeenCalled()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's serialize methods", ->
|
||||
it "absorbs exceptions that are thrown by the package module's serialize method", ->
|
||||
spyOn(console, 'error')
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -365,6 +365,16 @@ describe "the `atom` global", ->
|
||||
expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's deactivate method", ->
|
||||
spyOn(console, 'error')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-that-throws-on-deactivate")
|
||||
|
||||
runs ->
|
||||
expect(-> atom.packages.deactivatePackage("package-that-throws-on-deactivate")).not.toThrow()
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
it "removes the package's grammars", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-grammars')
|
||||
@@ -520,7 +530,7 @@ describe "the `atom` global", ->
|
||||
|
||||
reloadedHandler = jasmine.createSpy('reloadedHandler')
|
||||
reloadedHandler.reset()
|
||||
atom.themes.on('reloaded', reloadedHandler)
|
||||
atom.themes.onDidReloadAll reloadedHandler
|
||||
|
||||
pack = atom.packages.disablePackage(packageName)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ describe "DisplayBuffer", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
changeHandler = jasmine.createSpy 'changeHandler'
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
displayBuffer.onDidChange changeHandler
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -58,7 +58,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "soft wrapping", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
displayBuffer.setEditorWidthInChars(50)
|
||||
changeHandler.reset()
|
||||
|
||||
@@ -129,7 +129,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
expect(event).toEqual(start: 7, end: 8, screenDelta: -1, bufferDelta: 0)
|
||||
|
||||
describe "when the update causes a line to softwrap an additional time", ->
|
||||
describe "when the update causes a line to soft wrap an additional time", ->
|
||||
it "rewraps the line and emits a change event", ->
|
||||
buffer.insert([6, 28], '1234567890')
|
||||
expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot ? '
|
||||
@@ -162,7 +162,7 @@ describe "DisplayBuffer", ->
|
||||
describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", ->
|
||||
it "correctly renders the original wrapped line", ->
|
||||
buffer = atom.project.buildBufferSync(null, '')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrap: true})
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrapped: true})
|
||||
|
||||
buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.")
|
||||
buffer.insert([0, Infinity], '\n')
|
||||
@@ -220,10 +220,10 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.setWidth(50)
|
||||
displayBuffer.manageScrollPosition = true
|
||||
|
||||
displayBuffer.setSoftWrap(false)
|
||||
displayBuffer.setSoftWrapped(false)
|
||||
displayBuffer.setScrollLeft(Infinity)
|
||||
expect(displayBuffer.getScrollLeft()).toBeGreaterThan 0
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
displayBuffer.setScrollLeft(10)
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
@@ -234,7 +234,7 @@ describe "DisplayBuffer", ->
|
||||
buffer.release()
|
||||
buffer = atom.project.bufferForPathSync('two-hundred.txt')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
displayBuffer.onDidChange changeHandler
|
||||
|
||||
describe "when folds are created and destroyed", ->
|
||||
describe "when a fold spans multiple lines", ->
|
||||
@@ -568,7 +568,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "::clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
displayBuffer.setEditorWidthInChars(50)
|
||||
|
||||
it "allows valid positions", ->
|
||||
@@ -643,7 +643,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
it "correctly translates positions on soft wrapped lines containing tabs", ->
|
||||
buffer.setText('\t\taa bb cc dd ee ff gg')
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
displayBuffer.setEditorWidthInChars(10)
|
||||
expect(displayBuffer.screenPositionForBufferPosition([0, 10], wrapAtSoftNewlines: true)).toEqual [1, 0]
|
||||
expect(displayBuffer.bufferPositionForScreenPosition([1, 0])).toEqual [0, 10]
|
||||
@@ -686,7 +686,7 @@ describe "DisplayBuffer", ->
|
||||
expect(marker2.getScreenRange()).toEqual [[5, 4], [5, 10]]
|
||||
|
||||
it "emits a 'marker-created' event on the DisplayBuffer whenever a marker is created", ->
|
||||
displayBuffer.on 'marker-created', markerCreatedHandler = jasmine.createSpy("markerCreatedHandler")
|
||||
displayBuffer.onDidCreateMarker markerCreatedHandler = jasmine.createSpy("markerCreatedHandler")
|
||||
|
||||
marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
|
||||
expect(markerCreatedHandler).toHaveBeenCalledWith(marker1)
|
||||
@@ -722,7 +722,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
beforeEach ->
|
||||
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
|
||||
marker.on 'changed', markerChangedHandler = jasmine.createSpy("markerChangedHandler")
|
||||
marker.onDidChange markerChangedHandler = jasmine.createSpy("markerChangedHandler")
|
||||
|
||||
it "triggers the 'changed' event whenever the markers head's screen position changes in the buffer or on screen", ->
|
||||
marker.setHeadScreenPosition([8, 20])
|
||||
@@ -859,8 +859,8 @@ describe "DisplayBuffer", ->
|
||||
|
||||
it "updates markers before emitting buffer change events, but does not notify their observers until the change event", ->
|
||||
marker2 = displayBuffer.markBufferRange([[8, 1], [8, 1]])
|
||||
marker2.on 'changed', marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler")
|
||||
displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange()
|
||||
marker2.onDidChange marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler")
|
||||
displayBuffer.onDidChange changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange()
|
||||
|
||||
# New change ----
|
||||
|
||||
@@ -886,7 +886,7 @@ describe "DisplayBuffer", ->
|
||||
marker2ChangedHandler.reset()
|
||||
|
||||
marker3 = displayBuffer.markBufferRange([[8, 1], [8, 2]])
|
||||
marker3.on 'changed', marker3ChangedHandler = jasmine.createSpy("marker3ChangedHandler")
|
||||
marker3.onDidChange marker3ChangedHandler = jasmine.createSpy("marker3ChangedHandler")
|
||||
|
||||
onDisplayBufferChange = ->
|
||||
# calls change handler first
|
||||
@@ -932,7 +932,7 @@ describe "DisplayBuffer", ->
|
||||
expect(marker3ChangedHandler).toHaveBeenCalled()
|
||||
|
||||
it "updates the position of markers before emitting change events that aren't caused by a buffer change", ->
|
||||
displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake ->
|
||||
displayBuffer.onDidChange changeHandler = jasmine.createSpy("changeHandler").andCallFake ->
|
||||
# calls change handler first
|
||||
expect(markerChangedHandler).not.toHaveBeenCalled()
|
||||
# but still updates the markers
|
||||
@@ -998,16 +998,16 @@ describe "DisplayBuffer", ->
|
||||
expect(marker.isValid()).toBeFalsy()
|
||||
expect(displayBuffer.getMarker(marker.id)).toBeUndefined()
|
||||
|
||||
it "emits 'destroyed' events when markers are destroyed", ->
|
||||
it "notifies ::onDidDestroy observers when markers are destroyed", ->
|
||||
destroyedHandler = jasmine.createSpy("destroyedHandler")
|
||||
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
|
||||
marker.on 'destroyed', destroyedHandler
|
||||
marker.onDidDestroy destroyedHandler
|
||||
marker.destroy()
|
||||
expect(destroyedHandler).toHaveBeenCalled()
|
||||
destroyedHandler.reset()
|
||||
|
||||
marker2 = displayBuffer.markScreenRange([[5, 4], [5, 10]])
|
||||
marker2.on 'destroyed', destroyedHandler
|
||||
marker2.onDidDestroy destroyedHandler
|
||||
buffer.getMarker(marker2.id).destroy()
|
||||
expect(destroyedHandler).toHaveBeenCalled()
|
||||
|
||||
@@ -1041,9 +1041,8 @@ describe "DisplayBuffer", ->
|
||||
describe 'when a marker is created', ->
|
||||
it 'the second display buffer will not emit a marker-created event when the marker has been deleted in the first marker-created event', ->
|
||||
displayBuffer2 = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.on 'marker-created', markerCreated1 = jasmine.createSpy().andCallFake (marker) ->
|
||||
marker.destroy()
|
||||
displayBuffer2.on 'marker-created', markerCreated2 = jasmine.createSpy()
|
||||
displayBuffer.onDidCreateMarker markerCreated1 = jasmine.createSpy().andCallFake (marker) -> marker.destroy()
|
||||
displayBuffer2.onDidCreateMarker markerCreated2 = jasmine.createSpy()
|
||||
|
||||
displayBuffer.markBufferRange([[0, 0], [1, 5]], {})
|
||||
|
||||
@@ -1051,15 +1050,15 @@ describe "DisplayBuffer", ->
|
||||
expect(markerCreated2).not.toHaveBeenCalled()
|
||||
|
||||
describe "decorations", ->
|
||||
[marker, decoration, decorationParams] = []
|
||||
[marker, decoration, decorationProperties] = []
|
||||
beforeEach ->
|
||||
marker = displayBuffer.markBufferRange([[2, 13], [3, 15]])
|
||||
decorationParams = {type: 'gutter', class: 'one'}
|
||||
decoration = displayBuffer.decorateMarker(marker, decorationParams)
|
||||
decorationProperties = {type: 'gutter', class: 'one'}
|
||||
decoration = displayBuffer.decorateMarker(marker, decorationProperties)
|
||||
|
||||
it "can add decorations associated with markers and remove them", ->
|
||||
expect(decoration).toBeDefined()
|
||||
expect(decoration.getParams()).toBe decorationParams
|
||||
expect(decoration.getProperties()).toBe decorationProperties
|
||||
expect(displayBuffer.decorationForId(decoration.id)).toBe decoration
|
||||
expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration
|
||||
|
||||
@@ -1074,12 +1073,12 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when a decoration is updated via Decoration::update()", ->
|
||||
it "emits an 'updated' event containing the new and old params", ->
|
||||
decoration.on 'updated', updatedSpy = jasmine.createSpy()
|
||||
decoration.update type: 'gutter', class: 'two'
|
||||
decoration.onDidChangeProperties updatedSpy = jasmine.createSpy()
|
||||
decoration.setProperties type: 'gutter', class: 'two'
|
||||
|
||||
{oldParams, newParams} = updatedSpy.mostRecentCall.args[0]
|
||||
expect(oldParams).toEqual decorationParams
|
||||
expect(newParams).toEqual type: 'gutter', class: 'two', id: decoration.id
|
||||
{oldProperties, newProperties} = updatedSpy.mostRecentCall.args[0]
|
||||
expect(oldProperties).toEqual decorationProperties
|
||||
expect(newProperties).toEqual type: 'gutter', class: 'two', id: decoration.id
|
||||
|
||||
describe "::setScrollTop", ->
|
||||
beforeEach ->
|
||||
@@ -1172,7 +1171,7 @@ describe "DisplayBuffer", ->
|
||||
it "recomputes the scroll width when the scoped character widths change in a batch", ->
|
||||
operatorWidth = 20
|
||||
|
||||
displayBuffer.on 'character-widths-changed', changedSpy = jasmine.createSpy()
|
||||
displayBuffer.onDidChangeCharacterWidths changedSpy = jasmine.createSpy()
|
||||
|
||||
displayBuffer.batchCharacterMeasurement ->
|
||||
displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '<', operatorWidth)
|
||||
|
||||
@@ -278,7 +278,7 @@ describe "EditorComponent", ->
|
||||
describe "when soft wrapping is enabled", ->
|
||||
beforeEach ->
|
||||
editor.setText "a line that wraps \n"
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
nextAnimationFrame()
|
||||
componentNode.style.width = 16 * charWidth + editor.getVerticalScrollbarWidth() + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
@@ -457,7 +457,7 @@ describe "EditorComponent", ->
|
||||
expect(component.lineNumberNodeForScreenRow(6).offsetTop).toBe 6 * lineHeightInPixels
|
||||
|
||||
it "renders • characters for soft-wrapped lines", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
@@ -893,7 +893,7 @@ describe "EditorComponent", ->
|
||||
|
||||
it "only applies decorations to screen rows that are spanned by their marker when lines are soft-wrapped", ->
|
||||
editor.setText("a line that wraps, ok")
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
componentNode.style.width = 16 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
nextAnimationFrame()
|
||||
@@ -1134,7 +1134,7 @@ describe "EditorComponent", ->
|
||||
it "renders the decoration's new params", ->
|
||||
expect(componentNode.querySelector('.test-highlight')).toBeTruthy()
|
||||
|
||||
decoration.update(type: 'highlight', class: 'new-test-highlight')
|
||||
decoration.setProperties(type: 'highlight', class: 'new-test-highlight')
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(componentNode.querySelector('.test-highlight')).toBeFalsy()
|
||||
@@ -1331,9 +1331,19 @@ describe "EditorComponent", ->
|
||||
gutterNode = componentNode.querySelector('.gutter')
|
||||
|
||||
describe "when the gutter is clicked", ->
|
||||
it "moves the cursor to the beginning of the clicked row", ->
|
||||
it "selects the clicked row", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4)))
|
||||
expect(editor.getCursorScreenPosition()).toEqual [4, 0]
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[4, 0], [5, 0]]
|
||||
|
||||
describe "when the gutter is meta-clicked", ->
|
||||
it "creates a new selection for the clicked row", ->
|
||||
editor.setSelectedScreenRange([[3, 0], [3, 2]])
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]]]
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]], [[6, 0], [7, 0]]]
|
||||
|
||||
describe "when the gutter is shift-clicked", ->
|
||||
beforeEach ->
|
||||
@@ -1366,6 +1376,40 @@ describe "EditorComponent", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2)))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [7, 0]]
|
||||
|
||||
describe "when the gutter is meta-clicked and dragged", ->
|
||||
beforeEach ->
|
||||
editor.setSelectedScreenRange([[3, 0], [3, 2]])
|
||||
|
||||
describe "when dragging downward", ->
|
||||
it "selects the rows between the start and end of the drag", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]]
|
||||
|
||||
it "merges overlapping selections", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 0]]]
|
||||
|
||||
describe "when dragging upward", ->
|
||||
it "selects the rows between the start and end of the drag", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(4), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]]
|
||||
|
||||
it "merges overlapping selections", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 0]]]
|
||||
|
||||
describe "when the gutter is shift-clicked and dragged", ->
|
||||
describe "when the shift-click is below the existing selection's tail", ->
|
||||
describe "when dragging downward", ->
|
||||
@@ -2022,7 +2066,7 @@ describe "EditorComponent", ->
|
||||
|
||||
describe "soft wrapping", ->
|
||||
beforeEach ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
nextAnimationFrame()
|
||||
|
||||
it "updates the wrap location when the editor is resized", ->
|
||||
@@ -2148,10 +2192,10 @@ describe "EditorComponent", ->
|
||||
|
||||
describe "legacy editor compatibility", ->
|
||||
it "triggers the screen-lines-changed event before the editor:display-update event", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
|
||||
callingOrder = []
|
||||
editor.on 'screen-lines-changed', -> callingOrder.push 'screen-lines-changed'
|
||||
editor.onDidChangeScreenLines -> callingOrder.push 'screen-lines-changed'
|
||||
wrapperView.on 'editor:display-updated', -> callingOrder.push 'editor:display-updated'
|
||||
editor.insertText("HELLO! HELLO!\n HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! ")
|
||||
nextAnimationFrame()
|
||||
|
||||
@@ -110,7 +110,7 @@ describe "Editor", ->
|
||||
|
||||
runs ->
|
||||
expect(editor1.getTabLength()).toBe 4
|
||||
expect(editor1.getSoftWrap()).toBe true
|
||||
expect(editor1.isSoftWrapped()).toBe true
|
||||
expect(editor1.getSoftTabs()).toBe false
|
||||
|
||||
atom.config.set('editor.tabLength', 100)
|
||||
@@ -122,7 +122,7 @@ describe "Editor", ->
|
||||
|
||||
runs ->
|
||||
expect(editor2.getTabLength()).toBe 100
|
||||
expect(editor2.getSoftWrap()).toBe false
|
||||
expect(editor2.isSoftWrapped()).toBe false
|
||||
expect(editor2.getSoftTabs()).toBe true
|
||||
|
||||
describe "title", ->
|
||||
@@ -138,13 +138,24 @@ describe "Editor", ->
|
||||
buffer.setPath(undefined)
|
||||
expect(editor.getLongTitle()).toBe 'untitled'
|
||||
|
||||
it "emits 'title-changed' events when the underlying buffer path", ->
|
||||
titleChangedHandler = jasmine.createSpy("titleChangedHandler")
|
||||
editor.on 'title-changed', titleChangedHandler
|
||||
it "notifies ::onDidChangeTitle observers when the underlying buffer path changes", ->
|
||||
observed = []
|
||||
editor.onDidChangeTitle (title) -> observed.push(title)
|
||||
|
||||
buffer.setPath('/foo/bar/baz.txt')
|
||||
buffer.setPath(undefined)
|
||||
expect(titleChangedHandler.callCount).toBe 2
|
||||
|
||||
expect(observed).toEqual ['baz.txt', 'untitled']
|
||||
|
||||
describe "path", ->
|
||||
it "notifies ::onDidChangePath observers when the underlying buffer path changes", ->
|
||||
observed = []
|
||||
editor.onDidChangePath (filePath) -> observed.push(filePath)
|
||||
|
||||
buffer.setPath(__filename)
|
||||
buffer.setPath(undefined)
|
||||
|
||||
expect(observed).toEqual [__filename, undefined]
|
||||
|
||||
describe "cursor", ->
|
||||
describe ".getLastCursor()", ->
|
||||
@@ -163,21 +174,6 @@ describe "Editor", ->
|
||||
editor.moveDown()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [1, 1]
|
||||
|
||||
it "emits a single 'cursors-moved' event for all moved cursors", ->
|
||||
editor.on 'cursors-moved', cursorsMovedHandler = jasmine.createSpy("cursorsMovedHandler")
|
||||
|
||||
editor.moveDown()
|
||||
expect(cursorsMovedHandler.callCount).toBe 1
|
||||
|
||||
cursorsMovedHandler.reset()
|
||||
editor.addCursorAtScreenPosition([3, 0])
|
||||
editor.moveDown()
|
||||
expect(cursorsMovedHandler.callCount).toBe 1
|
||||
|
||||
cursorsMovedHandler.reset()
|
||||
editor.getLastCursor().moveDown()
|
||||
expect(cursorsMovedHandler.callCount).toBe 1
|
||||
|
||||
describe ".setCursorScreenPosition(screenPosition)", ->
|
||||
it "clears a goal column established by vertical movement", ->
|
||||
# set a goal column by moving down
|
||||
@@ -203,7 +199,7 @@ describe "Editor", ->
|
||||
|
||||
describe "when soft-wrap is enabled and code is folded", ->
|
||||
beforeEach ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(50)
|
||||
editor.createFold(2, 3)
|
||||
|
||||
@@ -321,6 +317,21 @@ describe "Editor", ->
|
||||
editor.moveLeft()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [1, 7]
|
||||
|
||||
it "moves the cursor by n columns to the left", ->
|
||||
editor.setCursorScreenPosition([1, 8])
|
||||
editor.moveLeft(4)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [1, 4]
|
||||
|
||||
it "moves the cursor by two rows up when the columnCount is longer than an entire line", ->
|
||||
editor.setCursorScreenPosition([2, 2])
|
||||
editor.moveLeft(34)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 29]
|
||||
|
||||
it "moves the cursor to the beginning columnCount is longer than the position in the buffer", ->
|
||||
editor.setCursorScreenPosition([1, 0])
|
||||
editor.moveLeft(100)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
|
||||
|
||||
describe "when the cursor is in the first column", ->
|
||||
describe "when there is a previous line", ->
|
||||
it "wraps to the end of the previous line", ->
|
||||
@@ -328,12 +339,28 @@ describe "Editor", ->
|
||||
editor.moveLeft()
|
||||
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: buffer.lineForRow(0).length)
|
||||
|
||||
it "moves the cursor by one row up and n columns to the left", ->
|
||||
editor.setCursorScreenPosition([1, 0])
|
||||
editor.moveLeft(4)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 26]
|
||||
|
||||
describe "when the next line is empty", ->
|
||||
it "wraps to the beginning of the previous line", ->
|
||||
editor.setCursorScreenPosition([11, 0])
|
||||
editor.moveLeft()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [10, 0]
|
||||
|
||||
describe "when the cursor is on the first line", ->
|
||||
it "remains in the same position (0,0)", ->
|
||||
editor.setCursorScreenPosition(row: 0, column: 0)
|
||||
editor.moveLeft()
|
||||
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
|
||||
|
||||
it "remains in the same position (0,0) when columnCount is specified", ->
|
||||
editor.setCursorScreenPosition([0, 0])
|
||||
editor.moveLeft(4)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
|
||||
|
||||
describe "when softTabs is enabled and the cursor is preceded by leading whitespace", ->
|
||||
it "skips tabLength worth of whitespace at a time", ->
|
||||
editor.setCursorBufferPosition([5, 6])
|
||||
@@ -368,6 +395,21 @@ describe "Editor", ->
|
||||
editor.moveRight()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [3, 4]
|
||||
|
||||
it "moves the cursor by n columns to the right", ->
|
||||
editor.setCursorScreenPosition([3, 7])
|
||||
editor.moveRight(4)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [3, 11]
|
||||
|
||||
it "moves the cursor by two rows down when the columnCount is longer than an entire line", ->
|
||||
editor.setCursorScreenPosition([0, 29])
|
||||
editor.moveRight(34)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [2, 2]
|
||||
|
||||
it "moves the cursor to the end of the buffer when columnCount is longer than the number of characters following the cursor position", ->
|
||||
editor.setCursorScreenPosition([11, 5])
|
||||
editor.moveRight(100)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [12, 2]
|
||||
|
||||
describe "when the cursor is on the last column of a line", ->
|
||||
describe "when there is a subsequent line", ->
|
||||
it "wraps to the beginning of the next line", ->
|
||||
@@ -375,6 +417,17 @@ describe "Editor", ->
|
||||
editor.moveRight()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [1, 0]
|
||||
|
||||
it "moves the cursor by one row down and n columns to the right", ->
|
||||
editor.setCursorScreenPosition([0, buffer.lineForRow(0).length])
|
||||
editor.moveRight(4)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [1, 3]
|
||||
|
||||
describe "when the next line is empty", ->
|
||||
it "wraps to the beginning of the next line", ->
|
||||
editor.setCursorScreenPosition([9, 4])
|
||||
editor.moveRight()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [10, 0]
|
||||
|
||||
describe "when the cursor is on the last line", ->
|
||||
it "remains in the same position", ->
|
||||
lastLineIndex = buffer.getLines().length - 1
|
||||
@@ -427,7 +480,7 @@ describe "Editor", ->
|
||||
describe ".moveToBeginningOfScreenLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.moveToBeginningOfScreenLine()
|
||||
@@ -447,7 +500,7 @@ describe "Editor", ->
|
||||
describe ".moveToEndOfScreenLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.moveToEndOfScreenLine()
|
||||
@@ -466,7 +519,7 @@ describe "Editor", ->
|
||||
|
||||
describe ".moveToBeginningOfLine()", ->
|
||||
it "moves cursor to the beginning of the buffer line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.moveToBeginningOfLine()
|
||||
@@ -475,7 +528,7 @@ describe "Editor", ->
|
||||
|
||||
describe ".moveToEndOfLine()", ->
|
||||
it "moves cursor to the end of the buffer line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([0, 2])
|
||||
editor.moveToEndOfLine()
|
||||
@@ -485,7 +538,7 @@ describe "Editor", ->
|
||||
describe ".moveToFirstCharacterOfLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves to the first character of the current screen line or the beginning of the screen line if it's already on the first character", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition [2,5]
|
||||
editor.addCursorAtScreenPosition [8,7]
|
||||
@@ -725,37 +778,6 @@ describe "Editor", ->
|
||||
editor.setCursorBufferPosition([3, 1])
|
||||
expect(editor.getCurrentParagraphBufferRange()).toBeUndefined()
|
||||
|
||||
describe "cursor-moved events", ->
|
||||
cursorMovedHandler = null
|
||||
|
||||
beforeEach ->
|
||||
editor.foldBufferRow(4)
|
||||
editor.setSelectedBufferRange([[8, 1], [9, 0]])
|
||||
cursorMovedHandler = jasmine.createSpy("cursorMovedHandler")
|
||||
editor.on 'cursor-moved', cursorMovedHandler
|
||||
|
||||
describe "when the position of the cursor changes", ->
|
||||
it "emits a cursor-moved event", ->
|
||||
buffer.insert([9, 0], '...')
|
||||
expect(cursorMovedHandler).toHaveBeenCalledWith(
|
||||
oldBufferPosition: [9, 0]
|
||||
oldScreenPosition: [6, 0]
|
||||
newBufferPosition: [9, 3]
|
||||
newScreenPosition: [6, 3]
|
||||
textChanged: true
|
||||
)
|
||||
|
||||
describe "when the position of the associated selection's tail changes, but not the cursor's position", ->
|
||||
it "does not emit a cursor-moved event", ->
|
||||
buffer.insert([8, 0], '...')
|
||||
expect(cursorMovedHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "::getCursorBufferPositions()", ->
|
||||
it "returns the cursor positions in the order they were added", ->
|
||||
cursor1 = editor.addCursorAtBufferPosition([8, 5])
|
||||
cursor2 = editor.addCursorAtBufferPosition([4, 5])
|
||||
expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [8, 5], [4, 5]]
|
||||
|
||||
describe "::getCursorScreenPositions()", ->
|
||||
it "returns the cursor positions in the order they were added", ->
|
||||
editor.foldBufferRow(4)
|
||||
@@ -924,6 +946,27 @@ describe "Editor", ->
|
||||
expect(selection1.getScreenRange()).toEqual([[0, 9], [1, 21]])
|
||||
expect(selection1.isReversed()).toBeFalsy()
|
||||
|
||||
describe "when counts are passed into the selection functions", ->
|
||||
it "expands each selection to its cursor's new location", ->
|
||||
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[3,16], [3,21]]])
|
||||
[selection1, selection2] = editor.getSelections()
|
||||
|
||||
editor.selectRight(2)
|
||||
expect(selection1.getBufferRange()).toEqual [[0,9], [0,15]]
|
||||
expect(selection2.getBufferRange()).toEqual [[3,16], [3,23]]
|
||||
|
||||
editor.selectLeft(3)
|
||||
expect(selection1.getBufferRange()).toEqual [[0,9], [0,12]]
|
||||
expect(selection2.getBufferRange()).toEqual [[3,16], [3,20]]
|
||||
|
||||
editor.selectDown(3)
|
||||
expect(selection1.getBufferRange()).toEqual [[0,9], [3,12]]
|
||||
expect(selection2.getBufferRange()).toEqual [[3,16], [6,20]]
|
||||
|
||||
editor.selectUp(2)
|
||||
expect(selection1.getBufferRange()).toEqual [[0,9], [1,12]]
|
||||
expect(selection2.getBufferRange()).toEqual [[3,16], [4,20]]
|
||||
|
||||
describe ".selectToBufferPosition(bufferPosition)", ->
|
||||
it "expands the last selection to the given position", ->
|
||||
editor.setSelectedBufferRange([[3, 0], [4, 5]])
|
||||
@@ -1573,7 +1616,7 @@ describe "Editor", ->
|
||||
beforeEach ->
|
||||
editor.setSelectedBufferRange([[1, 0], [1, 2]])
|
||||
|
||||
it "will-insert-text and did-insert-text events are emitted when inserting text", ->
|
||||
it "replaces the selection with the given text", ->
|
||||
range = editor.insertText('xxx')
|
||||
expect(range).toEqual [ [[1, 0], [1, 3]] ]
|
||||
expect(buffer.lineForRow(1)).toBe 'xxxvar sort = function(items) {'
|
||||
@@ -1655,19 +1698,19 @@ describe "Editor", ->
|
||||
editor.insertText('holy cow')
|
||||
expect(editor.tokenizedLineForScreenRow(2).fold).toBeUndefined()
|
||||
|
||||
describe "when will-insert-text and did-insert-text events are used", ->
|
||||
describe "when there are ::onWillInsertText and ::onDidInsertText observers", ->
|
||||
beforeEach ->
|
||||
editor.setSelectedBufferRange([[1, 0], [1, 2]])
|
||||
|
||||
it "will-insert-text and did-insert-text events are emitted when inserting text", ->
|
||||
it "notifies the observers when inserting text", ->
|
||||
willInsertSpy = jasmine.createSpy().andCallFake ->
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) {'
|
||||
|
||||
didInsertSpy = jasmine.createSpy().andCallFake ->
|
||||
expect(buffer.lineForRow(1)).toBe 'xxxvar sort = function(items) {'
|
||||
|
||||
editor.on('will-insert-text', willInsertSpy)
|
||||
editor.on('did-insert-text', didInsertSpy)
|
||||
editor.onWillInsertText(willInsertSpy)
|
||||
editor.onDidInsertText(didInsertSpy)
|
||||
|
||||
expect(editor.insertText('xxx')).toBeTruthy()
|
||||
expect(buffer.lineForRow(1)).toBe 'xxxvar sort = function(items) {'
|
||||
@@ -1682,14 +1725,14 @@ describe "Editor", ->
|
||||
options = didInsertSpy.mostRecentCall.args[0]
|
||||
expect(options.text).toBe 'xxx'
|
||||
|
||||
it "text insertion is prevented when cancel is called from a will-insert-text handler", ->
|
||||
it "cancels text insertion when an ::onWillInsertText observer calls cancel on an event", ->
|
||||
willInsertSpy = jasmine.createSpy().andCallFake ({cancel}) ->
|
||||
cancel()
|
||||
|
||||
didInsertSpy = jasmine.createSpy()
|
||||
|
||||
editor.on('will-insert-text', willInsertSpy)
|
||||
editor.on('did-insert-text', didInsertSpy)
|
||||
editor.onWillInsertText(willInsertSpy)
|
||||
editor.onDidInsertText(didInsertSpy)
|
||||
|
||||
expect(editor.insertText('xxx')).toBe false
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) {'
|
||||
@@ -1846,7 +1889,7 @@ describe "Editor", ->
|
||||
beforeEach ->
|
||||
selection = editor.getLastSelection()
|
||||
changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
|
||||
selection.on 'screen-range-changed', changeScreenRangeHandler
|
||||
selection.onDidChangeRange changeScreenRangeHandler
|
||||
|
||||
describe "when the cursor is on the middle of the line", ->
|
||||
it "removes the character before the cursor", ->
|
||||
@@ -2344,7 +2387,7 @@ describe "Editor", ->
|
||||
describe ".cutToEndOfLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "cuts up to the end of the line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([2, 2])
|
||||
editor.cutToEndOfLine()
|
||||
@@ -2846,7 +2889,7 @@ describe "Editor", ->
|
||||
|
||||
describe "when soft wrap is enabled", ->
|
||||
it "deletes the entire line that the cursor is on", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorBufferPosition([6])
|
||||
|
||||
@@ -3344,7 +3387,7 @@ describe "Editor", ->
|
||||
describe ".setIndentationForBufferRow", ->
|
||||
describe "when the editor uses soft tabs but the row has hard tabs", ->
|
||||
it "only replaces whitespace characters", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setText("\t1\n\t2")
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.setIndentationForBufferRow(0, 2)
|
||||
@@ -3352,7 +3395,7 @@ describe "Editor", ->
|
||||
|
||||
describe "when the indentation level is a non-integer", ->
|
||||
it "does not throw an exception", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setText("\t1\n\t2")
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.setIndentationForBufferRow(0, 2.1)
|
||||
|
||||
4
spec/fixtures/packages/package-that-throws-on-deactivate/index.coffee
vendored
Normal file
4
spec/fixtures/packages/package-that-throws-on-deactivate/index.coffee
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports =
|
||||
activate: ->
|
||||
deactivate: -> throw new Error('Top that')
|
||||
serialize: ->
|
||||
@@ -111,10 +111,10 @@ describe "Git", ->
|
||||
fs.writeFileSync(filePath, 'ch ch changes')
|
||||
repo.getPathStatus(filePath)
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.on 'status-changed', statusHandler
|
||||
repo.onDidChangeStatus statusHandler
|
||||
repo.checkoutHead(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler.argsForCall[0][0..1]).toEqual [filePath, 0]
|
||||
expect(statusHandler.argsForCall[0][0]).toEqual {path: filePath, pathStatus: 0}
|
||||
|
||||
repo.checkoutHead(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
@@ -167,11 +167,11 @@ describe "Git", ->
|
||||
|
||||
it "trigger a status-changed event when the new status differs from the last cached one", ->
|
||||
statusHandler = jasmine.createSpy("statusHandler")
|
||||
repo.on 'status-changed', statusHandler
|
||||
repo.onDidChangeStatus statusHandler
|
||||
fs.writeFileSync(filePath, '')
|
||||
status = repo.getPathStatus(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler.argsForCall[0][0..1]).toEqual [filePath, status]
|
||||
expect(statusHandler.argsForCall[0][0]).toEqual {path: filePath, pathStatus: status}
|
||||
|
||||
fs.writeFileSync(filePath, 'abc')
|
||||
status = repo.getPathStatus(filePath)
|
||||
@@ -208,7 +208,7 @@ describe "Git", ->
|
||||
it "returns status information for all new and modified files", ->
|
||||
fs.writeFileSync(modifiedPath, 'making this path modified')
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.on 'statuses-changed', statusHandler
|
||||
repo.onDidChangeStatuses statusHandler
|
||||
repo.refreshStatus()
|
||||
|
||||
waitsFor ->
|
||||
@@ -232,19 +232,19 @@ describe "Git", ->
|
||||
editor.insertNewline()
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
atom.project.getRepo().onDidChangeStatus statusHandler
|
||||
editor.save()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
|
||||
|
||||
it "emits a status-changed event when a buffer is reloaded", ->
|
||||
fs.writeFileSync(editor.getPath(), 'changed')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
atom.project.getRepo().onDidChangeStatus statusHandler
|
||||
editor.getBuffer().reload()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
|
||||
editor.getBuffer().reload()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
@@ -252,11 +252,11 @@ describe "Git", ->
|
||||
fs.writeFileSync(editor.getPath(), 'changed')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.getBuffer().emit 'path-changed'
|
||||
atom.project.getRepo().onDidChangeStatus statusHandler
|
||||
editor.getBuffer().emitter.emit 'did-change-path'
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
editor.getBuffer().emit 'path-changed'
|
||||
expect(statusHandler).toHaveBeenCalledWith {path: editor.getPath(), pathStatus: 256}
|
||||
editor.getBuffer().emitter.emit 'did-change-path'
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
describe "when a project is deserialized", ->
|
||||
@@ -283,7 +283,7 @@ describe "Git", ->
|
||||
buffer.append('changes')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
project2.getRepo().on 'status-changed', statusHandler
|
||||
project2.getRepo().onDidChangeStatus statusHandler
|
||||
buffer.save()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith buffer.getPath(), 256
|
||||
expect(statusHandler).toHaveBeenCalledWith {path: buffer.getPath(), pathStatus: 256}
|
||||
|
||||
@@ -102,6 +102,6 @@ describe "Package", ->
|
||||
theme.activate()
|
||||
|
||||
it "deactivated event fires on .deactivate()", ->
|
||||
theme.on 'deactivated', spy = jasmine.createSpy()
|
||||
theme.onDidDeactivate spy = jasmine.createSpy()
|
||||
theme.deactivate()
|
||||
expect(spy).toHaveBeenCalled()
|
||||
|
||||
@@ -27,42 +27,91 @@ describe "PaneContainer", ->
|
||||
|
||||
it "preserves the active pane across serialization, independent of focus", ->
|
||||
pane3A.activate()
|
||||
expect(containerA.activePane).toBe pane3A
|
||||
expect(containerA.getActivePane()).toBe pane3A
|
||||
|
||||
containerB = containerA.testSerialization()
|
||||
[pane1B, pane2B, pane3B] = containerB.getPanes()
|
||||
expect(containerB.activePane).toBe pane3B
|
||||
expect(containerB.getActivePane()).toBe pane3B
|
||||
|
||||
describe "::activePane", ->
|
||||
it "does not allow the root pane to be destroyed", ->
|
||||
container = new PaneContainer
|
||||
container.getRoot().destroy()
|
||||
expect(container.getRoot()).toBeDefined()
|
||||
expect(container.getRoot().isDestroyed()).toBe false
|
||||
|
||||
describe "::getActivePane()", ->
|
||||
[container, pane1, pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer
|
||||
pane1 = container.root
|
||||
pane1 = container.getRoot()
|
||||
|
||||
it "references the first pane if no pane has been made active", ->
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
it "returns the first pane if no pane has been made active", ->
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
expect(pane1.isActive()).toBe true
|
||||
|
||||
it "references the most pane on which ::activate was most recently called", ->
|
||||
it "returns the most pane on which ::activate() was most recently called", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane2.activate()
|
||||
expect(container.activePane).toBe pane2
|
||||
expect(pane1.active).toBe false
|
||||
expect(pane2.active).toBe true
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
expect(pane1.isActive()).toBe false
|
||||
expect(pane2.isActive()).toBe true
|
||||
pane1.activate()
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
expect(pane2.active).toBe false
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
expect(pane1.isActive()).toBe true
|
||||
expect(pane2.isActive()).toBe false
|
||||
|
||||
it "is reassigned to the next pane if the current active pane is destroyed", ->
|
||||
it "returns the next pane if the current active pane is destroyed", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane2.activate()
|
||||
pane2.destroy()
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
expect(pane1.isActive()).toBe true
|
||||
|
||||
it "does not allow the root pane to be destroyed", ->
|
||||
pane1.destroy()
|
||||
expect(container.root).toBe pane1
|
||||
expect(pane1.isDestroyed()).toBe false
|
||||
describe "::onDidChangeActivePaneItem()", ->
|
||||
[container, pane1, pane2, observed] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer(root: new Pane(items: [new Object, new Object]))
|
||||
container.getRoot().splitRight(items: [new Object, new Object])
|
||||
[pane1, pane2] = container.getPanes()
|
||||
|
||||
observed = []
|
||||
container.onDidChangeActivePaneItem (item) -> observed.push(item)
|
||||
|
||||
it "invokes observers when the active item of the active pane changes", ->
|
||||
pane2.activateNextItem()
|
||||
pane2.activateNextItem()
|
||||
expect(observed).toEqual [pane2.itemAtIndex(1), pane2.itemAtIndex(0)]
|
||||
|
||||
it "invokes observers when the active pane changes", ->
|
||||
pane1.activate()
|
||||
pane2.activate()
|
||||
expect(observed).toEqual [pane1.itemAtIndex(0), pane2.itemAtIndex(0)]
|
||||
|
||||
describe "::observePanes()", ->
|
||||
it "invokes observers with all current and future panes", ->
|
||||
container = new PaneContainer
|
||||
container.getRoot().splitRight()
|
||||
[pane1, pane2] = container.getPanes()
|
||||
|
||||
observed = []
|
||||
container.observePanes (pane) -> observed.push(pane)
|
||||
|
||||
pane3 = pane2.splitDown()
|
||||
pane4 = pane2.splitRight()
|
||||
|
||||
expect(observed).toEqual [pane1, pane2, pane3, pane4]
|
||||
|
||||
describe "::observePaneItems()", ->
|
||||
it "invokes observers with all current and future pane items", ->
|
||||
container = new PaneContainer(root: new Pane(items: [new Object, new Object]))
|
||||
container.getRoot().splitRight(items: [new Object])
|
||||
[pane1, pane2] = container.getPanes()
|
||||
observed = []
|
||||
container.observePaneItems (pane) -> observed.push(pane)
|
||||
|
||||
pane3 = pane2.splitDown(items: [new Object])
|
||||
pane3.addItems([new Object, new Object])
|
||||
|
||||
expect(observed).toEqual container.getPaneItems()
|
||||
|
||||
@@ -21,39 +21,83 @@ describe "Pane", ->
|
||||
describe "construction", ->
|
||||
it "sets the active item to the first item", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
expect(pane.activeItem).toBe pane.items[0]
|
||||
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
|
||||
|
||||
it "compacts the items array", ->
|
||||
pane = new Pane(items: [undefined, new Item("A"), null, new Item("B")])
|
||||
expect(pane.items.length).toBe 2
|
||||
expect(pane.activeItem).toBe pane.items[0]
|
||||
expect(pane.getItems().length).toBe 2
|
||||
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
|
||||
|
||||
describe "::activate()", ->
|
||||
[container, pane1, pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer(root: new Pane)
|
||||
container.getRoot().splitRight()
|
||||
[pane1, pane2] = container.getPanes()
|
||||
|
||||
it "changes the active pane on the container", ->
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
pane1.activate()
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
pane2.activate()
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
it "invokes ::onDidChangeActivePane observers on the container", ->
|
||||
observed = []
|
||||
container.onDidChangeActivePane (activePane) -> observed.push(activePane)
|
||||
|
||||
pane1.activate()
|
||||
pane1.activate()
|
||||
pane2.activate()
|
||||
pane1.activate()
|
||||
expect(observed).toEqual [pane1, pane2, pane1]
|
||||
|
||||
it "invokes ::onDidChangeActive observers on the relevant panes", ->
|
||||
observed = []
|
||||
pane1.onDidChangeActive (active) -> observed.push(active)
|
||||
pane1.activate()
|
||||
pane2.activate()
|
||||
expect(observed).toEqual [true, false]
|
||||
|
||||
it "invokes ::onDidActivate() observers", ->
|
||||
eventCount = 0
|
||||
pane1.onDidActivate -> eventCount++
|
||||
pane1.activate()
|
||||
pane1.activate()
|
||||
pane2.activate()
|
||||
expect(eventCount).toBe 2
|
||||
|
||||
describe "::addItem(item, index)", ->
|
||||
it "adds the item at the given index", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
[item1, item2] = pane.items
|
||||
[item1, item2] = pane.getItems()
|
||||
item3 = new Item("C")
|
||||
pane.addItem(item3, 1)
|
||||
expect(pane.items).toEqual [item1, item3, item2]
|
||||
expect(pane.getItems()).toEqual [item1, item3, item2]
|
||||
|
||||
it "adds the item after the active item ", ->
|
||||
it "adds the item after the active item if no index is provided", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.activateItem(item2)
|
||||
item4 = new Item("D")
|
||||
pane.addItem(item4)
|
||||
expect(pane.items).toEqual [item1, item2, item4, item3]
|
||||
expect(pane.getItems()).toEqual [item1, item2, item4, item3]
|
||||
|
||||
it "sets the active item after adding the first item", ->
|
||||
pane = new Pane
|
||||
item = new Item("A")
|
||||
events = []
|
||||
pane.on 'item-added', -> events.push('item-added')
|
||||
pane.$activeItem.changes.onValue -> events.push('active-item-changed')
|
||||
|
||||
pane.addItem(item)
|
||||
expect(pane.activeItem).toBe item
|
||||
expect(events).toEqual ['item-added', 'active-item-changed']
|
||||
expect(pane.getActiveItem()).toBe item
|
||||
|
||||
it "invokes ::onDidAddItem() observers", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
events = []
|
||||
pane.onDidAddItem (event) -> events.push(event)
|
||||
|
||||
item = new Item("C")
|
||||
pane.addItem(item, 1)
|
||||
expect(events).toEqual [{item, index: 1}]
|
||||
|
||||
describe "::activateItem(item)", ->
|
||||
pane = null
|
||||
@@ -62,83 +106,102 @@ describe "Pane", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
|
||||
it "changes the active item to the current item", ->
|
||||
expect(pane.activeItem).toBe pane.items[0]
|
||||
pane.activateItem(pane.items[1])
|
||||
expect(pane.activeItem).toBe pane.items[1]
|
||||
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
|
||||
pane.activateItem(pane.itemAtIndex(1))
|
||||
expect(pane.getActiveItem()).toBe pane.itemAtIndex(1)
|
||||
|
||||
it "adds the given item if it isn't present in ::items", ->
|
||||
item = new Item("C")
|
||||
pane.activateItem(item)
|
||||
expect(item in pane.items).toBe true
|
||||
expect(pane.activeItem).toBe item
|
||||
expect(item in pane.getItems()).toBe true
|
||||
expect(pane.getActiveItem()).toBe item
|
||||
|
||||
it "invokes ::onDidChangeActiveItem() observers", ->
|
||||
observed = []
|
||||
pane.onDidChangeActiveItem (item) -> observed.push(item)
|
||||
pane.activateItem(pane.itemAtIndex(1))
|
||||
expect(observed).toEqual [pane.itemAtIndex(1)]
|
||||
|
||||
describe "::activateNextItem() and ::activatePreviousItem()", ->
|
||||
it "sets the active item to the next/previous item, looping around at either end", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.activatePreviousItem()
|
||||
expect(pane.activeItem).toBe item3
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
pane.activatePreviousItem()
|
||||
expect(pane.activeItem).toBe item2
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
pane.activateNextItem()
|
||||
expect(pane.activeItem).toBe item3
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
pane.activateNextItem()
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
|
||||
describe "::activateItemAtIndex(index)", ->
|
||||
it "activates the item at the given index", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.activateItemAtIndex(2)
|
||||
expect(pane.activeItem).toBe item3
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
pane.activateItemAtIndex(1)
|
||||
expect(pane.activeItem).toBe item2
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
pane.activateItemAtIndex(0)
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
|
||||
# Doesn't fail with out-of-bounds indices
|
||||
pane.activateItemAtIndex(100)
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.activateItemAtIndex(-1)
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
|
||||
describe "::destroyItem(item)", ->
|
||||
[pane, item1, item2, item3] = []
|
||||
|
||||
beforeEach ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
|
||||
it "removes the item from the items list", ->
|
||||
expect(pane.activeItem).toBe item1
|
||||
it "removes the item from the items list and destroyes it", ->
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.destroyItem(item2)
|
||||
expect(item2 in pane.items).toBe false
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(item2 in pane.getItems()).toBe false
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
|
||||
pane.destroyItem(item1)
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
it "invokes ::onWillDestroyItem() observers before destroying the item", ->
|
||||
events = []
|
||||
pane.onWillDestroyItem (event) ->
|
||||
expect(item2.isDestroyed()).toBe false
|
||||
events.push(event)
|
||||
|
||||
pane.destroyItem(item2)
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
expect(events).toEqual [{item: item2, index: 1}]
|
||||
|
||||
it "invokes ::onDidRemoveItem() observers", ->
|
||||
events = []
|
||||
pane.onDidRemoveItem (event) -> events.push(event)
|
||||
pane.destroyItem(item2)
|
||||
expect(events).toEqual [{item: item2, index: 1, destroyed: true}]
|
||||
|
||||
describe "when the destroyed item is the active item and is the first item", ->
|
||||
it "activates the next item", ->
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.destroyItem(item1)
|
||||
expect(pane.activeItem).toBe item2
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
|
||||
describe "when the destroyed item is the active item and is not the first item", ->
|
||||
beforeEach ->
|
||||
pane.activateItem(item2)
|
||||
|
||||
it "activates the previous item", ->
|
||||
expect(pane.activeItem).toBe item2
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
pane.destroyItem(item2)
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
it "emits 'item-removed' with the item, its index, and true indicating the item is being destroyed", ->
|
||||
pane.on 'item-removed', itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
|
||||
pane.destroyItem(item2)
|
||||
expect(itemRemovedHandler).toHaveBeenCalledWith(item2, 1, true)
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
|
||||
describe "if the item is modified", ->
|
||||
itemUri = null
|
||||
@@ -157,7 +220,7 @@ describe "Pane", ->
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(item1.save).toHaveBeenCalled()
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
describe "when the item has no uri", ->
|
||||
@@ -170,7 +233,7 @@ describe "Pane", ->
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(item1.saveAs).toHaveBeenCalledWith("/selected/path")
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
describe "if the [Don't Save] option is selected", ->
|
||||
@@ -179,7 +242,7 @@ describe "Pane", ->
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(item1.save).not.toHaveBeenCalled()
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
describe "if the [Cancel] option is selected", ->
|
||||
@@ -188,7 +251,7 @@ describe "Pane", ->
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(item1.save).not.toHaveBeenCalled()
|
||||
expect(item1 in pane.items).toBe true
|
||||
expect(item1 in pane.getItems()).toBe true
|
||||
expect(item1.isDestroyed()).toBe false
|
||||
|
||||
describe "when the last item is destroyed", ->
|
||||
@@ -197,7 +260,7 @@ describe "Pane", ->
|
||||
expect(atom.config.get('core.destroyEmptyPanes')).toBe false
|
||||
pane.destroyItem(item) for item in pane.getItems()
|
||||
expect(pane.isDestroyed()).toBe false
|
||||
expect(pane.activeItem).toBeUndefined()
|
||||
expect(pane.getActiveItem()).toBeUndefined()
|
||||
expect(-> pane.saveActiveItem()).not.toThrow()
|
||||
expect(-> pane.saveActiveItemAs()).not.toThrow()
|
||||
|
||||
@@ -210,10 +273,10 @@ describe "Pane", ->
|
||||
describe "::destroyActiveItem()", ->
|
||||
it "destroys the active item", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
activeItem = pane.activeItem
|
||||
activeItem = pane.getActiveItem()
|
||||
pane.destroyActiveItem()
|
||||
expect(activeItem.isDestroyed()).toBe true
|
||||
expect(activeItem in pane.items).toBe false
|
||||
expect(activeItem in pane.getItems()).toBe false
|
||||
|
||||
it "does not throw an exception if there are no more items", ->
|
||||
pane = new Pane
|
||||
@@ -222,27 +285,40 @@ describe "Pane", ->
|
||||
describe "::destroyItems()", ->
|
||||
it "destroys all items", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.destroyItems()
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
expect(item3.isDestroyed()).toBe true
|
||||
expect(pane.items).toEqual []
|
||||
expect(pane.getItems()).toEqual []
|
||||
|
||||
describe "::observeItems()", ->
|
||||
it "invokes the observer with all current and future items", ->
|
||||
pane = new Pane(items: [new Item, new Item])
|
||||
[item1, item2] = pane.getItems()
|
||||
|
||||
observed = []
|
||||
pane.observeItems (item) -> observed.push(item)
|
||||
|
||||
item3 = new Item
|
||||
pane.addItem(item3)
|
||||
|
||||
expect(observed).toEqual [item1, item2, item3]
|
||||
|
||||
describe "when an item emits a destroyed event", ->
|
||||
it "removes it from the list of items", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.items[1].destroy()
|
||||
expect(pane.items).toEqual [item1, item3]
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.itemAtIndex(1).destroy()
|
||||
expect(pane.getItems()).toEqual [item1, item3]
|
||||
|
||||
describe "::destroyInactiveItems()", ->
|
||||
it "destroys all items but the active item", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.activateItem(item2)
|
||||
pane.destroyInactiveItems()
|
||||
expect(pane.items).toEqual [item2]
|
||||
expect(pane.getItems()).toEqual [item2]
|
||||
|
||||
describe "::saveActiveItem()", ->
|
||||
pane = null
|
||||
@@ -253,30 +329,30 @@ describe "Pane", ->
|
||||
|
||||
describe "when the active item has a uri", ->
|
||||
beforeEach ->
|
||||
pane.activeItem.uri = "test"
|
||||
pane.getActiveItem().uri = "test"
|
||||
|
||||
describe "when the active item has a save method", ->
|
||||
it "saves the current item", ->
|
||||
pane.activeItem.save = jasmine.createSpy("save")
|
||||
pane.getActiveItem().save = jasmine.createSpy("save")
|
||||
pane.saveActiveItem()
|
||||
expect(pane.activeItem.save).toHaveBeenCalled()
|
||||
expect(pane.getActiveItem().save).toHaveBeenCalled()
|
||||
|
||||
describe "when the current item has no save method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.save).toBeUndefined()
|
||||
expect(pane.getActiveItem().save).toBeUndefined()
|
||||
pane.saveActiveItem()
|
||||
|
||||
describe "when the current item has no uri", ->
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens a save dialog and saves the current item as the selected path", ->
|
||||
pane.activeItem.saveAs = jasmine.createSpy("saveAs")
|
||||
pane.getActiveItem().saveAs = jasmine.createSpy("saveAs")
|
||||
pane.saveActiveItem()
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item has no saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
expect(pane.getActiveItem().saveAs).toBeUndefined()
|
||||
pane.saveActiveItem()
|
||||
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
|
||||
|
||||
@@ -289,22 +365,22 @@ describe "Pane", ->
|
||||
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens the save dialog and calls saveAs on the item with the selected path", ->
|
||||
pane.activeItem.path = __filename
|
||||
pane.activeItem.saveAs = jasmine.createSpy("saveAs")
|
||||
pane.getActiveItem().path = __filename
|
||||
pane.getActiveItem().saveAs = jasmine.createSpy("saveAs")
|
||||
pane.saveActiveItemAs()
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__filename)
|
||||
expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
expect(pane.getActiveItem().saveAs).toBeUndefined()
|
||||
pane.saveActiveItemAs()
|
||||
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
|
||||
|
||||
describe "::itemForUri(uri)", ->
|
||||
it "returns the item for which a call to .getUri() returns the given uri", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
|
||||
[item1, item2, item3] = pane.items
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
item1.uri = "a"
|
||||
item2.uri = "b"
|
||||
expect(pane.itemForUri("a")).toBe item1
|
||||
@@ -312,24 +388,32 @@ describe "Pane", ->
|
||||
expect(pane.itemForUri("bogus")).toBeUndefined()
|
||||
|
||||
describe "::moveItem(item, index)", ->
|
||||
it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
|
||||
[item1, item2, item3, item4] = pane.items
|
||||
pane.on 'item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler")
|
||||
[pane, item1, item2, item3, item4] = []
|
||||
|
||||
beforeEach ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
|
||||
[item1, item2, item3, item4] = pane.getItems()
|
||||
|
||||
it "moves the item to the given index and invokes ::onDidMoveItem observers", ->
|
||||
pane.moveItem(item1, 2)
|
||||
expect(pane.getItems()).toEqual [item2, item3, item1, item4]
|
||||
expect(itemMovedHandler).toHaveBeenCalledWith(item1, 2)
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(item2, 3)
|
||||
expect(pane.getItems()).toEqual [item3, item1, item4, item2]
|
||||
expect(itemMovedHandler).toHaveBeenCalledWith(item2, 3)
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(item2, 1)
|
||||
expect(pane.getItems()).toEqual [item3, item2, item1, item4]
|
||||
expect(itemMovedHandler).toHaveBeenCalledWith(item2, 1)
|
||||
|
||||
it "invokes ::onDidMoveItem() observers", ->
|
||||
events = []
|
||||
pane.onDidMoveItem (event) -> events.push(event)
|
||||
|
||||
pane.moveItem(item1, 2)
|
||||
pane.moveItem(item2, 3)
|
||||
expect(events).toEqual [
|
||||
{item: item1, oldIndex: 0, newIndex: 2}
|
||||
{item: item2, oldIndex: 0, newIndex: 3}
|
||||
]
|
||||
|
||||
describe "::moveItemToPane(item, pane, index)", ->
|
||||
[container, pane1, pane2] = []
|
||||
@@ -339,13 +423,20 @@ describe "Pane", ->
|
||||
pane1 = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
container = new PaneContainer(root: pane1)
|
||||
pane2 = pane1.splitRight(items: [new Item("D"), new Item("E")])
|
||||
[item1, item2, item3] = pane1.items
|
||||
[item4, item5] = pane2.items
|
||||
[item1, item2, item3] = pane1.getItems()
|
||||
[item4, item5] = pane2.getItems()
|
||||
|
||||
it "moves the item to the given pane at the given index", ->
|
||||
pane1.moveItemToPane(item2, pane2, 1)
|
||||
expect(pane1.items).toEqual [item1, item3]
|
||||
expect(pane2.items).toEqual [item4, item2, item5]
|
||||
expect(pane1.getItems()).toEqual [item1, item3]
|
||||
expect(pane2.getItems()).toEqual [item4, item2, item5]
|
||||
|
||||
it "invokes ::onDidRemoveItem() observers", ->
|
||||
events = []
|
||||
pane1.onDidRemoveItem (event) -> events.push(event)
|
||||
pane1.moveItemToPane(item2, pane2, 1)
|
||||
|
||||
expect(events).toEqual [{item: item2, index: 1, destroyed: false}]
|
||||
|
||||
describe "when the moved item the last item in the source pane", ->
|
||||
beforeEach ->
|
||||
@@ -368,22 +459,27 @@ describe "Pane", ->
|
||||
[pane1, container] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new Pane(items: ["A"])
|
||||
pane1 = new Pane(items: [new Item("A")])
|
||||
container = new PaneContainer(root: pane1)
|
||||
|
||||
describe "::splitLeft(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a row and inserts a new pane to the left of itself", ->
|
||||
pane2 = pane1.splitLeft(items: ["B"])
|
||||
pane3 = pane1.splitLeft(items: ["C"])
|
||||
pane2 = pane1.splitLeft(items: [new Item("B")])
|
||||
pane3 = pane1.splitLeft(items: [new Item("C")])
|
||||
expect(container.root.orientation).toBe 'horizontal'
|
||||
expect(container.root.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "when `copyActiveItem: true` is passed in the params", ->
|
||||
it "duplicates the active item", ->
|
||||
pane2 = pane1.splitLeft(copyActiveItem: true)
|
||||
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
|
||||
|
||||
describe "when the parent is a column", ->
|
||||
it "replaces itself with a row and inserts a new pane to the left of itself", ->
|
||||
pane1.splitDown()
|
||||
pane2 = pane1.splitLeft(items: ["B"])
|
||||
pane3 = pane1.splitLeft(items: ["C"])
|
||||
pane2 = pane1.splitLeft(items: [new Item("B")])
|
||||
pane3 = pane1.splitLeft(items: [new Item("C")])
|
||||
row = container.root.children[0]
|
||||
expect(row.orientation).toBe 'horizontal'
|
||||
expect(row.children).toEqual [pane2, pane3, pane1]
|
||||
@@ -391,16 +487,21 @@ describe "Pane", ->
|
||||
describe "::splitRight(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a row and inserts a new pane to the right of itself", ->
|
||||
pane2 = pane1.splitRight(items: ["B"])
|
||||
pane3 = pane1.splitRight(items: ["C"])
|
||||
pane2 = pane1.splitRight(items: [new Item("B")])
|
||||
pane3 = pane1.splitRight(items: [new Item("C")])
|
||||
expect(container.root.orientation).toBe 'horizontal'
|
||||
expect(container.root.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "when `copyActiveItem: true` is passed in the params", ->
|
||||
it "duplicates the active item", ->
|
||||
pane2 = pane1.splitRight(copyActiveItem: true)
|
||||
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
|
||||
|
||||
describe "when the parent is a column", ->
|
||||
it "replaces itself with a row and inserts a new pane to the right of itself", ->
|
||||
pane1.splitDown()
|
||||
pane2 = pane1.splitRight(items: ["B"])
|
||||
pane3 = pane1.splitRight(items: ["C"])
|
||||
pane2 = pane1.splitRight(items: [new Item("B")])
|
||||
pane3 = pane1.splitRight(items: [new Item("C")])
|
||||
row = container.root.children[0]
|
||||
expect(row.orientation).toBe 'horizontal'
|
||||
expect(row.children).toEqual [pane1, pane3, pane2]
|
||||
@@ -408,16 +509,21 @@ describe "Pane", ->
|
||||
describe "::splitUp(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a column and inserts a new pane above itself", ->
|
||||
pane2 = pane1.splitUp(items: ["B"])
|
||||
pane3 = pane1.splitUp(items: ["C"])
|
||||
pane2 = pane1.splitUp(items: [new Item("B")])
|
||||
pane3 = pane1.splitUp(items: [new Item("C")])
|
||||
expect(container.root.orientation).toBe 'vertical'
|
||||
expect(container.root.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "when `copyActiveItem: true` is passed in the params", ->
|
||||
it "duplicates the active item", ->
|
||||
pane2 = pane1.splitUp(copyActiveItem: true)
|
||||
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
|
||||
|
||||
describe "when the parent is a row", ->
|
||||
it "replaces itself with a column and inserts a new pane above itself", ->
|
||||
pane1.splitRight()
|
||||
pane2 = pane1.splitUp(items: ["B"])
|
||||
pane3 = pane1.splitUp(items: ["C"])
|
||||
pane2 = pane1.splitUp(items: [new Item("B")])
|
||||
pane3 = pane1.splitUp(items: [new Item("C")])
|
||||
column = container.root.children[0]
|
||||
expect(column.orientation).toBe 'vertical'
|
||||
expect(column.children).toEqual [pane2, pane3, pane1]
|
||||
@@ -425,16 +531,21 @@ describe "Pane", ->
|
||||
describe "::splitDown(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a column and inserts a new pane below itself", ->
|
||||
pane2 = pane1.splitDown(items: ["B"])
|
||||
pane3 = pane1.splitDown(items: ["C"])
|
||||
pane2 = pane1.splitDown(items: [new Item("B")])
|
||||
pane3 = pane1.splitDown(items: [new Item("C")])
|
||||
expect(container.root.orientation).toBe 'vertical'
|
||||
expect(container.root.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "when `copyActiveItem: true` is passed in the params", ->
|
||||
it "duplicates the active item", ->
|
||||
pane2 = pane1.splitDown(copyActiveItem: true)
|
||||
expect(pane2.getActiveItem()).toEqual pane1.getActiveItem()
|
||||
|
||||
describe "when the parent is a row", ->
|
||||
it "replaces itself with a column and inserts a new pane below itself", ->
|
||||
pane1.splitRight()
|
||||
pane2 = pane1.splitDown(items: ["B"])
|
||||
pane3 = pane1.splitDown(items: ["C"])
|
||||
pane2 = pane1.splitDown(items: [new Item("B")])
|
||||
pane3 = pane1.splitDown(items: [new Item("C")])
|
||||
column = container.root.children[0]
|
||||
expect(column.orientation).toBe 'vertical'
|
||||
expect(column.children).toEqual [pane1, pane3, pane2]
|
||||
@@ -455,7 +566,7 @@ describe "Pane", ->
|
||||
pane2 = pane1.splitRight()
|
||||
|
||||
it "destroys the pane's destroyable items", ->
|
||||
[item1, item2] = pane1.items
|
||||
[item1, item2] = pane1.getItems()
|
||||
pane1.destroy()
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
@@ -493,12 +604,12 @@ describe "Pane", ->
|
||||
|
||||
it "can serialize and deserialize the pane and all its items", ->
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.items).toEqual pane.items
|
||||
expect(newPane.getItems()).toEqual pane.getItems()
|
||||
|
||||
it "restores the active item on deserialization", ->
|
||||
pane.activateItemAtIndex(1)
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.activeItem).toEqual newPane.items[1]
|
||||
expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1)
|
||||
|
||||
it "does not include items that cannot be deserialized", ->
|
||||
spyOn(console, 'warn')
|
||||
@@ -506,8 +617,8 @@ describe "Pane", ->
|
||||
pane.activateItem(unserializable)
|
||||
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.activeItem).toEqual pane.items[0]
|
||||
expect(newPane.items.length).toBe pane.items.length - 1
|
||||
expect(newPane.getActiveItem()).toEqual pane.itemAtIndex(0)
|
||||
expect(newPane.getItems().length).toBe pane.getItems().length - 1
|
||||
|
||||
it "includes the pane's focus state in the serialized state", ->
|
||||
pane.focus()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
PaneContainerView = require '../src/pane-container-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
fs = require 'fs-plus'
|
||||
{Emitter} = require 'event-kit'
|
||||
{$, View} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
@@ -12,9 +13,14 @@ describe "PaneView", ->
|
||||
@deserialize: ({id, text}) -> new TestView({id, text})
|
||||
@content: ({id, text}) -> @div class: 'test-view', id: id, tabindex: -1, text
|
||||
initialize: ({@id, @text}) ->
|
||||
@emitter = new Emitter
|
||||
serialize: -> { deserializer: 'TestView', @id, @text }
|
||||
getUri: -> @id
|
||||
isEqual: (other) -> other? and @id == other.id and @text == other.text
|
||||
changeTitle: ->
|
||||
@emitter.emit 'did-change-title', 'title'
|
||||
onDidChangeTitle: (callback) ->
|
||||
@emitter.on 'did-change-title', callback
|
||||
|
||||
beforeEach ->
|
||||
atom.deserializers.add(TestView)
|
||||
@@ -29,7 +35,7 @@ describe "PaneView", ->
|
||||
|
||||
runs ->
|
||||
pane = container.getRoot()
|
||||
paneModel = pane.model
|
||||
paneModel = pane.getModel()
|
||||
paneModel.addItems([view1, editor1, view2, editor2])
|
||||
|
||||
afterEach ->
|
||||
@@ -37,7 +43,7 @@ describe "PaneView", ->
|
||||
|
||||
describe "when the active pane item changes", ->
|
||||
it "hides all item views except the active one", ->
|
||||
expect(pane.activeItem).toBe view1
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
expect(view1.css('display')).not.toBe 'none'
|
||||
|
||||
pane.activateItem(view2)
|
||||
@@ -48,7 +54,7 @@ describe "PaneView", ->
|
||||
itemChangedHandler = jasmine.createSpy("itemChangedHandler")
|
||||
container.on 'pane:active-item-changed', itemChangedHandler
|
||||
|
||||
expect(pane.activeItem).toBe view1
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
paneModel.activateItem(view2)
|
||||
paneModel.activateItem(view2)
|
||||
|
||||
@@ -145,22 +151,47 @@ describe "PaneView", ->
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
|
||||
describe "when the title of the active item changes", ->
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
describe 'when there is no onDidChangeTitle method', ->
|
||||
beforeEach ->
|
||||
view1.onDidChangeTitle = null
|
||||
view2.onDidChangeTitle = null
|
||||
|
||||
expect(pane.activeItem).toBe view1
|
||||
pane.activateItem(view2)
|
||||
pane.activateItem(view1)
|
||||
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
|
||||
view1.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
view1.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe 'when there is a onDidChangeTitle method', ->
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
view2.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
view1.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when an unmodifed buffer's path is deleted", ->
|
||||
it "removes the pane item", ->
|
||||
@@ -246,7 +277,7 @@ describe "PaneView", ->
|
||||
|
||||
it "transfers focus to the active view", ->
|
||||
focusHandler = jasmine.createSpy("focusHandler")
|
||||
pane.activeItem.on 'focus', focusHandler
|
||||
pane.getActiveItem().on 'focus', focusHandler
|
||||
pane.focus()
|
||||
expect(focusHandler).toHaveBeenCalled()
|
||||
|
||||
@@ -259,7 +290,7 @@ describe "PaneView", ->
|
||||
describe "when a pane is split", ->
|
||||
it "builds the appropriate pane-row and pane-column views", ->
|
||||
pane1 = pane
|
||||
pane1Model = pane.model
|
||||
pane1Model = pane.getModel()
|
||||
pane.activateItem(editor1)
|
||||
|
||||
pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()])
|
||||
|
||||
@@ -50,9 +50,9 @@ describe "Editor", ->
|
||||
|
||||
randomlyMutateEditor = ->
|
||||
if Math.random() < .2
|
||||
softWrap = not editor.getSoftWrap()
|
||||
steps.push(['setSoftWrap', softWrap])
|
||||
editor.setSoftWrap(softWrap)
|
||||
softWrapped = not editor.isSoftWrapped()
|
||||
steps.push(['setSoftWrapped', softWrapped])
|
||||
editor.setSoftWrapped(softWrapped)
|
||||
else
|
||||
range = getRandomRange()
|
||||
text = getRandomText()
|
||||
@@ -79,7 +79,7 @@ describe "Editor", ->
|
||||
text
|
||||
|
||||
getReferenceScreenLines = ->
|
||||
if editor.getSoftWrap()
|
||||
if editor.isSoftWrapped()
|
||||
screenLines = []
|
||||
bufferRows = []
|
||||
for bufferRow in [0..tokenizedBuffer.getLastRow()]
|
||||
|
||||
@@ -57,10 +57,10 @@ describe "Selection", ->
|
||||
expect(selection.isReversed()).toBeFalsy()
|
||||
|
||||
describe "when only the selection's tail is moved (regression)", ->
|
||||
it "emits the 'screen-range-changed' event", ->
|
||||
it "notifies ::onDidChangeRange observers", ->
|
||||
selection.setBufferRange([[2, 0], [2, 10]], reversed: true)
|
||||
changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
|
||||
selection.on 'screen-range-changed', changeScreenRangeHandler
|
||||
selection.onDidChangeRange changeScreenRangeHandler
|
||||
|
||||
buffer.insert([2, 5], 'abc')
|
||||
expect(changeScreenRangeHandler).toHaveBeenCalled()
|
||||
|
||||
@@ -21,6 +21,7 @@ clipboard = require 'clipboard'
|
||||
|
||||
atom.themes.loadBaseStylesheets()
|
||||
atom.themes.requireStylesheet '../static/jasmine'
|
||||
atom.themes.initialLoadComplete = true
|
||||
|
||||
fixturePackagesPath = path.resolve(__dirname, './fixtures/packages')
|
||||
atom.packages.packageDirPaths.unshift(fixturePackagesPath)
|
||||
@@ -137,8 +138,7 @@ afterEach ->
|
||||
|
||||
jasmine.unspy(atom, 'saveSync')
|
||||
ensureNoPathSubscriptions()
|
||||
atom.syntax.off()
|
||||
ensureNoDeprecatedFunctionsCalled() if isCoreSpec
|
||||
atom.syntax.clearObservers()
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
|
||||
ensureNoPathSubscriptions = ->
|
||||
|
||||
@@ -69,7 +69,7 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "when the core.themes config value changes", ->
|
||||
it "add/removes stylesheets to reflect the new config value", ->
|
||||
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
|
||||
themeManager.onDidReloadAll reloadHandler = jasmine.createSpy()
|
||||
spyOn(themeManager, 'getUserStylesheetPath').andCallFake -> null
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -131,8 +131,8 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "requireStylesheet(path)", ->
|
||||
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
cssPath = atom.project.resolve('css.css')
|
||||
lengthBefore = $('head style').length
|
||||
|
||||
@@ -193,8 +193,8 @@ describe "ThemeManager", ->
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($(document.body).css('font-weight')).toBe("bold")
|
||||
|
||||
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
|
||||
themeManager.removeStylesheet(cssPath)
|
||||
|
||||
@@ -217,7 +217,7 @@ describe "ThemeManager", ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
it "loads the correct values from the theme's ui-variables file", ->
|
||||
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
|
||||
themeManager.onDidReloadAll reloadHandler = jasmine.createSpy()
|
||||
atom.config.set('core.themes', ['theme-with-ui-variables'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -234,7 +234,7 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "when there is a theme with incomplete variables", ->
|
||||
it "loads the correct values from the fallback ui-variables", ->
|
||||
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
|
||||
themeManager.onDidReloadAll reloadHandler = jasmine.createSpy()
|
||||
atom.config.set('core.themes', ['theme-with-incomplete-ui-variables'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -251,7 +251,7 @@ describe "ThemeManager", ->
|
||||
it 'adds theme-* classes to the workspace for each active theme', ->
|
||||
expect(atom.workspaceView).toHaveClass 'theme-atom-dark-ui'
|
||||
|
||||
themeManager.on 'reloaded', reloadHandler = jasmine.createSpy()
|
||||
themeManager.onDidReloadAll reloadHandler = jasmine.createSpy()
|
||||
atom.config.set('core.themes', ['theme-with-ui-variables'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -273,9 +273,9 @@ describe "ThemeManager", ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
runs ->
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
|
||||
|
||||
expect($(document.body).css('border-style')).toBe 'dotted'
|
||||
@@ -316,7 +316,9 @@ describe "ThemeManager", ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
runs ->
|
||||
themeManager.once 'reloaded', -> reloaded = true
|
||||
disposable = themeManager.onDidReloadAll ->
|
||||
disposable.dispose()
|
||||
reloaded = true
|
||||
spyOn(console, 'warn')
|
||||
expect(-> atom.config.set('core.themes', ['atom-light-ui', 'theme-really-does-not-exist'])).not.toThrow()
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
afterEach ->
|
||||
tokenizedBuffer.destroy()
|
||||
@@ -468,7 +468,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer.on 'tokenized', tokenizedHandler
|
||||
tokenizedBuffer.onDidTokenize tokenizedHandler
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedHandler.callCount).toBe(1)
|
||||
|
||||
@@ -483,7 +483,7 @@ describe "TokenizedBuffer", ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
tokenizedBuffer.on 'tokenized', tokenizedHandler
|
||||
tokenizedBuffer.onDidTokenize tokenizedHandler
|
||||
editor.getBuffer().insert([0, 0], "'")
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedHandler).not.toHaveBeenCalled()
|
||||
@@ -499,7 +499,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer.on 'tokenized', tokenizedHandler
|
||||
tokenizedBuffer.onDidTokenize tokenizedHandler
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
tokenizedHandler.reset()
|
||||
|
||||
@@ -753,7 +753,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 2
|
||||
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
buffer.setTextInRange([[7, 0], [8, 65]], ' one\n two\n three\n four')
|
||||
|
||||
@@ -771,7 +771,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.insert([7, 0], '\n\n')
|
||||
buffer.insert([5, 0], '\n\n')
|
||||
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
buffer.setTextInRange([[7, 0], [8, 65]], ' ok')
|
||||
|
||||
|
||||
@@ -256,3 +256,35 @@ describe "Window", ->
|
||||
|
||||
elements.trigger "core:focus-previous"
|
||||
expect(elements.find("[tabindex=1]:focus")).toExist()
|
||||
|
||||
describe "the window:open-path event", ->
|
||||
beforeEach ->
|
||||
spyOn(atom.workspace, 'open')
|
||||
|
||||
describe "when the project does not have a path", ->
|
||||
beforeEach ->
|
||||
atom.project.setPath()
|
||||
|
||||
describe "when the opened path exists", ->
|
||||
it "sets the project path to the opened path", ->
|
||||
$(window).trigger('window:open-path', [{pathToOpen: __filename}])
|
||||
|
||||
expect(atom.project.getPath()).toBe __dirname
|
||||
|
||||
describe "when the opened path does not exist but its parent directory does", ->
|
||||
it "sets the project path to the opened path's parent directory", ->
|
||||
$(window).trigger('window:open-path', [{pathToOpen: path.join(__dirname, 'this-path-does-not-exist.txt')}])
|
||||
|
||||
expect(atom.project.getPath()).toBe __dirname
|
||||
|
||||
describe "when the opened path is a file", ->
|
||||
it "opens it in the workspace", ->
|
||||
$(window).trigger('window:open-path', [{pathToOpen: __filename}])
|
||||
|
||||
expect(atom.workspace.open.mostRecentCall.args[0]).toBe __filename
|
||||
|
||||
describe "when the opened path is a directory", ->
|
||||
it "does not open it in the workspace", ->
|
||||
$(window).trigger('window:open-path', [{pathToOpen: __dirname}])
|
||||
|
||||
expect(atom.workspace.open.callCount).toBe 0
|
||||
|
||||
@@ -8,8 +8,12 @@ describe "Workspace", ->
|
||||
atom.workspace = workspace = new Workspace
|
||||
|
||||
describe "::open(uri, options)", ->
|
||||
openEvents = null
|
||||
|
||||
beforeEach ->
|
||||
spyOn(workspace.activePane, 'activate').andCallThrough()
|
||||
openEvents = []
|
||||
workspace.onDidOpen (event) -> openEvents.push(event)
|
||||
spyOn(workspace.getActivePane(), 'activate').andCallThrough()
|
||||
|
||||
describe "when the 'searchAllPanes' option is false (default)", ->
|
||||
describe "when called without a uri", ->
|
||||
@@ -21,18 +25,21 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
expect(editor1.getPath()).toBeUndefined()
|
||||
expect(workspace.activePane.items).toEqual [editor1]
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
expect(workspace.getActivePane().items).toEqual [editor1]
|
||||
expect(workspace.getActivePaneItem()).toBe editor1
|
||||
expect(workspace.getActivePane().activate).toHaveBeenCalled()
|
||||
expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor1, index: 0}]
|
||||
openEvents = []
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open().then (editor) -> editor2 = editor
|
||||
|
||||
runs ->
|
||||
expect(editor2.getPath()).toBeUndefined()
|
||||
expect(workspace.activePane.items).toEqual [editor1, editor2]
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
expect(workspace.getActivePane().items).toEqual [editor1, editor2]
|
||||
expect(workspace.getActivePaneItem()).toBe editor2
|
||||
expect(workspace.getActivePane().activate).toHaveBeenCalled()
|
||||
expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor2, index: 1}]
|
||||
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
@@ -51,8 +58,29 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe editor1
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
expect(workspace.getActivePaneItem()).toBe editor
|
||||
expect(workspace.getActivePane().activate).toHaveBeenCalled()
|
||||
|
||||
expect(openEvents).toEqual [
|
||||
{
|
||||
uri: atom.project.resolve('a')
|
||||
item: editor1
|
||||
pane: atom.workspace.getActivePane()
|
||||
index: 0
|
||||
}
|
||||
{
|
||||
uri: atom.project.resolve('b')
|
||||
item: editor2
|
||||
pane: atom.workspace.getActivePane()
|
||||
index: 1
|
||||
}
|
||||
{
|
||||
uri: atom.project.resolve('a')
|
||||
item: editor1
|
||||
pane: atom.workspace.getActivePane()
|
||||
index: 0
|
||||
}
|
||||
]
|
||||
|
||||
describe "when the active pane does not have an editor for the given uri", ->
|
||||
it "adds and activates a new editor for the given path on the active pane", ->
|
||||
@@ -62,9 +90,9 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
expect(editor.getUri()).toBe atom.project.resolve('a')
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.items).toEqual [editor]
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
expect(workspace.getActivePaneItem()).toBe editor
|
||||
expect(workspace.getActivePane().items).toEqual [editor]
|
||||
expect(workspace.getActivePane().activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the 'searchAllPanes' option is true", ->
|
||||
describe "when an editor for the given uri is already open on an inactive pane", ->
|
||||
@@ -83,14 +111,14 @@ describe "Workspace", ->
|
||||
workspace.open('b').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
expect(workspace.getActivePaneItem()).toBe editor2
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', searchAllPanes: true)
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(workspace.getActivePaneItem()).toBe editor1
|
||||
|
||||
describe "when no editor for the given uri is open in any pane", ->
|
||||
it "opens an editor for the given uri in the active pane", ->
|
||||
@@ -99,21 +127,21 @@ describe "Workspace", ->
|
||||
workspace.open('a', searchAllPanes: true).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.getActivePaneItem()).toBe editor
|
||||
|
||||
describe "when the 'split' option is set", ->
|
||||
describe "when the 'split' option is 'left'", ->
|
||||
it "opens the editor in the leftmost pane of the current pane axis", ->
|
||||
pane1 = workspace.activePane
|
||||
pane1 = workspace.getActivePane()
|
||||
pane2 = pane1.splitRight()
|
||||
expect(workspace.activePane).toBe pane2
|
||||
expect(workspace.getActivePane()).toBe pane2
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
@@ -123,37 +151,37 @@ describe "Workspace", ->
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
describe "when a pane axis is the leftmost sibling of the current pane", ->
|
||||
it "opens the new item in the current pane", ->
|
||||
editor = null
|
||||
pane1 = workspace.activePane
|
||||
pane1 = workspace.getActivePane()
|
||||
pane2 = pane1.splitLeft()
|
||||
pane3 = pane2.splitDown()
|
||||
pane1.activate()
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
|
||||
describe "when the 'split' option is 'right'", ->
|
||||
it "opens the editor in the rightmost pane of the current pane axis", ->
|
||||
editor = null
|
||||
pane1 = workspace.activePane
|
||||
pane1 = workspace.getActivePane()
|
||||
pane2 = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'right').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
pane2 = workspace.getPanes().filter((p) -> p != pane1)[0]
|
||||
expect(workspace.activePane).toBe pane2
|
||||
expect(workspace.getActivePane()).toBe pane2
|
||||
expect(pane1.items).toEqual []
|
||||
expect(pane2.items).toEqual [editor]
|
||||
|
||||
@@ -163,18 +191,18 @@ describe "Workspace", ->
|
||||
workspace.open('a', split: 'right').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane2
|
||||
expect(workspace.getActivePane()).toBe pane2
|
||||
expect(pane1.items).toEqual []
|
||||
expect(pane2.items).toEqual [editor]
|
||||
|
||||
describe "when a pane axis is the rightmost sibling of the current pane", ->
|
||||
it "opens the new item in a new pane split to the right of the current pane", ->
|
||||
editor = null
|
||||
pane1 = workspace.activePane
|
||||
pane1 = workspace.getActivePane()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitDown()
|
||||
pane1.activate()
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.getActivePane()).toBe pane1
|
||||
pane4 = null
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -182,7 +210,7 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
pane4 = workspace.getPanes().filter((p) -> p != pane1)[0]
|
||||
expect(workspace.activePane).toBe pane4
|
||||
expect(workspace.getActivePane()).toBe pane4
|
||||
expect(pane4.items).toEqual [editor]
|
||||
expect(workspace.paneContainer.root.children[0]).toBe pane1
|
||||
expect(workspace.paneContainer.root.children[1]).toBe pane4
|
||||
@@ -203,21 +231,21 @@ describe "Workspace", ->
|
||||
workspace.open("bar://baz").then (item) ->
|
||||
expect(item).toEqual { bar: "bar://baz" }
|
||||
|
||||
it "emits an 'editor-created' event", ->
|
||||
it "notifies ::onDidAddTextEditor observers", ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
workspace.on 'editor-created', newEditorHandler
|
||||
workspace.onDidAddTextEditor newEditorHandler
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open(absolutePath).then (e) -> editor = e
|
||||
|
||||
runs ->
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
expect(newEditorHandler.argsForCall[0][0].textEditor).toBe editor
|
||||
|
||||
describe "::reopenItem()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
pane = workspace.activePane
|
||||
pane = workspace.getActivePane()
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then ->
|
||||
workspace.open('b').then ->
|
||||
@@ -226,44 +254,44 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
# does not reopen items with no uri
|
||||
expect(workspace.activePaneItem.getUri()).toBeUndefined()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBeUndefined()
|
||||
pane.destroyActiveItem()
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
|
||||
expect(workspace.getActivePaneItem().getUri()).not.toBeUndefined()
|
||||
|
||||
# destroy all items
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a')
|
||||
pane.destroyActiveItem()
|
||||
|
||||
# reopens items with uris
|
||||
expect(workspace.activePaneItem).toBeUndefined()
|
||||
expect(workspace.getActivePaneItem()).toBeUndefined()
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a')
|
||||
|
||||
# does not reopen items that are already open
|
||||
waitsForPromise ->
|
||||
workspace.open('b')
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b')
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1')
|
||||
|
||||
describe "::increase/decreaseFontSize()", ->
|
||||
it "increases/decreases the font size without going below 1", ->
|
||||
@@ -282,7 +310,22 @@ describe "Workspace", ->
|
||||
describe "::openLicense()", ->
|
||||
it "opens the license as plain-text in a buffer", ->
|
||||
waitsForPromise -> workspace.openLicense()
|
||||
runs -> expect(workspace.activePaneItem.getText()).toMatch /Copyright/
|
||||
runs -> expect(workspace.getActivePaneItem().getText()).toMatch /Copyright/
|
||||
|
||||
describe "::observeTextEditors()", ->
|
||||
it "invokes the observer with current and future text editors", ->
|
||||
observed = []
|
||||
|
||||
waitsForPromise -> workspace.open()
|
||||
waitsForPromise -> workspace.open()
|
||||
waitsForPromise -> workspace.openLicense()
|
||||
|
||||
runs ->
|
||||
workspace.observeTextEditors (editor) -> observed.push(editor)
|
||||
|
||||
waitsForPromise -> workspace.open()
|
||||
|
||||
expect(observed).toEqual workspace.getTextEditors()
|
||||
|
||||
describe "when an editor is destroyed", ->
|
||||
it "removes the editor", ->
|
||||
@@ -292,23 +335,9 @@ describe "Workspace", ->
|
||||
workspace.open("a").then (e) -> editor = e
|
||||
|
||||
runs ->
|
||||
expect(workspace.getEditors()).toHaveLength 1
|
||||
expect(workspace.getTextEditors()).toHaveLength 1
|
||||
editor.destroy()
|
||||
expect(workspace.getEditors()).toHaveLength 0
|
||||
|
||||
describe "when an editor is copied", ->
|
||||
it "emits an 'editor-created' event", ->
|
||||
editor = null
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
workspace.on 'editor-created', handler
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open("a").then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(handler.callCount).toBe 1
|
||||
editorCopy = editor.copy()
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(workspace.getTextEditors()).toHaveLength 0
|
||||
|
||||
it "stores the active grammars used by all the open editors", ->
|
||||
waitsForPromise ->
|
||||
@@ -317,14 +346,19 @@ describe "Workspace", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-todo')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.coffee')
|
||||
|
||||
runs ->
|
||||
atom.workspace.getActiveEditor().setText('i = /test/;')
|
||||
atom.workspace.getActiveEditor().setText """
|
||||
i = /test/; #FIXME
|
||||
"""
|
||||
|
||||
state = atom.workspace.serialize()
|
||||
expect(state.packagesWithActiveGrammars).toEqual ['language-coffee-script', 'language-javascript']
|
||||
expect(state.packagesWithActiveGrammars).toEqual ['language-coffee-script', 'language-javascript', 'language-todo']
|
||||
|
||||
jsPackage = atom.packages.getLoadedPackage('language-javascript')
|
||||
coffeePackage = atom.packages.getLoadedPackage('language-coffee-script')
|
||||
|
||||
@@ -42,7 +42,7 @@ describe "WorkspaceView", ->
|
||||
runs ->
|
||||
editorView1 = atom.workspaceView.getActiveView()
|
||||
buffer = editorView1.getEditor().getBuffer()
|
||||
editorView1.splitRight()
|
||||
editorView1.getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
|
||||
simulateReload()
|
||||
@@ -196,7 +196,8 @@ describe "WorkspaceView", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
rightEditorView = atom.workspaceView.getActiveView()
|
||||
rightEditorView.getEditor().setText("\t \n")
|
||||
leftEditorView = rightEditorView.splitLeft()
|
||||
rightEditorView.getPaneView().getModel().splitLeft(copyActiveItem: true)
|
||||
leftEditorView = atom.workspaceView.getActiveView()
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
@@ -207,14 +208,16 @@ describe "WorkspaceView", ->
|
||||
expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
lowerLeftEditorView = leftEditorView.splitDown()
|
||||
leftEditorView.getPaneView().getModel().splitDown(copyActiveItem: true)
|
||||
lowerLeftEditorView = atom.workspaceView.getActiveView()
|
||||
expect(lowerLeftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
lowerRightEditorView = rightEditorView.splitDown()
|
||||
rightEditorView.getPaneView().getModel().splitDown(copyActiveItem: true)
|
||||
lowerRightEditorView = atom.workspaceView.getActiveView()
|
||||
expect(lowerRightEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
describe ".eachEditorView(callback)", ->
|
||||
@@ -241,7 +244,7 @@ describe "WorkspaceView", ->
|
||||
atom.workspaceView.eachEditorView(callback)
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.workspaceView.getActiveView()
|
||||
|
||||
@@ -259,10 +262,10 @@ describe "WorkspaceView", ->
|
||||
|
||||
subscription = atom.workspaceView.eachEditorView(callback)
|
||||
expect(count).toBe 1
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
expect(count).toBe 2
|
||||
subscription.off()
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
expect(count).toBe 2
|
||||
|
||||
describe "core:close", ->
|
||||
@@ -271,7 +274,7 @@ describe "WorkspaceView", ->
|
||||
|
||||
paneView1 = atom.workspaceView.getActivePaneView()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
editorView.splitRight()
|
||||
editorView.getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
paneView2 = atom.workspaceView.getActivePaneView()
|
||||
|
||||
expect(paneView1).not.toBe paneView2
|
||||
|
||||
@@ -187,7 +187,7 @@ class Atom extends Model
|
||||
|
||||
@syntax = @deserializers.deserialize(@state.syntax) ? new Syntax()
|
||||
|
||||
@subscribe @packages, 'activated', => @watchThemes()
|
||||
@subscribe @packages.onDidActivateAll => @watchThemes()
|
||||
|
||||
Project = require './project'
|
||||
TextBuffer = require 'text-buffer'
|
||||
@@ -373,7 +373,7 @@ class Atom extends Model
|
||||
@themes.load()
|
||||
|
||||
watchThemes: ->
|
||||
@themes.on 'reloaded', =>
|
||||
@themes.onDidReloadAll =>
|
||||
# Only reload stylesheets from non-theme packages
|
||||
for pack in @packages.getActivePackages() when pack.getType() isnt 'theme'
|
||||
pack.reloadStylesheets?()
|
||||
@@ -408,20 +408,19 @@ class Atom extends Model
|
||||
# ## Examples
|
||||
#
|
||||
# ```coffee
|
||||
# atom.confirm
|
||||
# message: 'How you feeling?'
|
||||
# detailedMessage: 'Be honest.'
|
||||
# buttons:
|
||||
# Good: -> window.alert('good to hear')
|
||||
# Bad: -> window.alert('bummer')
|
||||
# atom.confirm
|
||||
# message: 'How you feeling?'
|
||||
# detailedMessage: 'Be honest.'
|
||||
# buttons:
|
||||
# Good: -> window.alert('good to hear')
|
||||
# Bad: -> window.alert('bummer')
|
||||
# ```
|
||||
#
|
||||
# * `options` An {Object} with the following keys:
|
||||
# * `message` The {String} message to display.
|
||||
# * `detailedMessage` The {String} detailed message to display.
|
||||
# * `buttons` Either an array of strings or an object where keys are
|
||||
# button names and the values are callbacks to invoke when
|
||||
# clicked.
|
||||
# button names and the values are callbacks to invoke when clicked.
|
||||
#
|
||||
# Returns the chosen button index {Number} if the buttons option was an array.
|
||||
confirm: ({message, detailedMessage, buttons}={}) ->
|
||||
|
||||
@@ -5,13 +5,11 @@ AutoUpdateManager = require './auto-update-manager'
|
||||
BrowserWindow = require 'browser-window'
|
||||
Menu = require 'menu'
|
||||
app = require 'app'
|
||||
dialog = require 'dialog'
|
||||
fs = require 'fs'
|
||||
ipc = require 'ipc'
|
||||
path = require 'path'
|
||||
os = require 'os'
|
||||
net = require 'net'
|
||||
shell = require 'shell'
|
||||
url = require 'url'
|
||||
{EventEmitter} = require 'events'
|
||||
_ = require 'underscore-plus'
|
||||
@@ -103,11 +101,12 @@ class AtomApplication
|
||||
window.once 'window:loaded', =>
|
||||
@autoUpdateManager.emitUpdateAvailableEvent(window)
|
||||
|
||||
focusHandler = => @lastFocusedWindow = window
|
||||
window.browserWindow.on 'focus', focusHandler
|
||||
window.browserWindow.once 'closed', =>
|
||||
@lastFocusedWindow = null if window is @lastFocusedWindow
|
||||
window.browserWindow.removeListener 'focus', focusHandler
|
||||
unless window.isSpec
|
||||
focusHandler = => @lastFocusedWindow = window
|
||||
window.browserWindow.on 'focus', focusHandler
|
||||
window.browserWindow.once 'closed', =>
|
||||
@lastFocusedWindow = null if window is @lastFocusedWindow
|
||||
window.browserWindow.removeListener 'focus', focusHandler
|
||||
|
||||
# Creates server to listen for additional atom application launches.
|
||||
#
|
||||
@@ -155,8 +154,8 @@ class AtomApplication
|
||||
atomWindow ?= @focusedWindow()
|
||||
atomWindow?.browserWindow.inspectElement(x, y)
|
||||
|
||||
@on 'application:open-documentation', -> shell.openExternal('https://atom.io/docs/latest/?app')
|
||||
@on 'application:open-terms-of-use', -> shell.openExternal('https://atom.io/terms')
|
||||
@on 'application:open-documentation', -> require('shell').openExternal('https://atom.io/docs/latest/?app')
|
||||
@on 'application:open-terms-of-use', -> require('shell').openExternal('https://atom.io/terms')
|
||||
@on 'application:install-update', -> @autoUpdateManager.install()
|
||||
@on 'application:check-for-update', => @autoUpdateManager.check()
|
||||
|
||||
@@ -484,5 +483,15 @@ class AtomApplication
|
||||
when 'folder' then ['openDirectory']
|
||||
when 'all' then ['openFile', 'openDirectory']
|
||||
else throw new Error("#{type} is an invalid type for promptForPath")
|
||||
dialog.showOpenDialog title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) =>
|
||||
|
||||
# Show the open dialog as child window on Windows and Linux, and as
|
||||
# independent dialog on OS X. This matches most native apps.
|
||||
parentWindow =
|
||||
if process.platform is 'darwin'
|
||||
null
|
||||
else
|
||||
BrowserWindow.getFocusedWindow()
|
||||
|
||||
dialog = require 'dialog'
|
||||
dialog.showOpenDialog parentWindow, title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode, safeMode, window})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
app = require 'app'
|
||||
fs = require 'fs-plus'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
protocol = require 'protocol'
|
||||
|
||||
@@ -24,5 +24,5 @@ class AtomProtocolHandler
|
||||
relativePath = path.normalize(request.url.substr(7))
|
||||
for loadPath in @loadPaths
|
||||
filePath = path.join(loadPath, relativePath)
|
||||
break if fs.isFileSync(filePath)
|
||||
break if fs.statSyncNoException(filePath).isFile?()
|
||||
return new protocol.RequestFileJob(filePath)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
BrowserWindow = require 'browser-window'
|
||||
ContextMenu = require './context-menu'
|
||||
app = require 'app'
|
||||
dialog = require 'dialog'
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
url = require 'url'
|
||||
@@ -25,7 +23,12 @@ class AtomWindow
|
||||
# Normalize to make sure drive letter case is consistent on Windows
|
||||
@resourcePath = path.normalize(@resourcePath) if @resourcePath
|
||||
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath
|
||||
@browserWindow = new BrowserWindow
|
||||
show: false
|
||||
title: 'Atom'
|
||||
icon: @constructor.iconPath
|
||||
'web-preferences':
|
||||
'subpixel-font-scaling': false
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@handleEvents()
|
||||
@@ -73,6 +76,13 @@ class AtomWindow
|
||||
getInitialPath: ->
|
||||
@browserWindow.loadSettings.initialPath
|
||||
|
||||
setupContextMenu: ->
|
||||
ContextMenu = null
|
||||
|
||||
@browserWindow.on 'context-menu', (menuTemplate) =>
|
||||
ContextMenu ?= require './context-menu'
|
||||
new ContextMenu(menuTemplate, this)
|
||||
|
||||
containsPath: (pathToCheck) ->
|
||||
initialPath = @getInitialPath()
|
||||
if not initialPath
|
||||
@@ -95,6 +105,7 @@ class AtomWindow
|
||||
@browserWindow.on 'unresponsive', =>
|
||||
return if @isSpec
|
||||
|
||||
dialog = require 'dialog'
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
type: 'warning'
|
||||
buttons: ['Close', 'Keep Waiting']
|
||||
@@ -105,6 +116,7 @@ class AtomWindow
|
||||
@browserWindow.webContents.on 'crashed', =>
|
||||
global.atomApplication.exit(100) if @exitWhenDone
|
||||
|
||||
dialog = require 'dialog'
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
type: 'warning'
|
||||
buttons: ['Close Window', 'Reload', 'Keep It Open']
|
||||
@@ -114,8 +126,7 @@ class AtomWindow
|
||||
when 0 then @browserWindow.destroy()
|
||||
when 1 then @browserWindow.restart()
|
||||
|
||||
@browserWindow.on 'context-menu', (menuTemplate) =>
|
||||
new ContextMenu(menuTemplate, this)
|
||||
@setupContextMenu()
|
||||
|
||||
if @isSpec
|
||||
# Workaround for https://github.com/atom/atom-shell/issues/380
|
||||
|
||||
@@ -1,44 +1,47 @@
|
||||
https = require 'https'
|
||||
autoUpdater = require 'auto-updater'
|
||||
dialog = require 'dialog'
|
||||
autoUpdater = null
|
||||
_ = require 'underscore-plus'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
IDLE_STATE='idle'
|
||||
CHECKING_STATE='checking'
|
||||
DOWNLOADING_STATE='downloading'
|
||||
UPDATE_AVAILABLE_STATE='update-available'
|
||||
NO_UPDATE_AVAILABLE_STATE='no-update-available'
|
||||
ERROR_STATE='error'
|
||||
IdleState = 'idle'
|
||||
CheckingState = 'checking'
|
||||
DownladingState = 'downloading'
|
||||
UpdateAvailableState = 'update-available'
|
||||
NoUpdateAvailableState = 'no-update-available'
|
||||
ErrorState = 'error'
|
||||
|
||||
module.exports =
|
||||
class AutoUpdateManager
|
||||
_.extend @prototype, EventEmitter.prototype
|
||||
|
||||
constructor: (@version) ->
|
||||
@state = IDLE_STATE
|
||||
@state = IdleState
|
||||
@feedUrl = "https://atom.io/api/updates?version=#{@version}"
|
||||
|
||||
process.nextTick => @setupAutoUpdater()
|
||||
|
||||
setupAutoUpdater: ->
|
||||
autoUpdater = require 'auto-updater'
|
||||
|
||||
if process.platform is 'win32'
|
||||
autoUpdater.checkForUpdates = => @checkForUpdatesShim()
|
||||
|
||||
autoUpdater.setFeedUrl @feedUrl
|
||||
|
||||
autoUpdater.on 'checking-for-update', =>
|
||||
@setState(CHECKING_STATE)
|
||||
@setState(CheckingState)
|
||||
|
||||
autoUpdater.on 'update-not-available', =>
|
||||
@setState(NO_UPDATE_AVAILABLE_STATE)
|
||||
@setState(NoUpdateAvailableState)
|
||||
|
||||
autoUpdater.on 'update-available', =>
|
||||
@setState(DOWNLOADING_STATE)
|
||||
@setState(DownladingState)
|
||||
|
||||
autoUpdater.on 'error', (event, message) =>
|
||||
@setState(ERROR_STATE)
|
||||
@setState(ErrorState)
|
||||
console.error "Error Downloading Update: #{message}"
|
||||
|
||||
autoUpdater.on 'update-downloaded', (event, @releaseNotes, @releaseVersion) =>
|
||||
@setState(UPDATE_AVAILABLE_STATE)
|
||||
@setState(UpdateAvailableState)
|
||||
@emitUpdateAvailableEvent(@getWindows()...)
|
||||
|
||||
# Only released versions should check for updates.
|
||||
@@ -48,6 +51,8 @@ class AutoUpdateManager
|
||||
# Windows doesn't have an auto-updater, so use this method to shim the events.
|
||||
checkForUpdatesShim: ->
|
||||
autoUpdater.emit 'checking-for-update'
|
||||
|
||||
https = require 'https'
|
||||
request = https.get @feedUrl, (response) ->
|
||||
if response.statusCode == 200
|
||||
body = ""
|
||||
@@ -67,7 +72,7 @@ class AutoUpdateManager
|
||||
atomWindow.sendCommand('window:update-available', [@releaseVersion, @releaseNotes])
|
||||
|
||||
setState: (state) ->
|
||||
return unless @state != state
|
||||
return if @state is state
|
||||
@state = state
|
||||
@emit 'state-changed', @state
|
||||
|
||||
@@ -86,10 +91,12 @@ class AutoUpdateManager
|
||||
|
||||
onUpdateNotAvailable: =>
|
||||
autoUpdater.removeListener 'error', @onUpdateError
|
||||
dialog = require 'dialog'
|
||||
dialog.showMessageBox type: 'info', buttons: ['OK'], message: 'No update available.', detail: "Version #{@version} is the latest version."
|
||||
|
||||
onUpdateError: (event, message) =>
|
||||
autoUpdater.removeListener 'update-not-available', @onUpdateNotAvailable
|
||||
dialog = require 'dialog'
|
||||
dialog.showMessageBox type: 'warning', buttons: ['OK'], message: 'There was an error checking for updates.', detail: message
|
||||
|
||||
getWindows: ->
|
||||
|
||||
@@ -3,11 +3,9 @@ global.shellStartTime = Date.now()
|
||||
crashReporter = require 'crash-reporter'
|
||||
app = require 'app'
|
||||
fs = require 'fs'
|
||||
module = require 'module'
|
||||
path = require 'path'
|
||||
optimist = require 'optimist'
|
||||
nslog = require 'nslog'
|
||||
dialog = require 'dialog'
|
||||
|
||||
console.log = nslog
|
||||
|
||||
@@ -33,14 +31,14 @@ start = ->
|
||||
app.on 'will-finish-launching', ->
|
||||
setupCrashReporter()
|
||||
|
||||
app.on 'finish-launching', ->
|
||||
app.on 'ready', ->
|
||||
app.removeListener 'open-file', addPathToOpen
|
||||
app.removeListener 'open-url', addUrlToOpen
|
||||
|
||||
args.pathsToOpen = args.pathsToOpen.map (pathToOpen) ->
|
||||
path.resolve(args.executedFrom ? process.cwd(), pathToOpen.toString())
|
||||
|
||||
require('coffee-script').register()
|
||||
setupCoffeeScript()
|
||||
if args.devMode
|
||||
require(path.join(args.resourcePath, 'src', 'coffee-cache')).register()
|
||||
AtomApplication = require path.join(args.resourcePath, 'src', 'browser', 'atom-application')
|
||||
@@ -57,6 +55,15 @@ global.devResourcePath = path.normalize(global.devResourcePath) if global.devRes
|
||||
setupCrashReporter = ->
|
||||
crashReporter.start(productName: 'Atom', companyName: 'GitHub')
|
||||
|
||||
setupCoffeeScript = ->
|
||||
CoffeeScript = null
|
||||
|
||||
require.extensions['.coffee'] = (module, filePath) ->
|
||||
CoffeeScript ?= require('coffee-script')
|
||||
coffee = fs.readFileSync(filePath, 'utf8')
|
||||
js = CoffeeScript.compile(coffee, filename: filePath)
|
||||
module._compile(js, filePath)
|
||||
|
||||
parseCommandLine = ->
|
||||
version = app.getVersion()
|
||||
options = optimist(process.argv[1..])
|
||||
@@ -109,9 +116,7 @@ parseCommandLine = ->
|
||||
else if devMode
|
||||
resourcePath = global.devResourcePath
|
||||
|
||||
try
|
||||
fs.statSync resourcePath
|
||||
catch
|
||||
unless fs.statSyncNoException(resourcePath)
|
||||
resourcePath = path.dirname(path.dirname(__dirname))
|
||||
|
||||
{resourcePath, pathsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, logFile}
|
||||
|
||||
@@ -24,7 +24,7 @@ class ContextMenuManager
|
||||
@commandOptions = x: e.pageX, y: e.pageY
|
||||
]
|
||||
|
||||
atom.keymaps.on 'bundled-keymaps-loaded', => @loadPlatformItems()
|
||||
atom.keymaps.onDidLoadBundledKeymaps => @loadPlatformItems()
|
||||
|
||||
loadPlatformItems: ->
|
||||
menusDirPath = path.join(@resourcePath, 'menus')
|
||||
|
||||
@@ -1,37 +1,14 @@
|
||||
{Point, Range} = require 'text-buffer'
|
||||
{Model} = require 'theorist'
|
||||
{Emitter} = require 'event-kit'
|
||||
_ = require 'underscore-plus'
|
||||
Grim = require 'grim'
|
||||
|
||||
# Extended: The `Cursor` class represents the little blinking line identifying
|
||||
# where text can be inserted.
|
||||
#
|
||||
# Cursors belong to {Editor}s and have some metadata attached in the form
|
||||
# of a {Marker}.
|
||||
#
|
||||
# ## Events
|
||||
#
|
||||
# ### moved
|
||||
#
|
||||
# Extended: Emit when a cursor has been moved. If there are multiple cursors,
|
||||
# it will be emit for each cursor.
|
||||
#
|
||||
# * `event` {Object}
|
||||
# * `oldBufferPosition` {Point}
|
||||
# * `oldScreenPosition` {Point}
|
||||
# * `newBufferPosition` {Point}
|
||||
# * `newScreenPosition` {Point}
|
||||
# * `textChanged` {Boolean}
|
||||
#
|
||||
# ### destroyed
|
||||
#
|
||||
# Extended: Emit when the cursor is destroyed
|
||||
#
|
||||
# ### visibility-changed
|
||||
#
|
||||
# Extended: Emit when the Cursor is hidden or shown
|
||||
#
|
||||
# * `visible` {Boolean} true when cursor is visible
|
||||
#
|
||||
module.exports =
|
||||
class Cursor extends Model
|
||||
screenPosition: null
|
||||
@@ -42,9 +19,11 @@ class Cursor extends Model
|
||||
|
||||
# Instantiated by an {Editor}
|
||||
constructor: ({@editor, @marker, id}) ->
|
||||
@emitter = new Emitter
|
||||
|
||||
@assignId(id)
|
||||
@updateVisibility()
|
||||
@marker.on 'changed', (e) =>
|
||||
@marker.onDidChange (e) =>
|
||||
@updateVisibility()
|
||||
{oldHeadScreenPosition, newHeadScreenPosition} = e
|
||||
{oldHeadBufferPosition, newHeadBufferPosition} = e
|
||||
@@ -65,13 +44,67 @@ class Cursor extends Model
|
||||
textChanged: textChanged
|
||||
|
||||
@emit 'moved', movedEvent
|
||||
@editor.cursorMoved(movedEvent)
|
||||
@marker.on 'destroyed', =>
|
||||
@emitter.emit 'did-change-position'
|
||||
@editor.cursorMoved(this, movedEvent)
|
||||
@marker.onDidDestroy =>
|
||||
@destroyed = true
|
||||
@editor.removeCursor(this)
|
||||
@emit 'destroyed'
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
@needsAutoscroll = true
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Essential: Calls your `callback` when the cursor has been moved.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` {Object}
|
||||
# * `oldBufferPosition` {Point}
|
||||
# * `oldScreenPosition` {Point}
|
||||
# * `newBufferPosition` {Point}
|
||||
# * `newScreenPosition` {Point}
|
||||
# * `textChanged` {Boolean}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangePosition: (callback) ->
|
||||
@emitter.on 'did-change-position', callback
|
||||
|
||||
# Extended: Calls your `callback` when the cursor is destroyed
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidDestroy: (callback) ->
|
||||
@emitter.on 'did-destroy', callback
|
||||
|
||||
# Extended: Calls your `callback` when the cursor's visibility has changed
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `visibility` {Boolean}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeVisibility: (callback) ->
|
||||
@emitter.on 'did-change-visibility', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'moved'
|
||||
Grim.deprecate("Use Cursor::onDidChangePosition instead")
|
||||
when 'destroyed'
|
||||
Grim.deprecate("Use Cursor::onDidDestroy instead")
|
||||
when 'destroyed'
|
||||
Grim.deprecate("Use Cursor::onDidDestroy instead")
|
||||
else
|
||||
Grim.deprecate("::on is no longer supported. Use the event subscription methods instead")
|
||||
super
|
||||
|
||||
###
|
||||
Section: Methods
|
||||
###
|
||||
|
||||
destroy: ->
|
||||
@marker.destroy()
|
||||
|
||||
@@ -131,6 +164,7 @@ class Cursor extends Model
|
||||
@visible = visible
|
||||
@needsAutoscroll ?= true if @visible and @isLastCursor()
|
||||
@emit 'visibility-changed', @visible
|
||||
@emitter.emit 'did-change-visibility', @visible
|
||||
|
||||
# Public: Returns the visibility of the cursor.
|
||||
isVisible: -> @visible
|
||||
@@ -225,6 +259,11 @@ class Cursor extends Model
|
||||
@editor.lineTextForBufferRow(@getBufferRow())
|
||||
|
||||
# Public: Moves the cursor up one screen row.
|
||||
#
|
||||
# * `rowCount` (optional) {Number} number of rows to move (default: 1)
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `moveToEndOfSelection` if true, move to the left of the selection if a
|
||||
# selection exists.
|
||||
moveUp: (rowCount=1, {moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
@@ -237,7 +276,12 @@ class Cursor extends Model
|
||||
@goalColumn = column
|
||||
|
||||
# Public: Moves the cursor down one screen row.
|
||||
moveDown: (rowCount = 1, {moveToEndOfSelection}={}) ->
|
||||
#
|
||||
# * `rowCount` (optional) {Number} number of rows to move (default: 1)
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `moveToEndOfSelection` if true, move to the left of the selection if a
|
||||
# selection exists.
|
||||
moveDown: (rowCount=1, {moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
{ row, column } = range.end
|
||||
@@ -250,30 +294,51 @@ class Cursor extends Model
|
||||
|
||||
# Public: Moves the cursor left one screen column.
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to move (default: 1)
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `moveToEndOfSelection` if true, move to the left of the selection if a
|
||||
# selection exists.
|
||||
moveLeft: ({moveToEndOfSelection}={}) ->
|
||||
moveLeft: (columnCount=1, {moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
@setScreenPosition(range.start)
|
||||
else
|
||||
{row, column} = @getScreenPosition()
|
||||
[row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity]
|
||||
|
||||
while columnCount > column and row > 0
|
||||
columnCount -= column
|
||||
column = @editor.lineTextForScreenRow(--row).length
|
||||
columnCount-- # subtract 1 for the row move
|
||||
|
||||
column = column - columnCount
|
||||
@setScreenPosition({row, column})
|
||||
|
||||
# Public: Moves the cursor right one screen column.
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to move (default: 1)
|
||||
# * `options` (optional) {Object} with the following keys:
|
||||
# * `moveToEndOfSelection` if true, move to the right of the selection if a
|
||||
# selection exists.
|
||||
moveRight: ({moveToEndOfSelection}={}) ->
|
||||
moveRight: (columnCount=1, {moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
@setScreenPosition(range.end)
|
||||
else
|
||||
{ row, column } = @getScreenPosition()
|
||||
@setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
|
||||
maxLines = @editor.getScreenLineCount()
|
||||
rowLength = @editor.lineTextForScreenRow(row).length
|
||||
columnsRemainingInLine = rowLength - column
|
||||
|
||||
while columnCount > columnsRemainingInLine and row < maxLines - 1
|
||||
columnCount -= columnsRemainingInLine
|
||||
columnCount-- # subtract 1 for the row move
|
||||
|
||||
column = 0
|
||||
rowLength = @editor.lineTextForScreenRow(++row).length
|
||||
columnsRemainingInLine = rowLength
|
||||
|
||||
column = column + columnCount
|
||||
@setScreenPosition({row, column}, skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
|
||||
|
||||
# Public: Moves the cursor to the top of the buffer.
|
||||
moveToTop: ->
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
_ = require 'underscore-plus'
|
||||
{Subscriber, Emitter} = require 'emissary'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
|
||||
idCounter = 0
|
||||
nextId = -> idCounter++
|
||||
@@ -26,44 +28,59 @@ nextId = -> idCounter++
|
||||
#
|
||||
# You should only use {Decoration::destroy} when you still need or do not own
|
||||
# the marker.
|
||||
#
|
||||
# ## Events
|
||||
#
|
||||
# ### updated
|
||||
#
|
||||
# Extended: When the {Decoration} is updated via {Decoration::update}.
|
||||
#
|
||||
# * `event` {Object}
|
||||
# * `oldParams` {Object} the old parameters the decoration used to have
|
||||
# * `newParams` {Object} the new parameters the decoration now has
|
||||
#
|
||||
# ### destroyed
|
||||
#
|
||||
# Extended: When the {Decoration} is destroyed
|
||||
#
|
||||
module.exports =
|
||||
class Decoration
|
||||
Emitter.includeInto(this)
|
||||
EmitterMixin.includeInto(this)
|
||||
|
||||
# Extended: Check if the `decorationParams.type` matches `type`
|
||||
# Extended: Check if the `decorationProperties.type` matches `type`
|
||||
#
|
||||
# * `decorationParams` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
|
||||
# * `decorationProperties` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
|
||||
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
|
||||
# be an {Array} of {String}s, where it will return true if the decoration's
|
||||
# type matches any in the array.
|
||||
#
|
||||
# Returns {Boolean}
|
||||
@isType: (decorationParams, type) ->
|
||||
if _.isArray(decorationParams.type)
|
||||
type in decorationParams.type
|
||||
@isType: (decorationProperties, type) ->
|
||||
if _.isArray(decorationProperties.type)
|
||||
type in decorationProperties.type
|
||||
else
|
||||
type is decorationParams.type
|
||||
type is decorationProperties.type
|
||||
|
||||
constructor: (@marker, @displayBuffer, @params) ->
|
||||
constructor: (@marker, @displayBuffer, @properties) ->
|
||||
@emitter = new Emitter
|
||||
@id = nextId()
|
||||
@params.id = @id
|
||||
@properties.id = @id
|
||||
@flashQueue = null
|
||||
@isDestroyed = false
|
||||
@destroyed = false
|
||||
|
||||
@markerDestroyDisposable = @marker.onDidDestroy => @destroy()
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Essential: When the {Decoration} is updated via {Decoration::update}.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` {Object}
|
||||
# * `oldProperties` {Object} the old parameters the decoration used to have
|
||||
# * `newProperties` {Object} the new parameters the decoration now has
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeProperties: (callback) ->
|
||||
@emitter.on 'did-change-properties', callback
|
||||
|
||||
# Essential: Invoke the given callback when the {Decoration} is destroyed
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidDestroy: (callback) ->
|
||||
@emitter.on 'did-destroy', callback
|
||||
|
||||
###
|
||||
Section: Methods
|
||||
###
|
||||
|
||||
# Essential: An id unique across all {Decoration} objects
|
||||
getId: -> @id
|
||||
@@ -71,9 +88,6 @@ class Decoration
|
||||
# Essential: Returns the marker associated with this {Decoration}
|
||||
getMarker: -> @marker
|
||||
|
||||
# Essential: Returns the {Decoration}'s params.
|
||||
getParams: -> @params
|
||||
|
||||
# Public: Check if this decoration is of type `type`
|
||||
#
|
||||
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
|
||||
@@ -82,9 +96,16 @@ class Decoration
|
||||
#
|
||||
# Returns {Boolean}
|
||||
isType: (type) ->
|
||||
Decoration.isType(@params, type)
|
||||
Decoration.isType(@properties, type)
|
||||
|
||||
# Essential: Update the marker with new params. Allows you to change the decoration's class.
|
||||
# Essential: Returns the {Decoration}'s properties.
|
||||
getProperties: ->
|
||||
@properties
|
||||
getParams: ->
|
||||
Grim.deprecate 'Use Decoration::getProperties instead'
|
||||
@getProperties()
|
||||
|
||||
# Essential: Update the marker with new Properties. Allows you to change the decoration's class.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
@@ -92,37 +113,60 @@ class Decoration
|
||||
# decoration.update({type: 'gutter', class: 'my-new-class'})
|
||||
# ```
|
||||
#
|
||||
# * `newParams` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
|
||||
update: (newParams) ->
|
||||
return if @isDestroyed
|
||||
oldParams = @params
|
||||
@params = newParams
|
||||
@params.id = @id
|
||||
@displayBuffer.decorationUpdated(this)
|
||||
@emit 'updated', {oldParams, newParams}
|
||||
# * `newProperties` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
|
||||
setProperties: (newProperties) ->
|
||||
return if @destroyed
|
||||
oldProperties = @properties
|
||||
@properties = newProperties
|
||||
@properties.id = @id
|
||||
@emit 'updated', {oldParams: oldProperties, newParams: newProperties}
|
||||
@emitter.emit 'did-change-properties', {oldProperties, newProperties}
|
||||
update: (newProperties) ->
|
||||
Grim.deprecate 'Use Decoration::setProperties instead'
|
||||
@setProperties(newProperties)
|
||||
|
||||
# Essential: Destroy this marker.
|
||||
#
|
||||
# If you own the marker, you should use {Marker::destroy} which will destroy
|
||||
# this decoration.
|
||||
destroy: ->
|
||||
return if @isDestroyed
|
||||
@isDestroyed = true
|
||||
@displayBuffer.removeDecoration(this)
|
||||
return if @destroyed
|
||||
@markerDestroyDisposable.dispose()
|
||||
@markerDestroyDisposable = null
|
||||
@destroyed = true
|
||||
@emit 'destroyed'
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
|
||||
matchesPattern: (decorationPattern) ->
|
||||
return false unless decorationPattern?
|
||||
for key, value of decorationPattern
|
||||
return false if @params[key] != value
|
||||
return false if @properties[key] != value
|
||||
true
|
||||
|
||||
onDidFlash: (callback) ->
|
||||
@emitter.on 'did-flash', callback
|
||||
|
||||
flash: (klass, duration=500) ->
|
||||
flashObject = {class: klass, duration}
|
||||
@flashQueue ?= []
|
||||
@flashQueue.push(flashObject)
|
||||
@emit 'flash'
|
||||
@emitter.emit 'did-flash'
|
||||
|
||||
consumeNextFlash: ->
|
||||
return @flashQueue.shift() if @flashQueue?.length > 0
|
||||
null
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'updated'
|
||||
Grim.deprecate 'Use Decoration::onDidChangeProperties instead'
|
||||
when 'destroyed'
|
||||
Grim.deprecate 'Use Decoration::onDidDestroy instead'
|
||||
when 'flash'
|
||||
Grim.deprecate 'Use Decoration::onDidFlash instead'
|
||||
else
|
||||
Grim.deprecate 'Decoration::on is deprecated. Use event subscription methods instead.'
|
||||
|
||||
EmitterMixin::on.apply(this, arguments)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
{Range} = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
{Subscriber} = require 'emissary'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
|
||||
module.exports =
|
||||
class DisplayBufferMarker
|
||||
Emitter.includeInto(this)
|
||||
EmitterMixin.includeInto(this)
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
bufferMarkerSubscription: null
|
||||
@@ -13,8 +16,10 @@ class DisplayBufferMarker
|
||||
oldTailBufferPosition: null
|
||||
oldTailScreenPosition: null
|
||||
wasValid: true
|
||||
deferredChangeEvents: null
|
||||
|
||||
constructor: ({@bufferMarker, @displayBuffer}) ->
|
||||
@emitter = new Emitter
|
||||
@id = @bufferMarker.id
|
||||
@oldHeadBufferPosition = @getHeadBufferPosition()
|
||||
@oldHeadScreenPosition = @getHeadScreenPosition()
|
||||
@@ -22,8 +27,23 @@ class DisplayBufferMarker
|
||||
@oldTailScreenPosition = @getTailScreenPosition()
|
||||
@wasValid = @isValid()
|
||||
|
||||
@subscribe @bufferMarker, 'destroyed', => @destroyed()
|
||||
@subscribe @bufferMarker, 'changed', (event) => @notifyObservers(event)
|
||||
@subscribe @bufferMarker.onDidDestroy => @destroyed()
|
||||
@subscribe @bufferMarker.onDidChange (event) => @notifyObservers(event)
|
||||
|
||||
onDidChange: (callback) ->
|
||||
@emitter.on 'did-change', callback
|
||||
|
||||
onDidDestroy: (callback) ->
|
||||
@emitter.on 'did-destroy', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'changed'
|
||||
Grim.deprecate("Use DisplayBufferMarker::onDidChange instead")
|
||||
when 'destroyed'
|
||||
Grim.deprecate("Use DisplayBufferMarker::onDidDestroy instead")
|
||||
|
||||
EmitterMixin::on.apply(this, arguments)
|
||||
|
||||
copy: (attributes) ->
|
||||
@displayBuffer.getMarker(@bufferMarker.copy(attributes).id)
|
||||
@@ -199,6 +219,8 @@ class DisplayBufferMarker
|
||||
destroyed: ->
|
||||
delete @displayBuffer.markers[@id]
|
||||
@emit 'destroyed'
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
|
||||
notifyObservers: ({textChanged}) ->
|
||||
textChanged ?= false
|
||||
@@ -215,7 +237,7 @@ class DisplayBufferMarker
|
||||
_.isEqual(newTailBufferPosition, @oldTailBufferPosition) and
|
||||
_.isEqual(newTailScreenPosition, @oldTailScreenPosition)
|
||||
|
||||
@emit 'changed', {
|
||||
changeEvent = {
|
||||
@oldHeadScreenPosition, newHeadScreenPosition,
|
||||
@oldTailScreenPosition, newTailScreenPosition,
|
||||
@oldHeadBufferPosition, newHeadBufferPosition,
|
||||
@@ -224,8 +246,25 @@ class DisplayBufferMarker
|
||||
isValid
|
||||
}
|
||||
|
||||
if @deferredChangeEvents?
|
||||
@deferredChangeEvents.push(changeEvent)
|
||||
else
|
||||
@emit 'changed', changeEvent
|
||||
@emitter.emit 'did-change', changeEvent
|
||||
|
||||
@oldHeadBufferPosition = newHeadBufferPosition
|
||||
@oldHeadScreenPosition = newHeadScreenPosition
|
||||
@oldTailBufferPosition = newTailBufferPosition
|
||||
@oldTailScreenPosition = newTailScreenPosition
|
||||
@wasValid = isValid
|
||||
|
||||
pauseChangeEvents: ->
|
||||
@deferredChangeEvents = []
|
||||
|
||||
resumeChangeEvents: ->
|
||||
if deferredChangeEvents = @deferredChangeEvents
|
||||
@deferredChangeEvents = null
|
||||
|
||||
for event in deferredChangeEvents
|
||||
@emit 'changed', event
|
||||
@emitter.emit 'did-change', event
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter} = require 'emissary'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
guid = require 'guid'
|
||||
Serializable = require 'serializable'
|
||||
{Model} = require 'theorist'
|
||||
{Emitter} = require 'event-kit'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
TokenizedBuffer = require './tokenized-buffer'
|
||||
RowMap = require './row-map'
|
||||
@@ -10,6 +11,7 @@ Fold = require './fold'
|
||||
Token = require './token'
|
||||
Decoration = require './decoration'
|
||||
DisplayBufferMarker = require './display-buffer-marker'
|
||||
Grim = require 'grim'
|
||||
|
||||
class BufferToScreenConversionError extends Error
|
||||
constructor: (@message, @metadata) ->
|
||||
@@ -22,7 +24,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
@properties
|
||||
manageScrollPosition: false
|
||||
softWrap: null
|
||||
softWrapped: null
|
||||
editorWidthInChars: null
|
||||
lineHeightInPixels: null
|
||||
defaultCharWidth: null
|
||||
@@ -40,7 +42,10 @@ class DisplayBuffer extends Model
|
||||
|
||||
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, @invisibles}={}) ->
|
||||
super
|
||||
@softWrap ?= atom.config.get('editor.softWrap') ? false
|
||||
|
||||
@emitter = new Emitter
|
||||
|
||||
@softWrapped ?= atom.config.get('editor.softWrap') ? false
|
||||
@tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer, @invisibles})
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
@charWidthsByScope = {}
|
||||
@@ -48,29 +53,23 @@ class DisplayBuffer extends Model
|
||||
@foldsByMarkerId = {}
|
||||
@decorationsById = {}
|
||||
@decorationsByMarkerId = {}
|
||||
@decorationMarkerChangedSubscriptions = {}
|
||||
@decorationMarkerDestroyedSubscriptions = {}
|
||||
@updateAllScreenLines()
|
||||
@createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes())
|
||||
@subscribe @tokenizedBuffer, 'grammar-changed', (grammar) => @emit 'grammar-changed', grammar
|
||||
@subscribe @tokenizedBuffer, 'tokenized', => @emit 'tokenized'
|
||||
@subscribe @tokenizedBuffer, 'changed', @handleTokenizedBufferChange
|
||||
@subscribe @buffer, 'markers-updated', @handleBufferMarkersUpdated
|
||||
@subscribe @buffer, 'marker-created', @handleBufferMarkerCreated
|
||||
|
||||
@subscribe @$softWrap, (softWrap) =>
|
||||
@emit 'soft-wrap-changed', softWrap
|
||||
@updateWrappedScreenLines()
|
||||
@subscribe @tokenizedBuffer.onDidChange @handleTokenizedBufferChange
|
||||
@subscribe @buffer.onDidUpdateMarkers @handleBufferMarkersUpdated
|
||||
@subscribe @buffer.onDidCreateMarker @handleBufferMarkerCreated
|
||||
|
||||
@subscribe atom.config.observe 'editor.preferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @softWrap and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
@updateWrappedScreenLines() if @isSoftWrapped() and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
|
||||
@subscribe atom.config.observe 'editor.softWrapAtPreferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @softWrap
|
||||
@updateWrappedScreenLines() if @isSoftWrapped()
|
||||
|
||||
@updateAllScreenLines()
|
||||
|
||||
serializeParams: ->
|
||||
id: @id
|
||||
softWrap: @softWrap
|
||||
softWrapped: @isSoftWrapped()
|
||||
editorWidthInChars: @editorWidthInChars
|
||||
scrollTop: @scrollTop
|
||||
scrollLeft: @scrollLeft
|
||||
@@ -96,12 +95,71 @@ class DisplayBuffer extends Model
|
||||
@rowMap = new RowMap
|
||||
@updateScreenLines(0, @buffer.getLineCount(), null, suppressChangeEvent: true)
|
||||
|
||||
emitChanged: (eventProperties, refreshMarkers=true) ->
|
||||
onDidChangeSoftWrapped: (callback) ->
|
||||
@emitter.on 'did-change-soft-wrapped', callback
|
||||
|
||||
onDidChangeGrammar: (callback) ->
|
||||
@tokenizedBuffer.onDidChangeGrammar(callback)
|
||||
|
||||
onDidTokenize: (callback) ->
|
||||
@tokenizedBuffer.onDidTokenize(callback)
|
||||
|
||||
onDidChange: (callback) ->
|
||||
@emitter.on 'did-change', callback
|
||||
|
||||
onDidChangeCharacterWidths: (callback) ->
|
||||
@emitter.on 'did-change-character-widths', callback
|
||||
|
||||
observeDecorations: (callback) ->
|
||||
callback(decoration) for decoration in @getDecorations()
|
||||
@onDidAddDecoration(callback)
|
||||
|
||||
onDidAddDecoration: (callback) ->
|
||||
@emitter.on 'did-add-decoration', callback
|
||||
|
||||
onDidRemoveDecoration: (callback) ->
|
||||
@emitter.on 'did-remove-decoration', callback
|
||||
|
||||
onDidCreateMarker: (callback) ->
|
||||
@emitter.on 'did-create-marker', callback
|
||||
|
||||
onDidUpdateMarkers: (callback) ->
|
||||
@emitter.on 'did-update-markers', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'changed'
|
||||
Grim.deprecate("Use DisplayBuffer::onDidChange instead")
|
||||
when 'grammar-changed'
|
||||
Grim.deprecate("Use DisplayBuffer::onDidChangeGrammar instead")
|
||||
when 'soft-wrap-changed'
|
||||
Grim.deprecate("Use DisplayBuffer::onDidChangeSoftWrap instead")
|
||||
when 'character-widths-changed'
|
||||
Grim.deprecate("Use DisplayBuffer::onDidChangeCharacterWidths instead")
|
||||
when 'decoration-added'
|
||||
Grim.deprecate("Use DisplayBuffer::onDidAddDecoration instead")
|
||||
when 'decoration-removed'
|
||||
Grim.deprecate("Use DisplayBuffer::onDidRemoveDecoration instead")
|
||||
when 'decoration-changed'
|
||||
Grim.deprecate("Use decoration.getMarker().onDidChange() instead")
|
||||
when 'decoration-updated'
|
||||
Grim.deprecate("Use Decoration::onDidChangeProperties instead")
|
||||
when 'marker-created'
|
||||
Grim.deprecate("Use Decoration::onDidCreateMarker instead")
|
||||
when 'markers-updated'
|
||||
Grim.deprecate("Use Decoration::onDidUpdateMarkers instead")
|
||||
else
|
||||
Grim.deprecate("DisplayBuffer::on is deprecated. Use event subscription methods instead.")
|
||||
|
||||
EmitterMixin::on.apply(this, arguments)
|
||||
|
||||
emitDidChange: (eventProperties, refreshMarkers=true) ->
|
||||
if refreshMarkers
|
||||
@pauseMarkerObservers()
|
||||
@pauseMarkerChangeEvents()
|
||||
@refreshMarkerScreenPositions()
|
||||
@emit 'changed', eventProperties
|
||||
@resumeMarkerObservers()
|
||||
@emitter.emit 'did-change', eventProperties
|
||||
@resumeMarkerChangeEvents()
|
||||
|
||||
updateWrappedScreenLines: ->
|
||||
start = 0
|
||||
@@ -109,7 +167,7 @@ class DisplayBuffer extends Model
|
||||
@updateAllScreenLines()
|
||||
screenDelta = @getLastRow() - end
|
||||
bufferDelta = 0
|
||||
@emitChanged({ start, end, screenDelta, bufferDelta })
|
||||
@emitDidChange({ start, end, screenDelta, bufferDelta })
|
||||
|
||||
# Sets the visibility of the tokenized buffer.
|
||||
#
|
||||
@@ -153,7 +211,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
horizontallyScrollable: (reentrant) ->
|
||||
return false unless @width?
|
||||
return false if @getSoftWrap()
|
||||
return false if @isSoftWrapped()
|
||||
if reentrant
|
||||
@getScrollWidth() > @getWidth()
|
||||
else
|
||||
@@ -178,7 +236,7 @@ class DisplayBuffer extends Model
|
||||
setWidth: (newWidth) ->
|
||||
oldWidth = @width
|
||||
@width = newWidth
|
||||
@updateWrappedScreenLines() if newWidth isnt oldWidth and @softWrap
|
||||
@updateWrappedScreenLines() if newWidth isnt oldWidth and @isSoftWrapped()
|
||||
@setScrollTop(@getScrollTop()) # Ensure scrollTop is still valid in case horizontal scrollbar disappeared
|
||||
@width
|
||||
|
||||
@@ -251,6 +309,7 @@ class DisplayBuffer extends Model
|
||||
characterWidthsChanged: ->
|
||||
@computeScrollWidth()
|
||||
@emit 'character-widths-changed', @scopedCharacterWidthsChangeCount
|
||||
@emitter.emit 'did-change-character-widths', @scopedCharacterWidthsChangeCount
|
||||
|
||||
clearScopedCharWidths: ->
|
||||
@charWidthsByScope = {}
|
||||
@@ -344,11 +403,15 @@ class DisplayBuffer extends Model
|
||||
setInvisibles: (@invisibles) ->
|
||||
@tokenizedBuffer.setInvisibles(@invisibles)
|
||||
|
||||
# Deprecated: Use the softWrap property directly
|
||||
setSoftWrap: (@softWrap) -> @softWrap
|
||||
setSoftWrapped: (softWrapped) ->
|
||||
if softWrapped isnt @softWrapped
|
||||
@softWrapped = softWrapped
|
||||
@updateWrappedScreenLines()
|
||||
@emit 'soft-wrap-changed', @softWrapped
|
||||
@emitter.emit 'did-change-soft-wrapped', @softWrapped
|
||||
@softWrapped
|
||||
|
||||
# Deprecated: Use the softWrap property directly
|
||||
getSoftWrap: -> @softWrap
|
||||
isSoftWrapped: -> @softWrapped
|
||||
|
||||
# Set the number of characters that fit horizontally in the editor.
|
||||
#
|
||||
@@ -357,7 +420,7 @@ class DisplayBuffer extends Model
|
||||
if editorWidthInChars > 0
|
||||
previousWidthInChars = @editorWidthInChars
|
||||
@editorWidthInChars = editorWidthInChars
|
||||
if editorWidthInChars isnt previousWidthInChars and @softWrap
|
||||
if editorWidthInChars isnt previousWidthInChars and @isSoftWrapped()
|
||||
@updateWrappedScreenLines()
|
||||
|
||||
# Returns the editor width in characters for soft wrap.
|
||||
@@ -627,7 +690,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
unless screenLine?
|
||||
throw new BufferToScreenConversionError "No screen line exists when converting buffer row to screen row",
|
||||
softWrapEnabled: @getSoftWrap()
|
||||
softWrapEnabled: @isSoftWrapped()
|
||||
foldCount: @findFoldMarkers().length
|
||||
lastBufferRow: @buffer.getLastRow()
|
||||
lastScreenRow: @getLastRow()
|
||||
@@ -743,7 +806,7 @@ class DisplayBuffer extends Model
|
||||
# Returns a {Number} representing the `line` position where the wrap would take place.
|
||||
# Returns `null` if a wrap wouldn't occur.
|
||||
findWrapColumn: (line, softWrapColumn=@getSoftWrapColumn()) ->
|
||||
return unless @softWrap
|
||||
return unless @isSoftWrapped()
|
||||
return unless line.length > softWrapColumn
|
||||
|
||||
if /\s/.test(line[softWrapColumn])
|
||||
@@ -766,6 +829,12 @@ class DisplayBuffer extends Model
|
||||
decorationForId: (id) ->
|
||||
@decorationsById[id]
|
||||
|
||||
getDecorations: ->
|
||||
allDecorations = []
|
||||
for markerId, decorations of @decorationsByMarkerId
|
||||
allDecorations = allDecorations.concat(decorations) if decorations?
|
||||
allDecorations
|
||||
|
||||
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
decorationsByMarkerId = {}
|
||||
for marker in @findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow])
|
||||
@@ -775,24 +844,13 @@ class DisplayBuffer extends Model
|
||||
|
||||
decorateMarker: (marker, decorationParams) ->
|
||||
marker = @getMarker(marker.id)
|
||||
|
||||
@decorationMarkerDestroyedSubscriptions[marker.id] ?= @subscribe marker, 'destroyed', =>
|
||||
@removeAllDecorationsForMarker(marker)
|
||||
|
||||
@decorationMarkerChangedSubscriptions[marker.id] ?= @subscribe marker, 'changed', (event) =>
|
||||
decorations = @decorationsByMarkerId[marker.id]
|
||||
|
||||
# Why check existence? Markers may get destroyed or decorations removed
|
||||
# in the change handler. Bookmarks does this.
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
@emit 'decoration-changed', marker, decoration, event
|
||||
|
||||
decoration = new Decoration(marker, this, decorationParams)
|
||||
@subscribe decoration.onDidDestroy => @removeDecoration(decoration)
|
||||
@decorationsByMarkerId[marker.id] ?= []
|
||||
@decorationsByMarkerId[marker.id].push(decoration)
|
||||
@decorationsById[decoration.id] = decoration
|
||||
@emit 'decoration-added', marker, decoration
|
||||
@emit 'decoration-added', decoration
|
||||
@emitter.emit 'did-add-decoration', decoration
|
||||
decoration
|
||||
|
||||
removeDecoration: (decoration) ->
|
||||
@@ -803,25 +861,9 @@ class DisplayBuffer extends Model
|
||||
if index > -1
|
||||
decorations.splice(index, 1)
|
||||
delete @decorationsById[decoration.id]
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
@removedAllMarkerDecorations(marker) if decorations.length is 0
|
||||
|
||||
removeAllDecorationsForMarker: (marker) ->
|
||||
decorations = @decorationsByMarkerId[marker.id].slice()
|
||||
for decoration in decorations
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
@removedAllMarkerDecorations(marker)
|
||||
|
||||
removedAllMarkerDecorations: (marker) ->
|
||||
@decorationMarkerChangedSubscriptions[marker.id].off()
|
||||
@decorationMarkerDestroyedSubscriptions[marker.id].off()
|
||||
|
||||
delete @decorationsByMarkerId[marker.id]
|
||||
delete @decorationMarkerChangedSubscriptions[marker.id]
|
||||
delete @decorationMarkerDestroyedSubscriptions[marker.id]
|
||||
|
||||
decorationUpdated: (decoration) ->
|
||||
@emit 'decoration-updated', decoration
|
||||
@emit 'decoration-removed', decoration
|
||||
@emitter.emit 'did-remove-decoration', decoration
|
||||
delete @decorationsByMarkerId[marker.id] if decorations.length is 0
|
||||
|
||||
# Retrieves a {DisplayBufferMarker} based on its id.
|
||||
#
|
||||
@@ -965,12 +1007,13 @@ class DisplayBuffer extends Model
|
||||
getFoldMarkerAttributes: (attributes={}) ->
|
||||
_.extend(attributes, class: 'fold', displayBufferId: @id)
|
||||
|
||||
pauseMarkerObservers: ->
|
||||
marker.pauseEvents() for marker in @getMarkers()
|
||||
pauseMarkerChangeEvents: ->
|
||||
marker.pauseChangeEvents() for marker in @getMarkers()
|
||||
|
||||
resumeMarkerObservers: ->
|
||||
marker.resumeEvents() for marker in @getMarkers()
|
||||
resumeMarkerChangeEvents: ->
|
||||
marker.resumeChangeEvents() for marker in @getMarkers()
|
||||
@emit 'markers-updated'
|
||||
@emitter.emit 'did-update-markers'
|
||||
|
||||
refreshMarkerScreenPositions: ->
|
||||
for marker in @getMarkers()
|
||||
@@ -1012,10 +1055,10 @@ class DisplayBuffer extends Model
|
||||
bufferDelta: bufferDelta
|
||||
|
||||
if options.delayChangeEvent
|
||||
@pauseMarkerObservers()
|
||||
@pauseMarkerChangeEvents()
|
||||
@pendingChangeEvent = changeEvent
|
||||
else
|
||||
@emitChanged(changeEvent, options.refreshMarkers)
|
||||
@emitDidChange(changeEvent, options.refreshMarkers)
|
||||
|
||||
buildScreenLines: (startBufferRow, endBufferRow) ->
|
||||
screenLines = []
|
||||
@@ -1087,13 +1130,13 @@ class DisplayBuffer extends Model
|
||||
|
||||
computeScrollWidth: ->
|
||||
@scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left
|
||||
@scrollWidth += 1 unless @getSoftWrap()
|
||||
@scrollWidth += 1 unless @isSoftWrapped()
|
||||
@setScrollLeft(Math.min(@getScrollLeft(), @getMaxScrollLeft()))
|
||||
|
||||
handleBufferMarkersUpdated: =>
|
||||
if event = @pendingChangeEvent
|
||||
@pendingChangeEvent = null
|
||||
@emitChanged(event, false)
|
||||
@emitDidChange(event, false)
|
||||
|
||||
handleBufferMarkerCreated: (marker) =>
|
||||
@createFoldForMarker(marker) if marker.matchesAttributes(@getFoldMarkerAttributes())
|
||||
@@ -1101,6 +1144,7 @@ class DisplayBuffer extends Model
|
||||
# The marker might have been removed in some other handler called before
|
||||
# this one. Only emit when the marker still exists.
|
||||
@emit 'marker-created', displayBufferMarker
|
||||
@emitter.emit 'did-create-marker', displayBufferMarker
|
||||
|
||||
createFoldForMarker: (marker) ->
|
||||
@decorateMarker(marker, type: 'gutter', class: 'folded')
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
_ = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div, span} = require 'reactionary-atom-fork'
|
||||
{debounce, defaults, isEqualForProperties} = require 'underscore-plus'
|
||||
@@ -30,9 +31,8 @@ EditorComponent = React.createClass
|
||||
updateRequested: false
|
||||
updatesPaused: false
|
||||
updateRequestedWhilePaused: false
|
||||
cursorsMoved: false
|
||||
cursorMoved: false
|
||||
selectionChanged: false
|
||||
selectionAdded: false
|
||||
scrollingVertically: false
|
||||
mouseWheelScreenRow: null
|
||||
mouseWheelScreenRowClearDelay: 150
|
||||
@@ -176,10 +176,16 @@ EditorComponent = React.createClass
|
||||
@listenForDOMEvents()
|
||||
@listenForCommands()
|
||||
|
||||
@subscribe atom.themes, 'stylesheet-added stylesheet-removed stylesheet-updated', @onStylesheetsChanged
|
||||
@subscribe atom.themes.onDidAddStylesheet @onStylesheetsChanged
|
||||
@subscribe atom.themes.onDidUpdateStylesheet @onStylesheetsChanged
|
||||
@subscribe atom.themes.onDidRemoveStylesheet @onStylesheetsChanged
|
||||
unless atom.themes.isInitialLoadComplete()
|
||||
@subscribe atom.themes.onDidReloadAll @onStylesheetsChanged
|
||||
@subscribe scrollbarStyle.changes, @refreshScrollbars
|
||||
|
||||
@domPollingIntervalId = setInterval(@pollDOM, @domPollingInterval)
|
||||
@updateParentViewFocusedClassIfNeeded({})
|
||||
@updateParentViewMiniClassIfNeeded({})
|
||||
@checkForVisibilityChange()
|
||||
|
||||
componentWillUnmount: ->
|
||||
@@ -195,16 +201,16 @@ EditorComponent = React.createClass
|
||||
@props.editor.setMini(newProps.mini)
|
||||
|
||||
componentDidUpdate: (prevProps, prevState) ->
|
||||
cursorsMoved = @cursorsMoved
|
||||
cursorMoved = @cursorMoved
|
||||
selectionChanged = @selectionChanged
|
||||
@pendingChanges.length = 0
|
||||
@cursorsMoved = false
|
||||
@cursorMoved = false
|
||||
@selectionChanged = false
|
||||
|
||||
if @props.editor.isAlive()
|
||||
@updateParentViewFocusedClassIfNeeded(prevState)
|
||||
@updateParentViewMiniClassIfNeeded(prevState)
|
||||
@props.parentView.trigger 'cursor:moved' if cursorsMoved
|
||||
@props.parentView.trigger 'cursor:moved' if cursorMoved
|
||||
@props.parentView.trigger 'selection:changed' if selectionChanged
|
||||
@props.parentView.trigger 'editor:display-updated'
|
||||
|
||||
@@ -305,7 +311,7 @@ EditorComponent = React.createClass
|
||||
if marker.isValid()
|
||||
for decoration in decorations
|
||||
if decoration.isType('gutter') or decoration.isType('line')
|
||||
decorationParams = decoration.getParams()
|
||||
decorationParams = decoration.getProperties()
|
||||
screenRange ?= marker.getScreenRange()
|
||||
headScreenRow ?= marker.getHeadScreenPosition().row
|
||||
startRow = screenRange.start.row
|
||||
@@ -332,7 +338,7 @@ EditorComponent = React.createClass
|
||||
if marker.isValid() and not screenRange.isEmpty()
|
||||
for decoration in decorations
|
||||
if decoration.isType('highlight')
|
||||
decorationParams = decoration.getParams()
|
||||
decorationParams = decoration.getProperties()
|
||||
filteredDecorations[markerId] ?=
|
||||
id: markerId
|
||||
startPixelPosition: editor.pixelPositionForScreenPosition(screenRange.start)
|
||||
@@ -344,15 +350,12 @@ EditorComponent = React.createClass
|
||||
|
||||
observeEditor: ->
|
||||
{editor} = @props
|
||||
@subscribe editor, 'screen-lines-changed', @onScreenLinesChanged
|
||||
@subscribe editor, 'cursors-moved', @onCursorsMoved
|
||||
@subscribe editor, 'selection-removed selection-screen-range-changed', @onSelectionChanged
|
||||
@subscribe editor, 'selection-added', @onSelectionAdded
|
||||
@subscribe editor, 'decoration-added', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-removed', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-changed', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-updated', @onDecorationChanged
|
||||
@subscribe editor, 'character-widths-changed', @onCharacterWidthsChanged
|
||||
@subscribe editor.onDidChangeScreenLines(@onScreenLinesChanged)
|
||||
@subscribe editor.observeCursors(@onCursorAdded)
|
||||
@subscribe editor.observeSelections(@onSelectionAdded)
|
||||
@subscribe editor.observeDecorations(@onDecorationAdded)
|
||||
@subscribe editor.onDidRemoveDecoration(@onDecorationRemoved)
|
||||
@subscribe editor.onDidChangeCharacterWidths(@onCharacterWidthsChanged)
|
||||
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
|
||||
@subscribe editor.$scrollLeft.changes, @requestUpdate
|
||||
@subscribe editor.$verticalScrollbarWidth.changes, @requestUpdate
|
||||
@@ -477,7 +480,7 @@ EditorComponent = React.createClass
|
||||
'editor:add-selection-above': -> editor.addSelectionAbove()
|
||||
'editor:split-selections-into-lines': -> editor.splitSelectionsIntoLines()
|
||||
'editor:toggle-soft-tabs': -> editor.toggleSoftTabs()
|
||||
'editor:toggle-soft-wrap': -> editor.toggleSoftWrap()
|
||||
'editor:toggle-soft-wrap': -> editor.toggleSoftWrapped()
|
||||
'editor:fold-all': -> editor.foldAll()
|
||||
'editor:unfold-all': -> editor.unfoldAll()
|
||||
'editor:fold-current-row': -> editor.foldCurrentRow()
|
||||
@@ -644,8 +647,12 @@ EditorComponent = React.createClass
|
||||
onGutterMouseDown: (event) ->
|
||||
return unless event.button is 0 # only handle the left mouse button
|
||||
|
||||
if event.shiftKey
|
||||
{shiftKey, metaKey, ctrlKey} = event
|
||||
|
||||
if shiftKey
|
||||
@onGutterShiftClick(event)
|
||||
else if metaKey or (ctrlKey and process.platform isnt 'darwin')
|
||||
@onGutterMetaClick(event)
|
||||
else
|
||||
@onGutterClick(event)
|
||||
|
||||
@@ -653,14 +660,38 @@ EditorComponent = React.createClass
|
||||
{editor} = @props
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
|
||||
editor.setCursorScreenPosition([clickedRow, 0])
|
||||
editor.setSelectedScreenRange([[clickedRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
dragRow = screenPosition.row
|
||||
if dragRow < clickedRow # dragging up
|
||||
editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]])
|
||||
editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
|
||||
else
|
||||
editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]])
|
||||
editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
onGutterMetaClick: (event) ->
|
||||
{editor} = @props
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
|
||||
bufferRange = editor.bufferRangeForScreenRange([[clickedRow, 0], [clickedRow + 1, 0]])
|
||||
rowSelection = editor.addSelectionForBufferRange(bufferRange, preserveFolds: true)
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
dragRow = screenPosition.row
|
||||
|
||||
if dragRow < clickedRow # dragging up
|
||||
rowSelection.setScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
|
||||
else
|
||||
rowSelection.setScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
# After updating the selected screen range, merge overlapping selections
|
||||
editor.mergeIntersectingSelections(preserveFolds: true)
|
||||
|
||||
# The merge process will possibly destroy the current selection because
|
||||
# it will be merged into another one. Therefore, we need to obtain a
|
||||
# reference to the new selection that contains the originally selected row
|
||||
rowSelection = _.find editor.getSelections(), (selection) ->
|
||||
selection.intersectsBufferRange(bufferRange)
|
||||
|
||||
onGutterShiftClick: (event) ->
|
||||
{editor} = @props
|
||||
@@ -675,14 +706,15 @@ EditorComponent = React.createClass
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
dragRow = screenPosition.row
|
||||
if dragRow < tailPosition.row # dragging up
|
||||
editor.setSelectedScreenRange([[dragRow, 0], tailPosition])
|
||||
editor.setSelectedScreenRange([[dragRow, 0], tailPosition], preserveFolds: true)
|
||||
else
|
||||
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]])
|
||||
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
onStylesheetsChanged: (stylesheet) ->
|
||||
return unless @performedInitialMeasurement
|
||||
return unless atom.themes.isInitialLoadComplete()
|
||||
|
||||
@refreshScrollbars() if @containsScrollbarSelector(stylesheet)
|
||||
@refreshScrollbars() if not stylesheet? or @containsScrollbarSelector(stylesheet)
|
||||
@sampleFontStyling()
|
||||
@sampleBackgroundColors()
|
||||
@remeasureCharacterWidths()
|
||||
@@ -692,17 +724,22 @@ EditorComponent = React.createClass
|
||||
@pendingChanges.push(change)
|
||||
@requestUpdate() if editor.intersectsVisibleRowRange(change.start, change.end + 1) # TODO: Use closed-open intervals for change events
|
||||
|
||||
onSelectionChanged: (selection) ->
|
||||
onSelectionAdded: (selection) ->
|
||||
{editor} = @props
|
||||
|
||||
@subscribe selection.onDidChangeRange => @onSelectionChanged(selection)
|
||||
@subscribe selection.onDidDestroy =>
|
||||
@onSelectionChanged(selection)
|
||||
@unsubscribe(selection)
|
||||
|
||||
if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
@selectionChanged = true
|
||||
@requestUpdate()
|
||||
|
||||
onSelectionAdded: (selection) ->
|
||||
onSelectionChanged: (selection) ->
|
||||
{editor} = @props
|
||||
if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
@selectionChanged = true
|
||||
@selectionAdded = true
|
||||
@requestUpdate()
|
||||
|
||||
onScrollTopChanged: ->
|
||||
@@ -720,13 +757,24 @@ EditorComponent = React.createClass
|
||||
|
||||
onStoppedScrollingAfterDelay: null # created lazily
|
||||
|
||||
onCursorsMoved: ->
|
||||
@cursorsMoved = true
|
||||
onCursorAdded: (cursor) ->
|
||||
@subscribe cursor.onDidChangePosition @onCursorMoved
|
||||
|
||||
onCursorMoved: ->
|
||||
@cursorMoved = true
|
||||
@requestUpdate()
|
||||
|
||||
onDecorationAdded: (decoration) ->
|
||||
@subscribe decoration.onDidChangeProperties(@onDecorationChanged)
|
||||
@subscribe decoration.getMarker().onDidChange(@onDecorationChanged)
|
||||
@requestUpdate()
|
||||
|
||||
onDecorationChanged: ->
|
||||
@requestUpdate()
|
||||
|
||||
onDecorationRemoved: ->
|
||||
@requestUpdate()
|
||||
|
||||
onCharacterWidthsChanged: (@scopedCharacterWidthsChangeCount) ->
|
||||
@requestUpdate()
|
||||
|
||||
|
||||
@@ -70,9 +70,9 @@ class EditorView extends View
|
||||
# The constructor for setting up an `EditorView` instance.
|
||||
#
|
||||
# * `editorOrParams` Either an {Editor}, or an object with one property, `mini`.
|
||||
# If `mini` is `true`, a "miniature" `Editor` is constructed.
|
||||
# Typically, this is ideal for scenarios where you need an Atom editor,
|
||||
# but without all the chrome, like scrollbars, gutter, _e.t.c._.
|
||||
# If `mini` is `true`, a "miniature" `Editor` is constructed.
|
||||
# Typically, this is ideal for scenarios where you need an Atom editor,
|
||||
# but without all the chrome, like scrollbars, gutter, _e.t.c._.
|
||||
#
|
||||
constructor: (editorOrParams, props) ->
|
||||
super
|
||||
@@ -86,7 +86,7 @@ class EditorView extends View
|
||||
props.placeholderText = placeholderText
|
||||
@editor ?= new Editor
|
||||
buffer: new TextBuffer
|
||||
softWrap: false
|
||||
softWrapped: false
|
||||
tabLength: 2
|
||||
softTabs: true
|
||||
mini: mini
|
||||
@@ -101,7 +101,6 @@ class EditorView extends View
|
||||
@overlayer = $(node).find('.lines').addClass('overlayer')
|
||||
@hiddenInput = $(node).find('.hidden-input')
|
||||
|
||||
# FIXME: there should be a better way to deal with the gutter element
|
||||
@subscribe atom.config.observe 'editor.showLineNumbers', =>
|
||||
@gutter = $(node).find('.gutter')
|
||||
|
||||
@@ -136,7 +135,7 @@ class EditorView extends View
|
||||
Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth()
|
||||
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
|
||||
Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
|
||||
Object.defineProperty @::, 'active', get: -> @is(@getPane()?.activeView)
|
||||
Object.defineProperty @::, 'active', get: -> @is(@getPaneView()?.activeView)
|
||||
Object.defineProperty @::, 'isFocused', get: -> @component?.state.focused
|
||||
Object.defineProperty @::, 'mini', get: -> @component?.props.mini
|
||||
|
||||
@@ -144,12 +143,12 @@ class EditorView extends View
|
||||
return unless onDom
|
||||
return if @attached
|
||||
@attached = true
|
||||
@component.pollDOM()
|
||||
@component.checkForVisibilityChange()
|
||||
|
||||
@focus() if @focusOnAttach
|
||||
|
||||
@addGrammarScopeAttribute()
|
||||
@subscribe @editor, 'grammar-changed', =>
|
||||
@addGrammarScopeAttribute()
|
||||
@subscribe @editor.onDidChangeGrammar => @addGrammarScopeAttribute()
|
||||
|
||||
@trigger 'editor:attached', [this]
|
||||
|
||||
@@ -169,45 +168,28 @@ class EditorView extends View
|
||||
else
|
||||
@editor.getScrollLeft()
|
||||
|
||||
# Public: Scrolls the editor to the bottom.
|
||||
scrollToBottom: ->
|
||||
deprecate 'Use Editor::scrollToBottom instead. You can get the editor via editorView.getModel()'
|
||||
@editor.setScrollBottom(Infinity)
|
||||
|
||||
# Public: Scrolls the editor to the given screen position.
|
||||
#
|
||||
# * `screenPosition` An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# * `options` (optional) {Object} matching the options available to {::scrollToScreenPosition}
|
||||
scrollToScreenPosition: (screenPosition, options) ->
|
||||
deprecate 'Use Editor::scrollToScreenPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.scrollToScreenPosition(screenPosition, options)
|
||||
|
||||
# Public: Scrolls the editor to the given buffer position.
|
||||
#
|
||||
# * `bufferPosition` An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# * `options` (optional) {Object} matching the options available to {::scrollToBufferPosition}
|
||||
scrollToBufferPosition: (bufferPosition, options) ->
|
||||
deprecate 'Use Editor::scrollToBufferPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.scrollToBufferPosition(bufferPosition, options)
|
||||
|
||||
scrollToCursorPosition: ->
|
||||
deprecate 'Use Editor::scrollToCursorPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.scrollToCursorPosition()
|
||||
|
||||
# Public: Converts a buffer position to a pixel position.
|
||||
#
|
||||
# * `bufferPosition` An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
#
|
||||
# Returns an {Object} with two values: `top` and `left`, representing the pixel positions.
|
||||
pixelPositionForBufferPosition: (bufferPosition) ->
|
||||
deprecate 'Use Editor::pixelPositionForBufferPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.pixelPositionForBufferPosition(bufferPosition)
|
||||
|
||||
# Public: Converts a screen position to a pixel position.
|
||||
#
|
||||
# * `screenPosition` An object that represents a screen position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
#
|
||||
# Returns an object with two values: `top` and `left`, representing the pixel positions.
|
||||
pixelPositionForScreenPosition: (screenPosition) ->
|
||||
deprecate 'Use Editor::pixelPositionForScreenPosition instead. You can get the editor via editorView.getModel()'
|
||||
@editor.pixelPositionForScreenPosition(screenPosition)
|
||||
|
||||
appendToLinesView: (view) ->
|
||||
@@ -231,31 +213,50 @@ class EditorView extends View
|
||||
unmountComponent: ->
|
||||
React.unmountComponentAtNode(@element) if @component.isMounted()
|
||||
|
||||
# Public: Split the editor view left.
|
||||
splitLeft: ->
|
||||
pane = @getPane()
|
||||
deprecate """
|
||||
Use Pane::splitLeft instead.
|
||||
To duplicate this editor into the split use:
|
||||
editorView.getPaneView().getModel().splitLeft(copyActiveItem: true)
|
||||
"""
|
||||
pane = @getPaneView()
|
||||
pane?.splitLeft(pane?.copyActiveItem()).activeView
|
||||
|
||||
# Public: Split the editor view right.
|
||||
splitRight: ->
|
||||
pane = @getPane()
|
||||
deprecate """
|
||||
Use Pane::splitRight instead.
|
||||
To duplicate this editor into the split use:
|
||||
editorView.getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
"""
|
||||
pane = @getPaneView()
|
||||
pane?.splitRight(pane?.copyActiveItem()).activeView
|
||||
|
||||
# Public: Split the editor view up.
|
||||
splitUp: ->
|
||||
pane = @getPane()
|
||||
deprecate """
|
||||
Use Pane::splitUp instead.
|
||||
To duplicate this editor into the split use:
|
||||
editorView.getPaneView().getModel().splitUp(copyActiveItem: true)
|
||||
"""
|
||||
pane = @getPaneView()
|
||||
pane?.splitUp(pane?.copyActiveItem()).activeView
|
||||
|
||||
# Public: Split the editor view down.
|
||||
splitDown: ->
|
||||
pane = @getPane()
|
||||
deprecate """
|
||||
Use Pane::splitDown instead.
|
||||
To duplicate this editor into the split use:
|
||||
editorView.getPaneView().getModel().splitDown(copyActiveItem: true)
|
||||
"""
|
||||
pane = @getPaneView()
|
||||
pane?.splitDown(pane?.copyActiveItem()).activeView
|
||||
|
||||
# Public: Get this view's pane.
|
||||
# Public: Get this {EditorView}'s {PaneView}.
|
||||
#
|
||||
# Returns a {Pane}.
|
||||
getPane: ->
|
||||
# Returns a {PaneView}
|
||||
getPaneView: ->
|
||||
@parent('.item-views').parents('.pane').view()
|
||||
getPane: ->
|
||||
deprecate 'Use EditorView::getPaneView() instead'
|
||||
@getPaneView()
|
||||
|
||||
show: ->
|
||||
super
|
||||
@@ -273,72 +274,47 @@ class EditorView extends View
|
||||
deprecate('Use editorView.getModel().pageUp()')
|
||||
@editor.pageUp()
|
||||
|
||||
# Public: Retrieves the number of the row that is visible and currently at the
|
||||
# top of the editor.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getFirstVisibleScreenRow: ->
|
||||
@editor.getVisibleRowRange()[0]
|
||||
deprecate 'Use Editor::getFirstVisibleScreenRow instead. You can get the editor via editorView.getModel()'
|
||||
@editor.getFirstVisibleScreenRow()
|
||||
|
||||
# Public: Retrieves the number of the row that is visible and currently at the
|
||||
# bottom of the editor.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLastVisibleScreenRow: ->
|
||||
@editor.getVisibleRowRange()[1]
|
||||
deprecate 'Use Editor::getLastVisibleScreenRow instead. You can get the editor via editorView.getModel()'
|
||||
@editor.getLastVisibleScreenRow()
|
||||
|
||||
# Public: Gets the font family for the editor.
|
||||
#
|
||||
# Returns a {String} identifying the CSS `font-family`.
|
||||
getFontFamily: ->
|
||||
deprecate 'This is going away. Use atom.config.get("editor.fontFamily") instead'
|
||||
@component?.getFontFamily()
|
||||
|
||||
# Public: Sets the font family for the editor.
|
||||
#
|
||||
# * `fontFamily` A {String} identifying the CSS `font-family`.
|
||||
setFontFamily: (fontFamily) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.fontFamily", "my-font") instead'
|
||||
@component?.setFontFamily(fontFamily)
|
||||
|
||||
# Public: Retrieves the font size for the editor.
|
||||
#
|
||||
# Returns a {Number} indicating the font size in pixels.
|
||||
getFontSize: ->
|
||||
deprecate 'This is going away. Use atom.config.get("editor.fontSize") instead'
|
||||
@component?.getFontSize()
|
||||
|
||||
# Public: Sets the font size for the editor.
|
||||
#
|
||||
# * `fontSize` A {Number} indicating the font size in pixels.
|
||||
setFontSize: (fontSize) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.fontSize", 12) instead'
|
||||
@component?.setFontSize(fontSize)
|
||||
|
||||
setLineHeight: (lineHeight) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.lineHeight", 1.5) instead'
|
||||
@component.setLineHeight(lineHeight)
|
||||
|
||||
setWidthInChars: (widthInChars) ->
|
||||
@component.getDOMNode().style.width = (@editor.getDefaultCharWidth() * widthInChars) + 'px'
|
||||
|
||||
# Public: Sets the line height of the editor.
|
||||
#
|
||||
# Calling this method has no effect when called on a mini editor.
|
||||
#
|
||||
# * `lineHeight` A {Number} without a unit suffix identifying the CSS `line-height`.
|
||||
setLineHeight: (lineHeight) ->
|
||||
@component.setLineHeight(lineHeight)
|
||||
|
||||
# Public: Sets whether you want to show the indentation guides.
|
||||
#
|
||||
# * `showIndentGuide` A {Boolean} you can set to `true` if you want to see the
|
||||
# indentation guides.
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.showIndentGuide", true|false) instead'
|
||||
@component.setShowIndentGuide(showIndentGuide)
|
||||
|
||||
# Public: Enables/disables soft wrap on the editor.
|
||||
#
|
||||
# * `softWrap` A {Boolean} which, if `true`, enables soft wrap
|
||||
setSoftWrap: (softWrap) ->
|
||||
@editor.setSoftWrap(softWrap)
|
||||
setSoftWrap: (softWrapped) ->
|
||||
deprecate 'Use Editor::setSoftWrapped instead. You can get the editor via editorView.getModel()'
|
||||
@editor.setSoftWrapped(softWrapped)
|
||||
|
||||
# Public: Set whether invisible characters are shown.
|
||||
#
|
||||
# * `showInvisibles` A {Boolean} which, if `true`, show invisible characters.
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
deprecate 'This is going away. Use atom.config.set("editor.showInvisibles", true|false) instead'
|
||||
@component.setShowInvisibles(showInvisibles)
|
||||
|
||||
getText: ->
|
||||
|
||||
@@ -4,6 +4,8 @@ Serializable = require 'serializable'
|
||||
Delegator = require 'delegato'
|
||||
{deprecate} = require 'grim'
|
||||
{Model} = require 'theorist'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
LanguageMode = require './language-mode'
|
||||
DisplayBuffer = require './display-buffer'
|
||||
@@ -24,12 +26,12 @@ TextMateScopeSelector = require('first-mate').ScopeSelector
|
||||
# ## Accessing Editor Instances
|
||||
#
|
||||
# The easiest way to get hold of `Editor` objects is by registering a callback
|
||||
# with `::eachEditor` on the `atom.workspace` global. Your callback will then
|
||||
# be called with all current editor instances and also when any editor is
|
||||
# with `::observeTextEditors` on the `atom.workspace` global. Your callback will
|
||||
# then be called with all current editor instances and also when any editor is
|
||||
# created in the future.
|
||||
#
|
||||
# ```coffee
|
||||
# atom.workspace.eachEditor (editor) ->
|
||||
# atom.workspace.observeTextEditors (editor) ->
|
||||
# editor.insertText('Hello World')
|
||||
# ```
|
||||
#
|
||||
@@ -50,134 +52,6 @@ TextMateScopeSelector = require('first-mate').ScopeSelector
|
||||
#
|
||||
# **When in doubt, just default to buffer coordinates**, then experiment with
|
||||
# soft wraps and folds to ensure your code interacts with them correctly.
|
||||
#
|
||||
# ## Events
|
||||
#
|
||||
# ### path-changed
|
||||
#
|
||||
# Essential: Emit when the buffer's path, and therefore title, has changed.
|
||||
#
|
||||
# ### title-changed
|
||||
#
|
||||
# Essential: Emit when the buffer's path, and therefore title, has changed.
|
||||
#
|
||||
# ### modified-status-changed
|
||||
#
|
||||
# Extended: Emit when the result of {::isModified} changes.
|
||||
#
|
||||
# ### soft-wrap-changed
|
||||
#
|
||||
# Extended: Emit when soft wrap was enabled or disabled.
|
||||
#
|
||||
# * `softWrap` {Boolean} indicating whether soft wrap is enabled or disabled.
|
||||
#
|
||||
# ### grammar-changed
|
||||
#
|
||||
# Extended: Emit when the grammar that interprets and colorizes the text has
|
||||
# been changed.
|
||||
#
|
||||
#
|
||||
#
|
||||
# ### contents-modified
|
||||
#
|
||||
# Essential: Emit when the buffer's contents change. It is emit asynchronously
|
||||
# 300ms after the last buffer change. This is a good place to handle changes to
|
||||
# the buffer without compromising typing performance.
|
||||
#
|
||||
# ### contents-conflicted
|
||||
#
|
||||
# Extended: Emitted when the buffer's underlying file changes on disk at a
|
||||
# moment when the result of {::isModified} is true.
|
||||
#
|
||||
# ### will-insert-text
|
||||
#
|
||||
# Extended: Emit before the text has been inserted.
|
||||
#
|
||||
# * `event` event {Object}
|
||||
# * `text` {String} text to be inserted
|
||||
# * `cancel` {Function} Call to prevent the text from being inserted
|
||||
#
|
||||
# ### did-insert-text
|
||||
#
|
||||
# Extended: Emit after the text has been inserted.
|
||||
#
|
||||
# * `event` event {Object}
|
||||
# * `text` {String} text to be inserted
|
||||
#
|
||||
#
|
||||
#
|
||||
# ### cursor-moved
|
||||
#
|
||||
# Essential: Emit when a cursor has been moved. If there are multiple cursors,
|
||||
# it will be emit for each cursor.
|
||||
#
|
||||
# * `event` {Object}
|
||||
# * `oldBufferPosition` {Point}
|
||||
# * `oldScreenPosition` {Point}
|
||||
# * `newBufferPosition` {Point}
|
||||
# * `newScreenPosition` {Point}
|
||||
# * `textChanged` {Boolean}
|
||||
#
|
||||
# ### cursor-added
|
||||
#
|
||||
# Extended: Emit when a cursor has been added.
|
||||
#
|
||||
# * `cursor` {Cursor} that was added
|
||||
#
|
||||
# ### cursor-removed
|
||||
#
|
||||
# Extended: Emit when a cursor has been removed.
|
||||
#
|
||||
# * `cursor` {Cursor} that was removed
|
||||
#
|
||||
#
|
||||
#
|
||||
# ### selection-screen-range-changed
|
||||
#
|
||||
# Essential: Emit when a selection's screen range changes.
|
||||
#
|
||||
# * `selection`: {Selection} object that has a changed range
|
||||
#
|
||||
# ### selection-added
|
||||
#
|
||||
# Extended: Emit when a selection's was added.
|
||||
#
|
||||
# * `selection`: {Selection} object that was added
|
||||
#
|
||||
# ### selection-removed
|
||||
#
|
||||
# Extended: Emit when a selection's was removed.
|
||||
#
|
||||
# * `selection`: {Selection} object that was removed
|
||||
#
|
||||
#
|
||||
#
|
||||
# ### decoration-added
|
||||
#
|
||||
# Extended: Emit when a {Decoration} is added to the editor.
|
||||
#
|
||||
# * `decoration` {Decoration} that was added
|
||||
#
|
||||
# ### decoration-removed
|
||||
#
|
||||
# Extended: Emit when a {Decoration} is removed from the editor.
|
||||
#
|
||||
# * `decoration` {Decoration} that was removed
|
||||
#
|
||||
# ### decoration-changed
|
||||
#
|
||||
# Extended: Emit when a {Decoration}'s underlying marker changes. Say the user
|
||||
# inserts newlines above a decoration. That action will move the marker down,
|
||||
# and fire this event.
|
||||
#
|
||||
# * `decoration` {Decoration} that was added
|
||||
#
|
||||
# ### decoration-updated
|
||||
#
|
||||
# Extended: Emit when a {Decoration} is updated via the {Decoration::update} method.
|
||||
#
|
||||
# * `decoration` {Decoration} that was updated
|
||||
#
|
||||
module.exports =
|
||||
class Editor extends Model
|
||||
Serializable.includeInto(this)
|
||||
@@ -203,9 +77,10 @@ class Editor extends Model
|
||||
'$verticalScrollbarWidth', '$horizontalScrollbarHeight', '$scrollTop', '$scrollLeft',
|
||||
'manageScrollPosition', toProperty: 'displayBuffer'
|
||||
|
||||
constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrap, @displayBuffer, buffer, registerEditor, suppressCursorCreation, @mini}) ->
|
||||
constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, buffer, registerEditor, suppressCursorCreation, @mini}) ->
|
||||
super
|
||||
|
||||
@emitter = new Emitter
|
||||
@cursors = []
|
||||
@selections = []
|
||||
|
||||
@@ -213,7 +88,7 @@ class Editor extends Model
|
||||
invisibles = atom.config.get('editor.invisibles')
|
||||
|
||||
@displayBuffer?.setInvisibles(invisibles)
|
||||
@displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrap, invisibles})
|
||||
@displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrapped, invisibles})
|
||||
@buffer = @displayBuffer.buffer
|
||||
@softTabs = @usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true
|
||||
|
||||
@@ -231,8 +106,12 @@ class Editor extends Model
|
||||
|
||||
@languageMode = new LanguageMode(this)
|
||||
|
||||
@subscribe @$scrollTop, (scrollTop) => @emit 'scroll-top-changed', scrollTop
|
||||
@subscribe @$scrollLeft, (scrollLeft) => @emit 'scroll-left-changed', scrollLeft
|
||||
@subscribe @$scrollTop, (scrollTop) =>
|
||||
@emit 'scroll-top-changed', scrollTop
|
||||
@emitter.emit 'did-change-scroll-top', scrollTop
|
||||
@subscribe @$scrollLeft, (scrollLeft) =>
|
||||
@emit 'scroll-left-changed', scrollLeft
|
||||
@emitter.emit 'did-change-scroll-left', scrollLeft
|
||||
|
||||
@subscribe atom.config.observe 'editor.showInvisibles', callNow: false, (show) => @updateInvisibles()
|
||||
@subscribe atom.config.observe 'editor.invisibles', callNow: false, => @updateInvisibles()
|
||||
@@ -253,29 +132,35 @@ class Editor extends Model
|
||||
|
||||
subscribeToBuffer: ->
|
||||
@buffer.retain()
|
||||
@subscribe @buffer, "path-changed", =>
|
||||
@subscribe @buffer.onDidChangePath =>
|
||||
unless atom.project.getPath()?
|
||||
atom.project.setPath(path.dirname(@getPath()))
|
||||
@emit "title-changed"
|
||||
@emitter.emit 'did-change-title', @getTitle()
|
||||
@emit "path-changed"
|
||||
@subscribe @buffer, "contents-modified", => @emit "contents-modified"
|
||||
@subscribe @buffer, "contents-conflicted", => @emit "contents-conflicted"
|
||||
@subscribe @buffer, "modified-status-changed", => @emit "modified-status-changed"
|
||||
@subscribe @buffer, "destroyed", => @destroy()
|
||||
@emitter.emit 'did-change-path', @getPath()
|
||||
@subscribe @buffer.onDidDestroy => @destroy()
|
||||
|
||||
# TODO: remove these thwne we remove the deprecations. They are old events.
|
||||
@subscribe @buffer.onDidStopChanging => @emit "contents-modified"
|
||||
@subscribe @buffer.onDidConflict => @emit "contents-conflicted"
|
||||
@subscribe @buffer.onDidChangeModified => @emit "modified-status-changed"
|
||||
|
||||
@preserveCursorPositionOnBufferReload()
|
||||
|
||||
subscribeToDisplayBuffer: ->
|
||||
@subscribe @displayBuffer, 'marker-created', @handleMarkerCreated
|
||||
@subscribe @displayBuffer, "changed", (e) => @emit 'screen-lines-changed', e
|
||||
@subscribe @displayBuffer, "markers-updated", => @mergeIntersectingSelections()
|
||||
@subscribe @displayBuffer, 'grammar-changed', => @handleGrammarChange()
|
||||
@subscribe @displayBuffer, 'tokenized', => @handleTokenization()
|
||||
@subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args...
|
||||
@subscribe @displayBuffer, "decoration-added", (args...) => @emit 'decoration-added', args...
|
||||
@subscribe @displayBuffer, "decoration-removed", (args...) => @emit 'decoration-removed', args...
|
||||
@subscribe @displayBuffer, "decoration-changed", (args...) => @emit 'decoration-changed', args...
|
||||
@subscribe @displayBuffer, "decoration-updated", (args...) => @emit 'decoration-updated', args...
|
||||
@subscribe @displayBuffer, "character-widths-changed", (changeCount) => @emit 'character-widths-changed', changeCount
|
||||
@subscribe @displayBuffer.onDidCreateMarker @handleMarkerCreated
|
||||
@subscribe @displayBuffer.onDidUpdateMarkers => @mergeIntersectingSelections()
|
||||
@subscribe @displayBuffer.onDidChangeGrammar => @handleGrammarChange()
|
||||
@subscribe @displayBuffer.onDidTokenize => @handleTokenization()
|
||||
@subscribe @displayBuffer.onDidChange (e) =>
|
||||
@emit 'screen-lines-changed', e
|
||||
@emitter.emit 'did-change-screen-lines', e
|
||||
|
||||
# TODO: remove these when we remove the deprecations. Though, no one is likely using them
|
||||
@subscribe @displayBuffer.onDidChangeSoftWrapped (softWrapped) => @emit 'soft-wrap-changed', softWrapped
|
||||
@subscribe @displayBuffer.onDidAddDecoration (decoration) => @emit 'decoration-added', decoration
|
||||
@subscribe @displayBuffer.onDidRemoveDecoration (decoration) => @emit 'decoration-removed', decoration
|
||||
|
||||
getViewClass: ->
|
||||
require './editor-view'
|
||||
@@ -287,6 +172,280 @@ class Editor extends Model
|
||||
@displayBuffer.destroy()
|
||||
@languageMode.destroy()
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Essential: Calls your `callback` when the buffer's title has changed.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeTitle: (callback) ->
|
||||
@emitter.on 'did-change-title', callback
|
||||
|
||||
# Essential: Calls your `callback` when the buffer's path, and therefore title, has changed.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangePath: (callback) ->
|
||||
@emitter.on 'did-change-path', callback
|
||||
|
||||
# Extended: Calls your `callback` when soft wrap was enabled or disabled.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeSoftWrapped: (callback) ->
|
||||
@displayBuffer.onDidChangeSoftWrapped(callback)
|
||||
|
||||
# Extended: Calls your `callback` when the grammar that interprets and colorizes the text has
|
||||
# been changed.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeGrammar: (callback) ->
|
||||
@emitter.on 'did-change-grammar', callback
|
||||
|
||||
# Essential: Calls your `callback` when the buffer's contents change. It is
|
||||
# emit asynchronously 300ms after the last buffer change. This is a good place
|
||||
# to handle changes to the buffer without compromising typing performance.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidStopChanging: (callback) ->
|
||||
@getBuffer().onDidStopChanging(callback)
|
||||
|
||||
# Extended: Calls your `callback` when the result of {::isModified} changes.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeModified: (callback) ->
|
||||
@getBuffer().onDidChangeModified(callback)
|
||||
|
||||
# Extended: Calls your `callback` when the buffer's underlying file changes on
|
||||
# disk at a moment when the result of {::isModified} is true.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidConflict: (callback) ->
|
||||
@getBuffer().onDidConflict(callback)
|
||||
|
||||
# Extended: Calls your `callback` before text has been inserted.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` event {Object}
|
||||
# * `text` {String} text to be inserted
|
||||
# * `cancel` {Function} Call to prevent the text from being inserted
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onWillInsertText: (callback) ->
|
||||
@emitter.on 'will-insert-text', callback
|
||||
|
||||
# Extended: Calls your `callback` adter text has been inserted.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` event {Object}
|
||||
# * `text` {String} text to be inserted
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidInsertText: (callback) ->
|
||||
@emitter.on 'did-insert-text', callback
|
||||
|
||||
# Public: Invoke the given callback after the buffer is saved to disk.
|
||||
#
|
||||
# * `callback` {Function} to be called after the buffer is saved.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `path` The path to which the buffer was saved.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidSave: (callback) ->
|
||||
@getBuffer().onDidSave(callback)
|
||||
|
||||
# Extended: Calls your `callback` when a {Cursor} is added to the editor.
|
||||
# Immediately calls your callback for each existing cursor.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `selection` {Selection} that was added
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observeCursors: (callback) ->
|
||||
callback(cursor) for cursor in @getCursors()
|
||||
@onDidAddCursor(callback)
|
||||
|
||||
# Extended: Calls your `callback` when a {Cursor} is added to the editor.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `cursor` {Cursor} that was added
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidAddCursor: (callback) ->
|
||||
@emitter.on 'did-add-cursor', callback
|
||||
|
||||
# Extended: Calls your `callback` when a {Cursor} is removed from the editor.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `cursor` {Cursor} that was removed
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidRemoveCursor: (callback) ->
|
||||
@emitter.on 'did-remove-cursor', callback
|
||||
|
||||
# Essential: Calls your `callback` when a {Cursor} is moved. If there are
|
||||
# multiple cursors, your callback will be called for each cursor.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` {Object}
|
||||
# * `oldBufferPosition` {Point}
|
||||
# * `oldScreenPosition` {Point}
|
||||
# * `newBufferPosition` {Point}
|
||||
# * `newScreenPosition` {Point}
|
||||
# * `textChanged` {Boolean}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeCursorPosition: (callback) ->
|
||||
@emitter.on 'did-change-cursor-position', callback
|
||||
|
||||
# Extended: Calls your `callback` when a {Selection} is added to the editor.
|
||||
# Immediately calls your callback for each existing selection.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `selection` {Selection} that was added
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observeSelections: (callback) ->
|
||||
callback(selection) for selection in @getSelections()
|
||||
@onDidAddSelection(callback)
|
||||
|
||||
# Extended: Calls your `callback` when a {Selection} is added to the editor.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `selection` {Selection} that was added
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidAddSelection: (callback) ->
|
||||
@emitter.on 'did-add-selection', callback
|
||||
|
||||
# Extended: Calls your `callback` when a {Selection} is removed from the editor.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `selection` {Selection} that was removed
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidRemoveSelection: (callback) ->
|
||||
@emitter.on 'did-remove-selection', callback
|
||||
|
||||
# Essential: Calls your `callback` when a selection's screen range changes.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `selection` {Selection} that moved
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeSelectionRange: (callback) ->
|
||||
@emitter.on 'did-change-selection-range', callback
|
||||
|
||||
# Extended: Calls your `callback` with each {Decoration} added to the editor.
|
||||
# Calls your `callback` immediately for any existing decorations.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `decoration` {Decoration}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observeDecorations: (callback) ->
|
||||
@displayBuffer.observeDecorations(callback)
|
||||
|
||||
# Extended: Calls your `callback` when a {Decoration} is added to the editor.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `decoration` {Decoration} that was added
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidAddDecoration: (callback) ->
|
||||
@displayBuffer.onDidAddDecoration(callback)
|
||||
|
||||
# Extended: Calls your `callback` when a {Decoration} is removed from the editor.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `decoration` {Decoration} that was removed
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidRemoveDecoration: (callback) ->
|
||||
@displayBuffer.onDidRemoveDecoration(callback)
|
||||
|
||||
onDidChangeCharacterWidths: (callback) ->
|
||||
@displayBuffer.onDidChangeCharacterWidths(callback)
|
||||
|
||||
onDidChangeScreenLines: (callback) ->
|
||||
@emitter.on 'did-change-screen-lines', callback
|
||||
|
||||
onDidChangeScrollTop: (callback) ->
|
||||
@emitter.on 'did-change-scroll-top', callback
|
||||
|
||||
onDidChangeScrollLeft: (callback) ->
|
||||
@emitter.on 'did-change-scroll-left', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'title-changed'
|
||||
deprecate("Use Editor::onDidChangeTitle instead")
|
||||
when 'path-changed'
|
||||
deprecate("Use Editor::onDidChangePath instead")
|
||||
when 'modified-status-changed'
|
||||
deprecate("Use Editor::onDidChangeModified instead")
|
||||
when 'soft-wrap-changed'
|
||||
deprecate("Use Editor::onDidChangeSoftWrapped instead")
|
||||
when 'grammar-changed'
|
||||
deprecate("Use Editor::onDidChangeGrammar instead")
|
||||
when 'character-widths-changed'
|
||||
deprecate("Use Editor::onDidChangeCharacterWidths instead")
|
||||
when 'contents-modified'
|
||||
deprecate("Use Editor::onDidStopChanging instead")
|
||||
when 'contents-conflicted'
|
||||
deprecate("Use Editor::onDidConflict instead")
|
||||
|
||||
when 'will-insert-text'
|
||||
deprecate("Use Editor::onWillInsertText instead")
|
||||
when 'did-insert-text'
|
||||
deprecate("Use Editor::onDidInsertText instead")
|
||||
|
||||
when 'cursor-added'
|
||||
deprecate("Use Editor::onDidAddCursor instead")
|
||||
when 'cursor-removed'
|
||||
deprecate("Use Editor::onDidRemoveCursor instead")
|
||||
when 'cursor-moved'
|
||||
deprecate("Use Editor::onDidChangeCursorPosition instead")
|
||||
|
||||
when 'selection-added'
|
||||
deprecate("Use Editor::onDidAddSelection instead")
|
||||
when 'selection-removed'
|
||||
deprecate("Use Editor::onDidRemoveSelection instead")
|
||||
when 'selection-screen-range-changed'
|
||||
deprecate("Use Editor::onDidChangeSelectionRange instead")
|
||||
|
||||
when 'decoration-added'
|
||||
deprecate("Use Editor::onDidAddDecoration instead")
|
||||
when 'decoration-removed'
|
||||
deprecate("Use Editor::onDidRemoveDecoration instead")
|
||||
when 'decoration-updated'
|
||||
deprecate("Use Decoration::onDidChangeProperties instead. You will get the decoration back from `Editor::decorateMarker()`")
|
||||
when 'decoration-changed'
|
||||
deprecate("Use Marker::onDidChange instead. eg. `editor::decorateMarker(...).getMarker().onDidChange()`")
|
||||
|
||||
when 'screen-lines-changed'
|
||||
deprecate("Use Editor::onDidChangeScreenLines instead")
|
||||
|
||||
when 'scroll-top-changed'
|
||||
deprecate("Use Editor::onDidChangeScrollTop instead")
|
||||
when 'scroll-left-changed'
|
||||
deprecate("Use Editor::onDidChangeScrollLeft instead")
|
||||
|
||||
EmitterMixin::on.apply(this, arguments)
|
||||
|
||||
# Retrieves the current {TextBuffer}.
|
||||
getBuffer: -> @buffer
|
||||
|
||||
@@ -735,14 +894,18 @@ class Editor extends Model
|
||||
insertText: (text, options={}) ->
|
||||
willInsert = true
|
||||
cancel = -> willInsert = false
|
||||
@emit('will-insert-text', {cancel, text})
|
||||
willInsertEvent = {cancel, text}
|
||||
@emit('will-insert-text', willInsertEvent)
|
||||
@emitter.emit 'will-insert-text', willInsertEvent
|
||||
|
||||
if willInsert
|
||||
options.autoIndentNewline ?= @shouldAutoIndent()
|
||||
options.autoDecreaseIndent ?= @shouldAutoIndent()
|
||||
@mutateSelectedText (selection) =>
|
||||
range = selection.insertText(text, options)
|
||||
@emit('did-insert-text', {text, range})
|
||||
didInsertEvent = {text, range}
|
||||
@emit('did-insert-text', didInsertEvent)
|
||||
@emitter.emit 'did-insert-text', didInsertEvent
|
||||
range
|
||||
else
|
||||
false
|
||||
@@ -904,18 +1067,31 @@ class Editor extends Model
|
||||
# Public: Sets the column at which column will soft wrap
|
||||
getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn()
|
||||
|
||||
# Public: Get whether soft wrap is enabled for this editor.
|
||||
getSoftWrap: -> @displayBuffer.getSoftWrap()
|
||||
|
||||
# Public: Enable or disable soft wrap for this editor.
|
||||
# Public: Determine whether lines in this editor are soft-wrapped.
|
||||
#
|
||||
# * `softWrap` A {Boolean}
|
||||
setSoftWrap: (softWrap) -> @displayBuffer.setSoftWrap(softWrap)
|
||||
|
||||
# Public: Toggle soft wrap for this editor
|
||||
toggleSoftWrap: -> @setSoftWrap(not @getSoftWrap())
|
||||
# Returns a {Boolean}.
|
||||
isSoftWrapped: (softWrapped) -> @displayBuffer.isSoftWrapped()
|
||||
getSoftWrapped: ->
|
||||
deprecate("Use Editor::isSoftWrapped instead")
|
||||
@displayBuffer.isSoftWrapped()
|
||||
|
||||
# Public: Enable or disable soft wrapping for this editor.
|
||||
#
|
||||
# * `softWrapped` A {Boolean}
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
setSoftWrapped: (softWrapped) -> @displayBuffer.setSoftWrapped(softWrapped)
|
||||
setSoftWrap: (softWrapped) ->
|
||||
deprecate("Use Editor::setSoftWrapped instead")
|
||||
@setSoftWrapped(softWrapped)
|
||||
|
||||
# Public: Toggle soft wrapping for this editor
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
toggleSoftWrapped: -> @setSoftWrapped(not @isSoftWrapped())
|
||||
toggleSoftWrap: ->
|
||||
deprecate("Use Editor::toggleSoftWrapped instead")
|
||||
@toggleSoftWrapped()
|
||||
|
||||
###
|
||||
Section: Indentation
|
||||
@@ -1199,7 +1375,8 @@ class Editor extends Model
|
||||
isBufferRowCommented: (bufferRow) ->
|
||||
if match = @lineTextForBufferRow(bufferRow).match(/\S/)
|
||||
scopes = @tokenForBufferPosition([bufferRow, match.index]).scopes
|
||||
new TextMateScopeSelector('comment.*').matches(scopes)
|
||||
@commentScopeSelector ?= new TextMateScopeSelector('comment.*')
|
||||
@commentScopeSelector.matches(scopes)
|
||||
|
||||
# Public: Toggle line comments for rows intersecting selections.
|
||||
#
|
||||
@@ -1619,7 +1796,7 @@ class Editor extends Model
|
||||
|
||||
# Essential: Move every cursor up one row in screen coordinates.
|
||||
#
|
||||
# * `lineCount` {Number} number of lines to move
|
||||
# * `lineCount` (optional) {Number} number of lines to move
|
||||
moveUp: (lineCount) ->
|
||||
@moveCursors (cursor) -> cursor.moveUp(lineCount, moveToEndOfSelection: true)
|
||||
moveCursorUp: (lineCount) ->
|
||||
@@ -1628,7 +1805,7 @@ class Editor extends Model
|
||||
|
||||
# Essential: Move every cursor down one row in screen coordinates.
|
||||
#
|
||||
# * `lineCount` {Number} number of lines to move
|
||||
# * `lineCount` (optional) {Number} number of lines to move
|
||||
moveDown: (lineCount) ->
|
||||
@moveCursors (cursor) -> cursor.moveDown(lineCount, moveToEndOfSelection: true)
|
||||
moveCursorDown: (lineCount) ->
|
||||
@@ -1636,15 +1813,19 @@ class Editor extends Model
|
||||
@moveDown(lineCount)
|
||||
|
||||
# Essential: Move every cursor left one column.
|
||||
moveLeft: ->
|
||||
@moveCursors (cursor) -> cursor.moveLeft(moveToEndOfSelection: true)
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to move (default: 1)
|
||||
moveLeft: (columnCount) ->
|
||||
@moveCursors (cursor) -> cursor.moveLeft(columnCount, moveToEndOfSelection: true)
|
||||
moveCursorLeft: ->
|
||||
deprecate("Use Editor::moveLeft() instead")
|
||||
@moveLeft()
|
||||
|
||||
# Essential: Move every cursor right one column.
|
||||
moveRight: ->
|
||||
@moveCursors (cursor) -> cursor.moveRight(moveToEndOfSelection: true)
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to move (default: 1)
|
||||
moveRight: (columnCount) ->
|
||||
@moveCursors (cursor) -> cursor.moveRight(columnCount, moveToEndOfSelection: true)
|
||||
moveCursorRight: ->
|
||||
deprecate("Use Editor::moveRight() instead")
|
||||
@moveRight()
|
||||
@@ -1787,23 +1968,22 @@ class Editor extends Model
|
||||
@decorateMarker(marker, type: 'gutter', class: 'cursor-line-no-selection', onlyHead: true, onlyEmpty: true)
|
||||
@decorateMarker(marker, type: 'line', class: 'cursor-line', onlyEmpty: true)
|
||||
@emit 'cursor-added', cursor
|
||||
@emitter.emit 'did-add-cursor', cursor
|
||||
cursor
|
||||
|
||||
# Remove the given cursor from this editor.
|
||||
removeCursor: (cursor) ->
|
||||
_.remove(@cursors, cursor)
|
||||
@emit 'cursor-removed', cursor
|
||||
@emitter.emit 'did-remove-cursor', cursor
|
||||
|
||||
moveCursors: (fn) ->
|
||||
@movingCursors = true
|
||||
fn(cursor) for cursor in @getCursors()
|
||||
@mergeCursors()
|
||||
@movingCursors = false
|
||||
@emit 'cursors-moved'
|
||||
|
||||
cursorMoved: (event) ->
|
||||
cursorMoved: (cursor, event) ->
|
||||
@emit 'cursor-moved', event
|
||||
@emit 'cursors-moved' unless @movingCursors
|
||||
@emitter.emit 'did-change-cursor-position', event
|
||||
|
||||
# Merge cursors that have the same screen position
|
||||
mergeCursors: ->
|
||||
@@ -1817,9 +1997,9 @@ class Editor extends Model
|
||||
|
||||
preserveCursorPositionOnBufferReload: ->
|
||||
cursorPosition = null
|
||||
@subscribe @buffer, "will-reload", =>
|
||||
@subscribe @buffer.onWillReload =>
|
||||
cursorPosition = @getCursorBufferPosition()
|
||||
@subscribe @buffer, "reloaded", =>
|
||||
@subscribe @buffer.onDidReload =>
|
||||
@setCursorBufferPosition(cursorPosition) if cursorPosition
|
||||
cursorPosition = null
|
||||
|
||||
@@ -1978,7 +2158,7 @@ class Editor extends Model
|
||||
# Essential: Move the cursor of each selection one character upward while
|
||||
# preserving the selection's tail position.
|
||||
#
|
||||
# * `rowCount` {Number} of rows to select up
|
||||
# * `rowCount` (optional) {Number} number of rows to select (default: 1)
|
||||
#
|
||||
# This method may merge selections that end up intesecting.
|
||||
selectUp: (rowCount) ->
|
||||
@@ -1987,7 +2167,7 @@ class Editor extends Model
|
||||
# Essential: Move the cursor of each selection one character downward while
|
||||
# preserving the selection's tail position.
|
||||
#
|
||||
# * `rowCount` {Number} of rows to select down
|
||||
# * `rowCount` (optional) {Number} number of rows to select (default: 1)
|
||||
#
|
||||
# This method may merge selections that end up intesecting.
|
||||
selectDown: (rowCount) ->
|
||||
@@ -1996,16 +2176,20 @@ class Editor extends Model
|
||||
# Essential: Move the cursor of each selection one character leftward while
|
||||
# preserving the selection's tail position.
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to select (default: 1)
|
||||
#
|
||||
# This method may merge selections that end up intesecting.
|
||||
selectLeft: ->
|
||||
@expandSelectionsBackward (selection) -> selection.selectLeft()
|
||||
selectLeft: (columnCount) ->
|
||||
@expandSelectionsBackward (selection) -> selection.selectLeft(columnCount)
|
||||
|
||||
# Essential: Move the cursor of each selection one character rightward while
|
||||
# preserving the selection's tail position.
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to select (default: 1)
|
||||
#
|
||||
# This method may merge selections that end up intesecting.
|
||||
selectRight: ->
|
||||
@expandSelectionsForward (selection) -> selection.selectRight()
|
||||
selectRight: (columnCount) ->
|
||||
@expandSelectionsForward (selection) -> selection.selectRight(columnCount)
|
||||
|
||||
# Essential: Select from the top of the buffer to the end of the last selection
|
||||
# in the buffer.
|
||||
@@ -2248,19 +2432,21 @@ class Editor extends Model
|
||||
selection = new Selection(_.extend({editor: this, marker, cursor}, options))
|
||||
@selections.push(selection)
|
||||
selectionBufferRange = selection.getBufferRange()
|
||||
@mergeIntersectingSelections()
|
||||
@mergeIntersectingSelections(preserveFolds: marker.getAttributes().preserveFolds)
|
||||
if selection.destroyed
|
||||
for selection in @getSelections()
|
||||
if selection.intersectsBufferRange(selectionBufferRange)
|
||||
return selection
|
||||
else
|
||||
@emit 'selection-added', selection
|
||||
@emitter.emit 'did-add-selection', selection
|
||||
selection
|
||||
|
||||
# Remove the given selection.
|
||||
removeSelection: (selection) ->
|
||||
_.remove(@selections, selection)
|
||||
@emit 'selection-removed', selection
|
||||
@emitter.emit 'did-remove-selection', selection
|
||||
|
||||
# Reduce one or more selections to a single empty selection based on the most
|
||||
# recently added cursor.
|
||||
@@ -2277,22 +2463,62 @@ class Editor extends Model
|
||||
else
|
||||
false
|
||||
|
||||
selectionScreenRangeChanged: (selection) ->
|
||||
# Called by the selection
|
||||
selectionRangeChanged: (selection) ->
|
||||
@emit 'selection-screen-range-changed', selection
|
||||
@emitter.emit 'did-change-selection-range', selection
|
||||
|
||||
|
||||
###
|
||||
Section: Scrolling the Editor
|
||||
###
|
||||
|
||||
# Public: Scroll the editor to reveal the most recently added cursor if it is
|
||||
# Essential: Scroll the editor to reveal the most recently added cursor if it is
|
||||
# off-screen.
|
||||
#
|
||||
# * `options` (optional) {Object}
|
||||
# * `center` Center the editor around the cursor if possible. Defauls to true.
|
||||
# * `center` Center the editor around the cursor if possible. (default: true)
|
||||
scrollToCursorPosition: (options) ->
|
||||
@getLastCursor().autoscroll(center: options?.center ? true)
|
||||
|
||||
# Essential: Scrolls the editor to the given buffer position.
|
||||
#
|
||||
# * `bufferPosition` An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# * `options` (optional) {Object}
|
||||
# * `center` Center the editor around the position if possible. (default: false)
|
||||
scrollToBufferPosition: (bufferPosition, options) ->
|
||||
@displayBuffer.scrollToBufferPosition(bufferPosition, options)
|
||||
|
||||
# Essential: Scrolls the editor to the given screen position.
|
||||
#
|
||||
# * `screenPosition` An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
# * `options` (optional) {Object}
|
||||
# * `center` Center the editor around the position if possible. (default: false)
|
||||
scrollToScreenPosition: (screenPosition, options) ->
|
||||
@displayBuffer.scrollToScreenPosition(screenPosition, options)
|
||||
|
||||
# Essential: Scrolls the editor to the top
|
||||
scrollToTop: ->
|
||||
@setScrollTop(0)
|
||||
|
||||
# Essential: Scrolls the editor to the bottom
|
||||
scrollToBottom: ->
|
||||
@setScrollBottom(Infinity)
|
||||
|
||||
scrollToScreenRange: (screenRange, options) -> @displayBuffer.scrollToScreenRange(screenRange, options)
|
||||
|
||||
horizontallyScrollable: -> @displayBuffer.horizontallyScrollable()
|
||||
|
||||
verticallyScrollable: -> @displayBuffer.verticallyScrollable()
|
||||
|
||||
getHorizontalScrollbarHeight: -> @displayBuffer.getHorizontalScrollbarHeight()
|
||||
setHorizontalScrollbarHeight: (height) -> @displayBuffer.setHorizontalScrollbarHeight(height)
|
||||
|
||||
getVerticalScrollbarWidth: -> @displayBuffer.getVerticalScrollbarWidth()
|
||||
setVerticalScrollbarWidth: (width) -> @displayBuffer.setVerticalScrollbarWidth(width)
|
||||
|
||||
pageUp: ->
|
||||
newScrollTop = @getScrollTop() - @getHeight()
|
||||
@moveUp(@getRowsPerPage())
|
||||
@@ -2341,6 +2567,7 @@ class Editor extends Model
|
||||
handleGrammarChange: ->
|
||||
@unfoldAll()
|
||||
@emit 'grammar-changed'
|
||||
@emitter.emit 'did-change-grammar'
|
||||
|
||||
handleMarkerCreated: (marker) =>
|
||||
if marker.matchesAttributes(@getSelectionMarkerAttributes())
|
||||
@@ -2350,6 +2577,36 @@ class Editor extends Model
|
||||
Section: Editor Rendering
|
||||
###
|
||||
|
||||
# Extended: Retrieves the number of the row that is visible and currently at the
|
||||
# top of the editor.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getFirstVisibleScreenRow: ->
|
||||
@getVisibleRowRange()[0]
|
||||
|
||||
# Extended: Retrieves the number of the row that is visible and currently at the
|
||||
# bottom of the editor.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getLastVisibleScreenRow: ->
|
||||
@getVisibleRowRange()[1]
|
||||
|
||||
# Extended: Converts a buffer position to a pixel position.
|
||||
#
|
||||
# * `bufferPosition` An object that represents a buffer position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
#
|
||||
# Returns an {Object} with two values: `top` and `left`, representing the pixel positions.
|
||||
pixelPositionForBufferPosition: (bufferPosition) -> @displayBuffer.pixelPositionForBufferPosition(bufferPosition)
|
||||
|
||||
# Extended: Converts a screen position to a pixel position.
|
||||
#
|
||||
# * `screenPosition` An object that represents a screen position. It can be either
|
||||
# an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
|
||||
#
|
||||
# Returns an {Object} with two values: `top` and `left`, representing the pixel positions.
|
||||
pixelPositionForScreenPosition: (screenPosition) -> @displayBuffer.pixelPositionForScreenPosition(screenPosition)
|
||||
|
||||
getSelectionMarkerAttributes: ->
|
||||
type: 'selection', editorId: @id, invalidate: 'never'
|
||||
|
||||
@@ -2403,30 +2660,10 @@ class Editor extends Model
|
||||
|
||||
selectionIntersectsVisibleRowRange: (selection) -> @displayBuffer.selectionIntersectsVisibleRowRange(selection)
|
||||
|
||||
pixelPositionForScreenPosition: (screenPosition) -> @displayBuffer.pixelPositionForScreenPosition(screenPosition)
|
||||
|
||||
pixelPositionForBufferPosition: (bufferPosition) -> @displayBuffer.pixelPositionForBufferPosition(bufferPosition)
|
||||
|
||||
screenPositionForPixelPosition: (pixelPosition) -> @displayBuffer.screenPositionForPixelPosition(pixelPosition)
|
||||
|
||||
pixelRectForScreenRange: (screenRange) -> @displayBuffer.pixelRectForScreenRange(screenRange)
|
||||
|
||||
scrollToScreenRange: (screenRange, options) -> @displayBuffer.scrollToScreenRange(screenRange, options)
|
||||
|
||||
scrollToScreenPosition: (screenPosition, options) -> @displayBuffer.scrollToScreenPosition(screenPosition, options)
|
||||
|
||||
scrollToBufferPosition: (bufferPosition, options) -> @displayBuffer.scrollToBufferPosition(bufferPosition, options)
|
||||
|
||||
horizontallyScrollable: -> @displayBuffer.horizontallyScrollable()
|
||||
|
||||
verticallyScrollable: -> @displayBuffer.verticallyScrollable()
|
||||
|
||||
getHorizontalScrollbarHeight: -> @displayBuffer.getHorizontalScrollbarHeight()
|
||||
setHorizontalScrollbarHeight: (height) -> @displayBuffer.setHorizontalScrollbarHeight(height)
|
||||
|
||||
getVerticalScrollbarWidth: -> @displayBuffer.getVerticalScrollbarWidth()
|
||||
setVerticalScrollbarWidth: (width) -> @displayBuffer.setVerticalScrollbarWidth(width)
|
||||
|
||||
# Deprecated: Call {::joinLines} instead.
|
||||
joinLine: ->
|
||||
deprecate("Use Editor::joinLines() instead")
|
||||
|
||||
@@ -14,8 +14,8 @@ class Fold
|
||||
@id = @marker.id
|
||||
@displayBuffer.foldsByMarkerId[@marker.id] = this
|
||||
@updateDisplayBuffer()
|
||||
@marker.on 'destroyed', => @destroyed()
|
||||
@marker.on 'changed', ({isValid}) => @destroy() unless isValid
|
||||
@marker.onDidDestroy => @destroyed()
|
||||
@marker.onDidChange ({isValid}) => @destroy() unless isValid
|
||||
|
||||
# Returns whether this fold is contained within another fold
|
||||
isInsideLargerFold: ->
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{basename, join} = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
{Subscriber} = require 'emissary'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
fs = require 'fs-plus'
|
||||
GitUtils = require 'git-utils'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
Task = require './task'
|
||||
|
||||
@@ -20,28 +23,34 @@ Task = require './task'
|
||||
# For a repository with submodules this would have the following outcome:
|
||||
#
|
||||
# ```coffee
|
||||
# repo = atom.project.getRepo()
|
||||
# repo.getShortHead() # 'master'
|
||||
# repo.getShortHead('vendor/path/to/a/submodule') # 'dead1234'
|
||||
# repo = atom.project.getRepo()
|
||||
# repo.getShortHead() # 'master'
|
||||
# repo.getShortHead('vendor/path/to/a/submodule') # 'dead1234'
|
||||
# ```
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# ### Logging the URL of the origin remote
|
||||
#
|
||||
# ```coffee
|
||||
# git = atom.project.getRepo()
|
||||
# console.log git.getOriginUrl()
|
||||
# git = atom.project.getRepo()
|
||||
# console.log git.getOriginUrl()
|
||||
# ```
|
||||
#
|
||||
# ## Requiring in packages
|
||||
# ### Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {Git} = require 'atom'
|
||||
# {Git} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class Git
|
||||
Emitter.includeInto(this)
|
||||
EmitterMixin.includeInto(this)
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
###
|
||||
Section: Class Methods
|
||||
###
|
||||
|
||||
# Public: Creates a new Git instance.
|
||||
#
|
||||
# * `path` The {String} path to the Git repository to open.
|
||||
@@ -64,7 +73,12 @@ class Git
|
||||
else
|
||||
false
|
||||
|
||||
###
|
||||
Section: Construction
|
||||
###
|
||||
|
||||
constructor: (path, options={}) ->
|
||||
@emitter = new Emitter
|
||||
@repo = GitUtils.open(path)
|
||||
unless @repo?
|
||||
throw new Error("No Git repository found searching path: #{path}")
|
||||
@@ -86,12 +100,59 @@ class Git
|
||||
if @project?
|
||||
@subscribe @project.eachBuffer (buffer) => @subscribeToBuffer(buffer)
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Essential: Invoke the given callback when a specific file's status has
|
||||
# changed. When a file is updated, reloaded, etc, and the status changes, this
|
||||
# will be fired.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `event` {Object}
|
||||
# * `path` {String} the old parameters the decoration used to have
|
||||
# * `pathStatus` {Number} representing the status. This value can be passed to
|
||||
# {::isStatusModified} or {::isStatusNew} to get more information.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeStatus: (callback) ->
|
||||
@emitter.on 'did-change-status', callback
|
||||
|
||||
# Essential: Invoke the given callback when a multiple files' statuses have
|
||||
# changed. For example, on window focus, the status of all the paths in the
|
||||
# repo is checked. If any of them have changed, this will be fired. Call
|
||||
# {::getPathStatus(path)} to get the status for your path of choice.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeStatuses: (callback) ->
|
||||
@emitter.on 'did-change-statuses', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'status-changed'
|
||||
deprecate 'Use Git::onDidChangeStatus instead'
|
||||
when 'statuses-changed'
|
||||
deprecate 'Use Git::onDidChangeStatuses instead'
|
||||
else
|
||||
deprecate 'Git::on is deprecated. Use event subscription methods instead.'
|
||||
EmitterMixin::on.apply(this, arguments)
|
||||
|
||||
###
|
||||
Section: Instance Methods
|
||||
###
|
||||
|
||||
# Subscribes to buffer events.
|
||||
subscribeToBuffer: (buffer) ->
|
||||
@subscribe buffer, 'saved reloaded path-changed', =>
|
||||
getBufferPathStatus = =>
|
||||
if path = buffer.getPath()
|
||||
@getPathStatus(path)
|
||||
@subscribe buffer, 'destroyed', => @unsubscribe(buffer)
|
||||
|
||||
@subscribe buffer.onDidSave(getBufferPathStatus)
|
||||
@subscribe buffer.onDidReload(getBufferPathStatus)
|
||||
@subscribe buffer.onDidChangePath(getBufferPathStatus)
|
||||
@subscribe buffer.onDidDestroy => @unsubscribe(buffer)
|
||||
|
||||
# Subscribes to editor view event.
|
||||
checkoutHeadForEditor: (editor) ->
|
||||
@@ -165,6 +226,8 @@ class Git
|
||||
delete @statuses[relativePath]
|
||||
if currentPathStatus isnt pathStatus
|
||||
@emit 'status-changed', path, pathStatus
|
||||
@emitter.emit 'did-change-status', {path, pathStatus}
|
||||
|
||||
pathStatus
|
||||
|
||||
# Public: Is the given path ignored?
|
||||
@@ -385,4 +448,6 @@ class Git
|
||||
for submodulePath, submoduleRepo of @getRepo().submodules
|
||||
submoduleRepo.upstream = submodules[submodulePath]?.upstream ? {ahead: 0, behind: 0}
|
||||
|
||||
@emit 'statuses-changed' unless statusesUnchanged
|
||||
unless statusesUnchanged
|
||||
@emit 'statuses-changed'
|
||||
@emitter.emit 'did-change-statuses'
|
||||
|
||||
@@ -21,7 +21,7 @@ GutterComponent = React.createClass
|
||||
if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)'
|
||||
backgroundColor = gutterBackgroundColor
|
||||
|
||||
div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown,
|
||||
div className: 'gutter', onClick: @onClick, onMouseDown: @onMouseDown,
|
||||
div className: 'line-numbers', ref: 'lineNumbers', style:
|
||||
height: Math.max(scrollHeight, scrollViewHeight)
|
||||
WebkitTransform: @getTransform()
|
||||
@@ -215,6 +215,14 @@ GutterComponent = React.createClass
|
||||
lineNumberNodeForScreenRow: (screenRow) ->
|
||||
@lineNumberNodesById[@lineNumberIdsByScreenRow[screenRow]]
|
||||
|
||||
onMouseDown: (event) ->
|
||||
{editor} = @props
|
||||
{target} = event
|
||||
lineNumber = target.parentNode
|
||||
|
||||
unless target.classList.contains('icon-right') and lineNumber.classList.contains('foldable')
|
||||
@props.onMouseDown(event)
|
||||
|
||||
onClick: (event) ->
|
||||
{editor} = @props
|
||||
{target} = event
|
||||
|
||||
@@ -22,11 +22,12 @@ HighlightComponent = React.createClass
|
||||
{editor, decoration} = @props
|
||||
if decoration.id?
|
||||
@decoration = editor.decorationForId(decoration.id)
|
||||
@decoration.on 'flash', @startFlashAnimation
|
||||
@decorationDisposable = @decoration.onDidFlash @startFlashAnimation
|
||||
@startFlashAnimation()
|
||||
|
||||
componentWillUnmount: ->
|
||||
@decoration?.off 'flash', @startFlashAnimation
|
||||
@decorationDisposable?.dispose()
|
||||
@decorationDisposable = null
|
||||
|
||||
startFlashAnimation: ->
|
||||
return unless flash = @decoration.consumeNextFlash()
|
||||
|
||||
@@ -4,9 +4,13 @@ KeymapManager = require 'atom-keymap'
|
||||
CSON = require 'season'
|
||||
{jQuery} = require 'space-pen'
|
||||
|
||||
KeymapManager::onDidLoadBundledKeymaps = (callback) ->
|
||||
@emitter.on 'did-load-bundled-keymaps', callback
|
||||
|
||||
KeymapManager::loadBundledKeymaps = ->
|
||||
@loadKeymap(path.join(@resourcePath, 'keymaps'))
|
||||
@emit('bundled-keymaps-loaded')
|
||||
@emit 'bundled-keymaps-loaded'
|
||||
@emitter.emit 'did-load-bundled-keymaps'
|
||||
|
||||
KeymapManager::getUserKeymapPath = ->
|
||||
if userKeymapPath = CSON.resolve(path.join(@configDirPath, 'keymap'))
|
||||
|
||||
@@ -14,8 +14,8 @@ class MenuManager
|
||||
constructor: ({@resourcePath}) ->
|
||||
@pendingUpdateOperation = null
|
||||
@template = []
|
||||
atom.keymaps.on 'bundled-keymaps-loaded', => @loadPlatformItems()
|
||||
atom.packages.on 'activated', => @sortPackagesMenu()
|
||||
atom.keymaps.onDidLoadBundledKeymaps => @loadPlatformItems()
|
||||
atom.packages.onDidActivateAll => @sortPackagesMenu()
|
||||
|
||||
# Public: Adds the given items to the application menu.
|
||||
#
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter} = require 'emissary'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
fs = require 'fs-plus'
|
||||
Q = require 'q'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
Package = require './package'
|
||||
ThemePackage = require './theme-package'
|
||||
|
||||
# Public: Package manager for coordinating the lifecycle of Atom packages.
|
||||
# Extended: Package manager for coordinating the lifecycle of Atom packages.
|
||||
#
|
||||
# An instance of this class is always available as the `atom.packages` global.
|
||||
#
|
||||
@@ -25,9 +27,10 @@ ThemePackage = require './theme-package'
|
||||
# settings and also by calling `enablePackage()/disablePackage()`.
|
||||
module.exports =
|
||||
class PackageManager
|
||||
Emitter.includeInto(this)
|
||||
EmitterMixin.includeInto(this)
|
||||
|
||||
constructor: ({configDirPath, devMode, safeMode, @resourcePath}) ->
|
||||
@emitter = new Emitter
|
||||
@packageDirPaths = []
|
||||
unless safeMode
|
||||
if devMode
|
||||
@@ -41,6 +44,40 @@ class PackageManager
|
||||
@packageActivators = []
|
||||
@registerPackageActivator(this, ['atom', 'textmate'])
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Essential: Invoke the given callback when all packages have been activated.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidLoadAll: (callback) ->
|
||||
@emitter.on 'did-load-all', callback
|
||||
|
||||
# Essential: Invoke the given callback when all packages have been activated.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidActivateAll: (callback) ->
|
||||
@emitter.on 'did-activate-all', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'loaded'
|
||||
deprecate 'Use PackageManager::onDidLoadAll instead'
|
||||
when 'activated'
|
||||
deprecate 'Use PackageManager::onDidActivateAll instead'
|
||||
else
|
||||
deprecate 'PackageManager::on is deprecated. Use event subscription methods instead.'
|
||||
EmitterMixin::on.apply(this, arguments)
|
||||
|
||||
###
|
||||
Section: Instance Methods
|
||||
###
|
||||
|
||||
# Extended: Get the path to the apm command.
|
||||
#
|
||||
# Return a {String} file path to apm.
|
||||
@@ -83,6 +120,7 @@ class PackageManager
|
||||
packages = @getLoadedPackagesForTypes(types)
|
||||
activator.activatePackages(packages)
|
||||
@emit 'activated'
|
||||
@emitter.emit 'did-activate-all'
|
||||
|
||||
# another type of package manager can handle other package types.
|
||||
# See ThemeManager
|
||||
@@ -159,8 +197,11 @@ class PackageManager
|
||||
packagePaths = _.uniq packagePaths, (packagePath) -> path.basename(packagePath)
|
||||
@loadPackage(packagePath) for packagePath in packagePaths
|
||||
@emit 'loaded'
|
||||
@emitter.emit 'did-load-all'
|
||||
|
||||
loadPackage: (nameOrPath) ->
|
||||
return pack if pack = @getLoadedPackage(nameOrPath)
|
||||
|
||||
if packagePath = @resolvePackagePath(nameOrPath)
|
||||
name = path.basename(nameOrPath)
|
||||
return pack if pack = @getLoadedPackage(name)
|
||||
|
||||
@@ -4,8 +4,10 @@ _ = require 'underscore-plus'
|
||||
async = require 'async'
|
||||
CSON = require 'season'
|
||||
fs = require 'fs-plus'
|
||||
{Emitter} = require 'emissary'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
Q = require 'q'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
$ = null # Defer require in case this is in the window-less browser process
|
||||
ScopedProperties = require './scoped-properties'
|
||||
@@ -14,7 +16,7 @@ ScopedProperties = require './scoped-properties'
|
||||
# stylesheets, keymaps, grammar, editor properties, and menus.
|
||||
module.exports =
|
||||
class Package
|
||||
Emitter.includeInto(this)
|
||||
EmitterMixin.includeInto(this)
|
||||
|
||||
@stylesheetsDir: 'stylesheets'
|
||||
|
||||
@@ -37,11 +39,40 @@ class Package
|
||||
resolvedMainModulePath: false
|
||||
mainModule: null
|
||||
|
||||
###
|
||||
Section: Construction
|
||||
###
|
||||
|
||||
constructor: (@path, @metadata) ->
|
||||
@emitter = new Emitter
|
||||
@metadata ?= Package.loadMetadata(@path)
|
||||
@name = @metadata?.name ? path.basename(@path)
|
||||
@reset()
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Essential: Invoke the given callback when all packages have been activated.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidDeactivate: (callback) ->
|
||||
@emitter.on 'did-deactivate', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'deactivated'
|
||||
deprecate 'Use Package::onDidDeactivate instead'
|
||||
else
|
||||
deprecate 'Package::on is deprecated. Use event subscription methods instead.'
|
||||
EmitterMixin::on.apply(this, arguments)
|
||||
|
||||
###
|
||||
Section: Instance Methods
|
||||
###
|
||||
|
||||
enable: ->
|
||||
atom.config.removeAtKeyPath('core.disabledPackages', @name)
|
||||
|
||||
@@ -242,8 +273,13 @@ class Package
|
||||
@unsubscribeFromActivationEvents()
|
||||
@deactivateResources()
|
||||
@deactivateConfig()
|
||||
@mainModule?.deactivate?() if @mainActivated
|
||||
@emit('deactivated')
|
||||
if @mainActivated
|
||||
try
|
||||
@mainModule?.deactivate?()
|
||||
catch e
|
||||
console.error "Error deactivating package '#{@name}'", e.stack
|
||||
@emit 'deactivated'
|
||||
@emitter.emit 'did-deactivate'
|
||||
|
||||
deactivateConfig: ->
|
||||
@mainModule?.deactivateConfig?()
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
{View} = require './space-pen-extensions'
|
||||
PaneView = null
|
||||
|
||||
module.exports =
|
||||
class PaneAxisView extends View
|
||||
initialize: (@model) ->
|
||||
@onChildAdded(child) for child in @model.children
|
||||
@subscribe @model.children, 'changed', @onChildrenChanged
|
||||
@subscriptions = new CompositeDisposable
|
||||
|
||||
@onChildAdded({child, index}) for child, index in @model.getChildren()
|
||||
|
||||
@subscriptions.add @model.onDidAddChild(@onChildAdded)
|
||||
@subscriptions.add @model.onDidRemoveChild(@onChildRemoved)
|
||||
@subscriptions.add @model.onDidReplaceChild(@onChildReplaced)
|
||||
|
||||
afterAttach: ->
|
||||
@container = @closest('.panes').view()
|
||||
@@ -14,19 +20,22 @@ class PaneAxisView extends View
|
||||
viewClass = model.getViewClass()
|
||||
model._view ?= new viewClass(model)
|
||||
|
||||
onChildrenChanged: ({index, removedValues, insertedValues}) =>
|
||||
onChildReplaced: ({index, oldChild, newChild}) =>
|
||||
focusedElement = document.activeElement if @hasFocus()
|
||||
@onChildRemoved(child, index) for child in removedValues
|
||||
@onChildAdded(child, index + i) for child, i in insertedValues
|
||||
@onChildRemoved({child: oldChild, index})
|
||||
@onChildAdded({child: newChild, index})
|
||||
focusedElement?.focus() if document.activeElement is document.body
|
||||
|
||||
onChildAdded: (child, index) =>
|
||||
onChildAdded: ({child, index}) =>
|
||||
view = @viewForModel(child)
|
||||
@insertAt(index, view)
|
||||
|
||||
onChildRemoved: (child) =>
|
||||
onChildRemoved: ({child}) =>
|
||||
view = @viewForModel(child)
|
||||
view.detach()
|
||||
PaneView ?= require './pane-view'
|
||||
if view instanceof PaneView and view.model.isDestroyed()
|
||||
@container?.trigger 'pane:removed', [view]
|
||||
|
||||
beforeRemove: ->
|
||||
@subscriptions.dispose()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{Model, Sequence} = require 'theorist'
|
||||
{Model} = require 'theorist'
|
||||
{Emitter, CompositeDisposable} = require 'event-kit'
|
||||
{flatten} = require 'underscore-plus'
|
||||
Serializable = require 'serializable'
|
||||
|
||||
@@ -10,18 +11,17 @@ class PaneAxis extends Model
|
||||
atom.deserializers.add(this)
|
||||
Serializable.includeInto(this)
|
||||
|
||||
parent: null
|
||||
container: null
|
||||
orientation: null
|
||||
|
||||
constructor: ({@container, @orientation, children}) ->
|
||||
@children = Sequence.fromArray(children ? [])
|
||||
|
||||
@subscribe @children.onEach (child) =>
|
||||
child.parent = this
|
||||
child.container = @container
|
||||
@subscribe child, 'destroyed', => @removeChild(child)
|
||||
|
||||
@subscribe @children.onRemoval (child) => @unsubscribe(child)
|
||||
|
||||
@when @children.$length.becomesLessThan(2), 'reparentLastChild'
|
||||
@when @children.$length.becomesLessThan(1), 'destroy'
|
||||
@emitter = new Emitter
|
||||
@subscriptionsByChild = new WeakMap
|
||||
@subscriptions = new CompositeDisposable
|
||||
@children = []
|
||||
if children?
|
||||
@addChild(child) for child in children
|
||||
|
||||
deserializeParams: (params) ->
|
||||
{container} = params
|
||||
@@ -32,35 +32,93 @@ class PaneAxis extends Model
|
||||
children: @children.map (child) -> child.serialize()
|
||||
orientation: @orientation
|
||||
|
||||
getParent: -> @parent
|
||||
|
||||
setParent: (@parent) -> @parent
|
||||
|
||||
getContainer: -> @container
|
||||
|
||||
setContainer: (@container) -> @container
|
||||
|
||||
getViewClass: ->
|
||||
if @orientation is 'vertical'
|
||||
PaneColumnView ?= require './pane-column-view'
|
||||
else
|
||||
PaneRowView ?= require './pane-row-view'
|
||||
|
||||
getChildren: -> @children.slice()
|
||||
|
||||
getPanes: ->
|
||||
flatten(@children.map (child) -> child.getPanes())
|
||||
|
||||
addChild: (child, index=@children.length) ->
|
||||
@children.splice(index, 0, child)
|
||||
getItems: ->
|
||||
flatten(@children.map (child) -> child.getItems())
|
||||
|
||||
removeChild: (child) ->
|
||||
onDidAddChild: (fn) ->
|
||||
@emitter.on 'did-add-child', fn
|
||||
|
||||
onDidRemoveChild: (fn) ->
|
||||
@emitter.on 'did-remove-child', fn
|
||||
|
||||
onDidReplaceChild: (fn) ->
|
||||
@emitter.on 'did-replace-child', fn
|
||||
|
||||
onDidDestroy: (fn) ->
|
||||
@emitter.on 'did-destroy', fn
|
||||
|
||||
addChild: (child, index=@children.length) ->
|
||||
child.setParent(this)
|
||||
child.setContainer(@container)
|
||||
|
||||
@subscribeToChild(child)
|
||||
|
||||
@children.splice(index, 0, child)
|
||||
@emitter.emit 'did-add-child', {child, index}
|
||||
|
||||
removeChild: (child, replacing=false) ->
|
||||
index = @children.indexOf(child)
|
||||
throw new Error("Removing non-existent child") if index is -1
|
||||
|
||||
@unsubscribeFromChild(child)
|
||||
|
||||
@children.splice(index, 1)
|
||||
@emitter.emit 'did-remove-child', {child, index}
|
||||
@reparentLastChild() if not replacing and @children.length < 2
|
||||
|
||||
replaceChild: (oldChild, newChild) ->
|
||||
@unsubscribeFromChild(oldChild)
|
||||
@subscribeToChild(newChild)
|
||||
|
||||
newChild.setParent(this)
|
||||
newChild.setContainer(@container)
|
||||
|
||||
index = @children.indexOf(oldChild)
|
||||
throw new Error("Replacing non-existent child") if index is -1
|
||||
@children.splice(index, 1, newChild)
|
||||
@emitter.emit 'did-replace-child', {oldChild, newChild, index}
|
||||
|
||||
insertChildBefore: (currentChild, newChild) ->
|
||||
index = @children.indexOf(currentChild)
|
||||
@children.splice(index, 0, newChild)
|
||||
@addChild(newChild, index)
|
||||
|
||||
insertChildAfter: (currentChild, newChild) ->
|
||||
index = @children.indexOf(currentChild)
|
||||
@children.splice(index + 1, 0, newChild)
|
||||
@addChild(newChild, index + 1)
|
||||
|
||||
reparentLastChild: ->
|
||||
@parent.replaceChild(this, @children[0])
|
||||
@destroy()
|
||||
|
||||
subscribeToChild: (child) ->
|
||||
subscription = child.onDidDestroy => @removeChild(child)
|
||||
@subscriptionsByChild.set(child, subscription)
|
||||
@subscriptions.add(subscription)
|
||||
|
||||
unsubscribeFromChild: (child) ->
|
||||
subscription = @subscriptionsByChild.get(child)
|
||||
@subscriptions.remove(subscription)
|
||||
subscription.dispose()
|
||||
|
||||
destroyed: ->
|
||||
@subscriptions.dispose()
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{deprecate} = require 'grim'
|
||||
Delegator = require 'delegato'
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
{$, View} = require './space-pen-extensions'
|
||||
PaneView = require './pane-view'
|
||||
PaneContainer = require './pane-container'
|
||||
@@ -15,13 +16,15 @@ class PaneContainerView extends View
|
||||
@div class: 'panes'
|
||||
|
||||
initialize: (params) ->
|
||||
@subscriptions = new CompositeDisposable
|
||||
|
||||
if params instanceof PaneContainer
|
||||
@model = params
|
||||
else
|
||||
@model = new PaneContainer({root: params?.root?.model})
|
||||
|
||||
@subscribe @model.$root, @onRootChanged
|
||||
@subscribe @model.$activePaneItem.changes, @onActivePaneItemChanged
|
||||
@subscriptions.add @model.observeRoot(@onRootChanged)
|
||||
@subscriptions.add @model.onDidChangeActivePaneItem(@onActivePaneItemChanged)
|
||||
|
||||
viewForModel: (model) ->
|
||||
if model?
|
||||
@@ -88,7 +91,7 @@ class PaneContainerView extends View
|
||||
@viewForModel(@model.activePane)
|
||||
|
||||
getActivePaneItem: ->
|
||||
@model.activePaneItem
|
||||
@model.getActivePaneItem()
|
||||
|
||||
getActiveView: ->
|
||||
@getActivePaneView()?.activeView
|
||||
@@ -153,3 +156,6 @@ class PaneContainerView extends View
|
||||
getPanes: ->
|
||||
deprecate("Use PaneContainerView::getPaneViews() instead")
|
||||
@getPaneViews()
|
||||
|
||||
beforeRemove: ->
|
||||
@subscriptions.dispose()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{find} = require 'underscore-plus'
|
||||
{find, flatten} = require 'underscore-plus'
|
||||
{Model} = require 'theorist'
|
||||
{Emitter, CompositeDisposable} = require 'event-kit'
|
||||
Serializable = require 'serializable'
|
||||
Pane = require './pane'
|
||||
|
||||
@@ -11,10 +12,9 @@ class PaneContainer extends Model
|
||||
@version: 1
|
||||
|
||||
@properties
|
||||
root: -> new Pane
|
||||
activePane: null
|
||||
|
||||
previousRoot: null
|
||||
root: null
|
||||
|
||||
@behavior 'activePaneItem', ->
|
||||
@$activePane
|
||||
@@ -23,9 +23,16 @@ class PaneContainer extends Model
|
||||
|
||||
constructor: (params) ->
|
||||
super
|
||||
@subscribe @$root, @onRootChanged
|
||||
|
||||
@emitter = new Emitter
|
||||
@subscriptions = new CompositeDisposable
|
||||
|
||||
@setRoot(params?.root ? new Pane)
|
||||
@destroyEmptyPanes() if params?.destroyEmptyPanes
|
||||
|
||||
@monitorActivePaneItem()
|
||||
@monitorPaneItems()
|
||||
|
||||
deserializeParams: (params) ->
|
||||
params.root = atom.deserializers.deserialize(params.root, container: this)
|
||||
params.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes')
|
||||
@@ -36,16 +43,75 @@ class PaneContainer extends Model
|
||||
root: @root?.serialize()
|
||||
activePaneId: @activePane.id
|
||||
|
||||
onDidChangeRoot: (fn) ->
|
||||
@emitter.on 'did-change-root', fn
|
||||
|
||||
observeRoot: (fn) ->
|
||||
fn(@getRoot())
|
||||
@onDidChangeRoot(fn)
|
||||
|
||||
onDidAddPane: (fn) ->
|
||||
@emitter.on 'did-add-pane', fn
|
||||
|
||||
observePanes: (fn) ->
|
||||
fn(pane) for pane in @getPanes()
|
||||
@onDidAddPane ({pane}) -> fn(pane)
|
||||
|
||||
onDidChangeActivePane: (fn) ->
|
||||
@emitter.on 'did-change-active-pane', fn
|
||||
|
||||
observeActivePane: (fn) ->
|
||||
fn(@getActivePane())
|
||||
@onDidChangeActivePane(fn)
|
||||
|
||||
onDidAddPaneItem: (fn) ->
|
||||
@emitter.on 'did-add-pane-item', fn
|
||||
|
||||
observePaneItems: (fn) ->
|
||||
fn(item) for item in @getPaneItems()
|
||||
@onDidAddPaneItem ({item}) -> fn(item)
|
||||
|
||||
onDidChangeActivePaneItem: (fn) ->
|
||||
@emitter.on 'did-change-active-pane-item', fn
|
||||
|
||||
observeActivePaneItem: (fn) ->
|
||||
fn(@getActivePaneItem())
|
||||
@onDidChangeActivePaneItem(fn)
|
||||
|
||||
onDidDestroyPaneItem: (fn) ->
|
||||
@emitter.on 'did-destroy-pane-item', fn
|
||||
|
||||
getRoot: -> @root
|
||||
|
||||
setRoot: (@root) ->
|
||||
@root.setParent(this)
|
||||
@root.setContainer(this)
|
||||
@emitter.emit 'did-change-root', @root
|
||||
if not @getActivePane()? and @root instanceof Pane
|
||||
@setActivePane(@root)
|
||||
|
||||
replaceChild: (oldChild, newChild) ->
|
||||
throw new Error("Replacing non-existent child") if oldChild isnt @root
|
||||
@root = newChild
|
||||
@setRoot(newChild)
|
||||
|
||||
getPanes: ->
|
||||
@root?.getPanes() ? []
|
||||
@getRoot().getPanes()
|
||||
|
||||
getPaneItems: ->
|
||||
@getRoot().getItems()
|
||||
|
||||
getActivePane: ->
|
||||
@activePane
|
||||
|
||||
setActivePane: (activePane) ->
|
||||
if activePane isnt @activePane
|
||||
@activePane = activePane
|
||||
@emitter.emit 'did-change-active-pane', @activePane
|
||||
@activePane
|
||||
|
||||
getActivePaneItem: ->
|
||||
@getActivePane().getActiveItem()
|
||||
|
||||
paneForUri: (uri) ->
|
||||
find @getPanes(), (pane) -> pane.itemForUri(uri)?
|
||||
|
||||
@@ -73,26 +139,37 @@ class PaneContainer extends Model
|
||||
else
|
||||
false
|
||||
|
||||
onRootChanged: (root) =>
|
||||
@unsubscribe(@previousRoot) if @previousRoot?
|
||||
@previousRoot = root
|
||||
|
||||
unless root?
|
||||
@activePane = null
|
||||
return
|
||||
|
||||
root.parent = this
|
||||
root.container = this
|
||||
|
||||
|
||||
@activePane ?= root if root instanceof Pane
|
||||
|
||||
destroyEmptyPanes: ->
|
||||
pane.destroy() for pane in @getPanes() when pane.items.length is 0
|
||||
|
||||
itemDestroyed: (item) ->
|
||||
@emit 'item-destroyed', item
|
||||
paneItemDestroyed: (item) ->
|
||||
@emitter.emit 'did-destroy-pane-item', item
|
||||
|
||||
didAddPane: (pane) ->
|
||||
@emitter.emit 'did-add-pane', pane
|
||||
|
||||
# Called by Model superclass when destroyed
|
||||
destroyed: ->
|
||||
pane.destroy() for pane in @getPanes()
|
||||
@subscriptions.dispose()
|
||||
@emitter.dispose()
|
||||
|
||||
monitorActivePaneItem: ->
|
||||
childSubscription = null
|
||||
@subscriptions.add @observeActivePane (activePane) =>
|
||||
if childSubscription?
|
||||
@subscriptions.remove(childSubscription)
|
||||
childSubscription.dispose()
|
||||
|
||||
childSubscription = activePane.observeActiveItem (activeItem) =>
|
||||
@emitter.emit 'did-change-active-pane-item', activeItem
|
||||
|
||||
@subscriptions.add(childSubscription)
|
||||
|
||||
monitorPaneItems: ->
|
||||
@subscriptions.add @observePanes (pane) =>
|
||||
for item, index in pane.getItems()
|
||||
@emitter.emit 'did-add-pane-item', {item, pane, index}
|
||||
|
||||
pane.onDidAddItem ({item, index}) =>
|
||||
@emitter.emit 'did-add-pane-item', {item, pane, index}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{$, View} = require './space-pen-extensions'
|
||||
Delegator = require 'delegato'
|
||||
{deprecate} = require 'grim'
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
PropertyAccessors = require 'property-accessors'
|
||||
|
||||
Pane = require './pane'
|
||||
@@ -33,6 +34,8 @@ class PaneView extends View
|
||||
previousActiveItem: null
|
||||
|
||||
initialize: (args...) ->
|
||||
@subscriptions = new CompositeDisposable
|
||||
|
||||
if args[0] instanceof Pane
|
||||
@model = args[0]
|
||||
else
|
||||
@@ -44,13 +47,13 @@ class PaneView extends View
|
||||
@handleEvents()
|
||||
|
||||
handleEvents: ->
|
||||
@subscribe @model.$activeItem, @onActiveItemChanged
|
||||
@subscribe @model, 'item-added', @onItemAdded
|
||||
@subscribe @model, 'item-removed', @onItemRemoved
|
||||
@subscribe @model, 'item-moved', @onItemMoved
|
||||
@subscribe @model, 'before-item-destroyed', @onBeforeItemDestroyed
|
||||
@subscribe @model, 'activated', @onActivated
|
||||
@subscribe @model.$active, @onActiveStatusChanged
|
||||
@subscriptions.add @model.observeActiveItem(@onActiveItemChanged)
|
||||
@subscriptions.add @model.onDidAddItem(@onItemAdded)
|
||||
@subscriptions.add @model.onDidRemoveItem(@onItemRemoved)
|
||||
@subscriptions.add @model.onDidMoveItem(@onItemMoved)
|
||||
@subscriptions.add @model.onWillDestroyItem(@onBeforeItemDestroyed)
|
||||
@subscriptions.add @model.onDidActivate(@onActivated)
|
||||
@subscriptions.add @model.observeActive(@onActiveStatusChanged)
|
||||
|
||||
@subscribe this, 'focusin', => @model.focus()
|
||||
@subscribe this, 'focusout', => @model.blur()
|
||||
@@ -72,15 +75,18 @@ class PaneView extends View
|
||||
@command 'pane:show-item-8', => @activateItemAtIndex(7)
|
||||
@command 'pane:show-item-9', => @activateItemAtIndex(8)
|
||||
|
||||
@command 'pane:split-left', => @splitLeft(@copyActiveItem())
|
||||
@command 'pane:split-right', => @splitRight(@copyActiveItem())
|
||||
@command 'pane:split-up', => @splitUp(@copyActiveItem())
|
||||
@command 'pane:split-down', => @splitDown(@copyActiveItem())
|
||||
@command 'pane:split-left', => @model.splitLeft(copyActiveItem: true)
|
||||
@command 'pane:split-right', => @model.splitRight(copyActiveItem: true)
|
||||
@command 'pane:split-up', => @model.splitUp(copyActiveItem: true)
|
||||
@command 'pane:split-down', => @model.splitDown(copyActiveItem: true)
|
||||
@command 'pane:close', =>
|
||||
@model.destroyItems()
|
||||
@model.destroy()
|
||||
@command 'pane:close-other-items', => @destroyInactiveItems()
|
||||
|
||||
# Essential: Returns the {Pane} model underlying this pane view
|
||||
getModel: -> @model
|
||||
|
||||
# Deprecated: Use ::destroyItem
|
||||
removeItem: (item) ->
|
||||
deprecate("Use PaneView::destroyItem instead")
|
||||
@@ -141,6 +147,9 @@ class PaneView extends View
|
||||
@activeItem
|
||||
|
||||
onActiveItemChanged: (item) =>
|
||||
@activeItemDisposables.dispose() if @activeItemDisposables?
|
||||
@activeItemDisposables = new CompositeDisposable()
|
||||
|
||||
if @previousActiveItem?.off?
|
||||
@previousActiveItem.off 'title-changed', @activeItemTitleChanged
|
||||
@previousActiveItem.off 'modified-status-changed', @activeItemModifiedChanged
|
||||
@@ -148,22 +157,36 @@ class PaneView extends View
|
||||
|
||||
return unless item?
|
||||
|
||||
hasFocus = @hasFocus()
|
||||
if item.on?
|
||||
item.on 'title-changed', @activeItemTitleChanged
|
||||
item.on 'modified-status-changed', @activeItemModifiedChanged
|
||||
if item.onDidChangeTitle?
|
||||
disposable = item.onDidChangeTitle(@activeItemTitleChanged)
|
||||
deprecate 'Please return a Disposable object from your ::onDidChangeTitle method!' unless disposable?.dispose?
|
||||
@activeItemDisposables.add(disposable) if disposable?.dispose?
|
||||
else if item.on?
|
||||
deprecate '::on methods for items are no longer supported. If you would like your item to title change behavior, please implement a ::onDidChangeTitle() method.'
|
||||
disposable = item.on('title-changed', @activeItemTitleChanged)
|
||||
@activeItemDisposables.add(disposable) if disposable?.dispose?
|
||||
|
||||
if item.onDidChangeModified?
|
||||
disposable = item.onDidChangeModified(@activeItemModifiedChanged)
|
||||
deprecate 'Please return a Disposable object from your ::onDidChangeModified method!' unless disposable?.dispose?
|
||||
@activeItemDisposables.add(disposable) if disposable?.dispose?
|
||||
else if item.on?
|
||||
deprecate '::on methods for items are no longer supported. If you would like your item to support modified behavior, please implement a ::onDidChangeModified() method.'
|
||||
item.on('modified-status-changed', @activeItemModifiedChanged)
|
||||
@activeItemDisposables.add(disposable) if disposable?.dispose?
|
||||
|
||||
view = @viewForItem(item)
|
||||
otherView.hide() for otherView in @itemViews.children().not(view).views()
|
||||
@itemViews.append(view) unless view.parent().is(@itemViews)
|
||||
view.show() if @attached
|
||||
view.focus() if hasFocus
|
||||
view.focus() if @hasFocus()
|
||||
|
||||
@trigger 'pane:active-item-changed', [item]
|
||||
|
||||
onItemAdded: (item, index) =>
|
||||
onItemAdded: ({item, index}) =>
|
||||
@trigger 'pane:item-added', [item, index]
|
||||
|
||||
onItemRemoved: (item, index, destroyed) =>
|
||||
onItemRemoved: ({item, index, destroyed}) =>
|
||||
if item instanceof $
|
||||
viewToRemove = item
|
||||
else if viewToRemove = @viewsByItem.get(item)
|
||||
@@ -177,7 +200,7 @@ class PaneView extends View
|
||||
|
||||
@trigger 'pane:item-removed', [item, index]
|
||||
|
||||
onItemMoved: (item, newIndex) =>
|
||||
onItemMoved: ({item, newIndex}) =>
|
||||
@trigger 'pane:item-moved', [item, newIndex]
|
||||
|
||||
onBeforeItemDestroyed: (item) =>
|
||||
@@ -219,6 +242,7 @@ class PaneView extends View
|
||||
@closest('.panes').view()
|
||||
|
||||
beforeRemove: ->
|
||||
@subscriptions.dispose()
|
||||
@model.destroy() unless @model.isDestroyed()
|
||||
|
||||
remove: (selector, keepData) ->
|
||||
|
||||
454
src/pane.coffee
454
src/pane.coffee
@@ -1,47 +1,16 @@
|
||||
{find, compact, extend, last} = require 'underscore-plus'
|
||||
{Model, Sequence} = require 'theorist'
|
||||
{Model} = require 'theorist'
|
||||
{Emitter} = require 'event-kit'
|
||||
Serializable = require 'serializable'
|
||||
Grim = require 'grim'
|
||||
PaneAxis = require './pane-axis'
|
||||
Editor = require './editor'
|
||||
PaneView = null
|
||||
|
||||
# Extended: A container for multiple items, one of which is *active* at a given
|
||||
# time. With the default packages, a tab is displayed for each item and the
|
||||
# active item's view is displayed.
|
||||
#
|
||||
# ## Events
|
||||
# ### activated
|
||||
#
|
||||
# Extended: Emit when this pane as been activated
|
||||
#
|
||||
# ### item-added
|
||||
#
|
||||
# Extended: Emit when an item was added to the pane
|
||||
#
|
||||
# * `item` The pane item that has been added
|
||||
# * `index` {Number} Index in the pane
|
||||
#
|
||||
# ### before-item-destroyed
|
||||
#
|
||||
# Extended: Emit before the item is destroyed
|
||||
#
|
||||
# * `item` The pane item that will be destoryed
|
||||
#
|
||||
# ### item-removed
|
||||
#
|
||||
# Extended: Emit when the item was removed from the pane
|
||||
#
|
||||
# * `item` The pane item that was removed
|
||||
# * `index` {Number} Index in the pane
|
||||
# * `destroying` {Boolean} `true` when the item is being removed because of destruction
|
||||
#
|
||||
# ### item-moved
|
||||
#
|
||||
# Extended: Emit when an item was moved within the pane
|
||||
#
|
||||
# * `item` The pane item that was moved
|
||||
# * `newIndex` {Number} Index that the item was moved to
|
||||
#
|
||||
# Extended: A container for presenting content in the center of the workspace.
|
||||
# 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.
|
||||
module.exports =
|
||||
class Pane extends Model
|
||||
atom.deserializers.add(this)
|
||||
@@ -64,15 +33,11 @@ class Pane extends Model
|
||||
constructor: (params) ->
|
||||
super
|
||||
|
||||
@items = Sequence.fromArray(compact(params?.items ? []))
|
||||
@activeItem ?= @items[0]
|
||||
@emitter = new Emitter
|
||||
@items = []
|
||||
|
||||
@subscribe @items.onEach (item) =>
|
||||
if typeof item.on is 'function'
|
||||
@subscribe item, 'destroyed', => @removeItem(item, true)
|
||||
|
||||
@subscribe @items.onRemoval (item, index) =>
|
||||
@unsubscribe item if typeof item.on is 'function'
|
||||
@addItems(compact(params?.items ? []))
|
||||
@setActiveItem(@items[0]) unless @getActiveItem()?
|
||||
|
||||
# Called by the Serializable mixin during serialization.
|
||||
serializeParams: ->
|
||||
@@ -91,7 +56,174 @@ class Pane extends Model
|
||||
# Called by the view layer to construct a view for this model.
|
||||
getViewClass: -> PaneView ?= require './pane-view'
|
||||
|
||||
isActive: -> @active
|
||||
getParent: -> @parent
|
||||
|
||||
setParent: (@parent) -> @parent
|
||||
|
||||
getContainer: -> @container
|
||||
|
||||
setContainer: (container) ->
|
||||
container.didAddPane({pane: this}) unless container is @container
|
||||
@container = container
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Public: Invoke the given callback when the pane is activated.
|
||||
#
|
||||
# The given callback will be invoked whenever {::activate} is called on the
|
||||
# pane, even if it is already active at the time.
|
||||
#
|
||||
# * `callback` {Function} to be called when the pane is activated.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidActivate: (callback) ->
|
||||
@emitter.on 'did-activate', callback
|
||||
|
||||
# Public: Invoke the given callback when the pane is destroyed.
|
||||
#
|
||||
# * `callback` {Function} to be called when the pane is destroyed.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidDestroy: (callback) ->
|
||||
@emitter.on 'did-destroy', callback
|
||||
|
||||
# Public: Invoke the given callback when the value of the {::isActive}
|
||||
# property changes.
|
||||
#
|
||||
# * `callback` {Function} to be called when the value of the {::isActive}
|
||||
# property changes.
|
||||
# * `active` {Boolean} indicating whether the pane is active.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeActive: (callback) ->
|
||||
@container.onDidChangeActivePane (activePane) =>
|
||||
callback(this is activePane)
|
||||
|
||||
# Public: Invoke the given callback with the current and future values of the
|
||||
# {::isActive} property.
|
||||
#
|
||||
# * `callback` {Function} to be called with the current and future values of
|
||||
# the {::isActive} property.
|
||||
# * `active` {Boolean} indicating whether the pane is active.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observeActive: (callback) ->
|
||||
callback(@isActive())
|
||||
@onDidChangeActive(callback)
|
||||
|
||||
# Public: Invoke the given callback when an item is added to the pane.
|
||||
#
|
||||
# * `callback` {Function} to be called with when items are added.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `item` The added pane item.
|
||||
# * `index` {Number} indicating where the item is located.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidAddItem: (callback) ->
|
||||
@emitter.on 'did-add-item', callback
|
||||
|
||||
# Public: Invoke the given callback when an item is removed from the pane.
|
||||
#
|
||||
# * `callback` {Function} to be called with when items are removed.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `item` The removed pane item.
|
||||
# * `index` {Number} indicating where the item was located.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidRemoveItem: (callback) ->
|
||||
@emitter.on 'did-remove-item', callback
|
||||
|
||||
# Public: Invoke the given callback when an item is moved within the pane.
|
||||
#
|
||||
# * `callback` {Function} to be called with when items are moved.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `item` The removed pane item.
|
||||
# * `oldIndex` {Number} indicating where the item was located.
|
||||
# * `newIndex` {Number} indicating where the item is now located.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidMoveItem: (callback) ->
|
||||
@emitter.on 'did-move-item', callback
|
||||
|
||||
# Public: Invoke the given callback with all current and future items.
|
||||
#
|
||||
# * `callback` {Function} to be called with current and future items.
|
||||
# * `item` An item that is present in {::getItems} at the time of
|
||||
# subscription or that is added at some later time.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observeItems: (callback) ->
|
||||
callback(item) for item in @getItems()
|
||||
@onDidAddItem ({item}) -> callback(item)
|
||||
|
||||
# Public: Invoke the given callback when the value of {::getActiveItem}
|
||||
# changes.
|
||||
#
|
||||
# * `callback` {Function} to be called with when the active item changes.
|
||||
# * `activeItem` The current active item.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeActiveItem: (callback) ->
|
||||
@emitter.on 'did-change-active-item', callback
|
||||
|
||||
# Public: Invoke the given callback with the current and future values of
|
||||
# {::getActiveItem}.
|
||||
#
|
||||
# * `callback` {Function} to be called with the current and future active
|
||||
# items.
|
||||
# * `activeItem` The current active item.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observeActiveItem: (callback) ->
|
||||
callback(@getActiveItem())
|
||||
@onDidChangeActiveItem(callback)
|
||||
|
||||
# Public: Invoke the given callback before items are destroyed.
|
||||
#
|
||||
# * `callback` {Function} to be called before items are destroyed.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `item` The item that will be destroyed.
|
||||
# * `index` The location of the item.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to
|
||||
# unsubscribe.
|
||||
onWillDestroyItem: (callback) ->
|
||||
@emitter.on 'will-destroy-item', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'activated'
|
||||
Grim.deprecate("Use Pane::onDidActivate instead")
|
||||
when 'destroyed'
|
||||
Grim.deprecate("Use Pane::onDidDestroy instead")
|
||||
when 'item-added'
|
||||
Grim.deprecate("Use Pane::onDidAddItem instead")
|
||||
when 'item-removed'
|
||||
Grim.deprecate("Use Pane::onDidRemoveItem instead")
|
||||
when 'item-moved'
|
||||
Grim.deprecate("Use Pane::onDidMoveItem instead")
|
||||
when 'before-item-destroyed'
|
||||
Grim.deprecate("Use Pane::onWillDestroyItem instead")
|
||||
else
|
||||
Grim.deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.")
|
||||
super
|
||||
|
||||
behavior: (behaviorName) ->
|
||||
switch behaviorName
|
||||
when 'active'
|
||||
Grim.deprecate("The $active behavior property is deprecated. Use ::observeActive or ::onDidChangeActive instead.")
|
||||
when 'container'
|
||||
Grim.deprecate("The $container behavior property is deprecated.")
|
||||
when 'activeItem'
|
||||
Grim.deprecate("The $activeItem behavior property is deprecated. Use ::observeActiveItem or ::onDidChangeActiveItem instead.")
|
||||
when 'focused'
|
||||
Grim.deprecate("The $focused behavior property is deprecated.")
|
||||
else
|
||||
Grim.deprecate("Pane::behavior is deprecated. Use event subscription methods instead.")
|
||||
|
||||
super
|
||||
|
||||
# Called by the view layer to indicate that the pane has gained focus.
|
||||
focus: ->
|
||||
@@ -103,14 +235,12 @@ class Pane extends Model
|
||||
@focused = false
|
||||
true # if this is called from an event handler, don't cancel it
|
||||
|
||||
# Public: Makes this pane the *active* pane, causing it to gain focus
|
||||
# immediately.
|
||||
activate: ->
|
||||
@container?.activePane = this
|
||||
@emit 'activated'
|
||||
|
||||
getPanes: -> [this]
|
||||
|
||||
###
|
||||
Section: Items
|
||||
###
|
||||
|
||||
# Public: Get the items in this pane.
|
||||
#
|
||||
# Returns an {Array} of items.
|
||||
@@ -120,15 +250,23 @@ class Pane extends Model
|
||||
# Public: Get the active pane item in this pane.
|
||||
#
|
||||
# Returns a pane item.
|
||||
getActiveItem: ->
|
||||
getActiveItem: -> @activeItem
|
||||
|
||||
setActiveItem: (activeItem) ->
|
||||
unless activeItem is @activeItem
|
||||
@activeItem = activeItem
|
||||
@emitter.emit 'did-change-active-item', @activeItem
|
||||
@activeItem
|
||||
|
||||
# Public: Returns an {Editor} if the pane item is an {Editor}, or null
|
||||
# otherwise.
|
||||
# Return an {Editor} if the pane item is an {Editor}, or null otherwise.
|
||||
getActiveEditor: ->
|
||||
@activeItem if @activeItem instanceof Editor
|
||||
|
||||
# Public: Returns the item at the specified index.
|
||||
# Public: Return the item at the given index.
|
||||
#
|
||||
# * `index` {Number}
|
||||
#
|
||||
# Returns an item or `null` if no item exists at the given index.
|
||||
itemAtIndex: (index) ->
|
||||
@items[index]
|
||||
|
||||
@@ -148,86 +286,115 @@ class Pane extends Model
|
||||
else
|
||||
@activateItemAtIndex(@items.length - 1)
|
||||
|
||||
# Returns the index of the current active item.
|
||||
# Public: Get the index of the active item.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getActiveItemIndex: ->
|
||||
@items.indexOf(@activeItem)
|
||||
|
||||
# Makes the item at the given index active.
|
||||
# Public: Activate the item at the given index.
|
||||
#
|
||||
# * `index` {Number}
|
||||
activateItemAtIndex: (index) ->
|
||||
@activateItem(@itemAtIndex(index))
|
||||
|
||||
# Makes the given item active, adding the item if necessary.
|
||||
# Public: Make the given item *active*, causing it to be displayed by
|
||||
# the pane's view.
|
||||
activateItem: (item) ->
|
||||
if item?
|
||||
@addItem(item)
|
||||
@activeItem = item
|
||||
@setActiveItem(item)
|
||||
|
||||
# Public: Adds the item to the pane.
|
||||
# Public: Add the given item to the pane.
|
||||
#
|
||||
# * `item` The item to add. It can be a model with an associated view or a view.
|
||||
# * `index` (optional) {Number} at which to add the item. If omitted, the item is
|
||||
# added after the current active item.
|
||||
# * `item` The item to add. It can be a model with an associated view or a
|
||||
# view.
|
||||
# * `index` (optional) {Number} indicating the index at which to add the item.
|
||||
# If omitted, the item is added after the current active item.
|
||||
#
|
||||
# Returns the added item
|
||||
# Returns the added item.
|
||||
addItem: (item, index=@getActiveItemIndex() + 1) ->
|
||||
return if item in @items
|
||||
|
||||
if typeof item.on is 'function'
|
||||
@subscribe item, 'destroyed', => @removeItem(item, true)
|
||||
|
||||
@items.splice(index, 0, item)
|
||||
@emit 'item-added', item, index
|
||||
@activeItem ?= item
|
||||
@emitter.emit 'did-add-item', {item, index}
|
||||
@setActiveItem(item) unless @getActiveItem()?
|
||||
item
|
||||
|
||||
# Public: Adds the given items to the pane.
|
||||
# Public: Add the given items to the pane.
|
||||
#
|
||||
# * `items` An {Array} of items to add. Items can be models with associated
|
||||
# views or views. Any items that are already present in items will
|
||||
# not be added.
|
||||
# * `index` (optional) {Number} index at which to add the item. If omitted, the item is
|
||||
# added after the current active item.
|
||||
# * `items` An {Array} of items to add. Items can be views or models with
|
||||
# associated views. Any objects that are already present in the pane's
|
||||
# current items will not be added again.
|
||||
# * `index` (optional) {Number} index at which to add the items. If omitted,
|
||||
# the item is # added after the current active item.
|
||||
#
|
||||
# Returns an {Array} of the added items
|
||||
# Returns an {Array} of added items.
|
||||
addItems: (items, index=@getActiveItemIndex() + 1) ->
|
||||
items = items.filter (item) => not (item in @items)
|
||||
@addItem(item, index + i) for item, i in items
|
||||
items
|
||||
|
||||
removeItem: (item, destroying) ->
|
||||
removeItem: (item, destroyed=false) ->
|
||||
index = @items.indexOf(item)
|
||||
return if index is -1
|
||||
|
||||
if typeof item.on is 'function'
|
||||
@unsubscribe item
|
||||
|
||||
if item is @activeItem
|
||||
if @items.length is 1
|
||||
@activeItem = undefined
|
||||
@setActiveItem(undefined)
|
||||
else if index is 0
|
||||
@activateNextItem()
|
||||
else
|
||||
@activatePreviousItem()
|
||||
@items.splice(index, 1)
|
||||
@emit 'item-removed', item, index, destroying
|
||||
@container?.itemDestroyed(item) if destroying
|
||||
@emit 'item-removed', item, index, destroyed
|
||||
@emitter.emit 'did-remove-item', {item, index, destroyed}
|
||||
@container?.paneItemDestroyed(item) if destroyed
|
||||
@destroy() if @items.length is 0 and atom.config.get('core.destroyEmptyPanes')
|
||||
|
||||
# Public: Moves the given item to the specified index.
|
||||
# Public: Move the given item to the given index.
|
||||
#
|
||||
# * `item` The item to move.
|
||||
# * `index` {Number} indicating the index to which to move the item.
|
||||
moveItem: (item, newIndex) ->
|
||||
oldIndex = @items.indexOf(item)
|
||||
@items.splice(oldIndex, 1)
|
||||
@items.splice(newIndex, 0, item)
|
||||
@emit 'item-moved', item, newIndex
|
||||
@emitter.emit 'did-move-item', {item, oldIndex, newIndex}
|
||||
|
||||
# Public: Moves the given item to the given index at another pane.
|
||||
# Public: Move the given item to the given index on another pane.
|
||||
#
|
||||
# * `item` The item to move.
|
||||
# * `pane` {Pane} to which to move the item.
|
||||
# * `index` {Number} indicating the index to which to move the item in the
|
||||
# given pane.
|
||||
moveItemToPane: (item, pane, index) ->
|
||||
pane.addItem(item, index)
|
||||
@removeItem(item)
|
||||
|
||||
# Public: Destroys the currently active item and make the next item active.
|
||||
# Public: Destroy the active item and activate the next item.
|
||||
destroyActiveItem: ->
|
||||
@destroyItem(@activeItem)
|
||||
false
|
||||
|
||||
# Public: Destroys the given item. If it is the active item, activate the next
|
||||
# one. If this is the last item, also destroys the pane.
|
||||
# Public: Destroy the given item.
|
||||
#
|
||||
# If the item is active, the next item will be activated. If the item is the
|
||||
# last item, the pane will be destroyed if the `core.destroyEmptyPanes` config
|
||||
# setting is `true`.
|
||||
destroyItem: (item) ->
|
||||
if item?
|
||||
index = @items.indexOf(item)
|
||||
if index isnt -1
|
||||
@emit 'before-item-destroyed', item
|
||||
@emitter.emit 'will-destroy-item', {item, index}
|
||||
if @promptToSaveItem(item)
|
||||
@removeItem(item, true)
|
||||
item.destroy?()
|
||||
@@ -235,27 +402,14 @@ class Pane extends Model
|
||||
else
|
||||
false
|
||||
|
||||
# Public: Destroys all items and destroys the pane.
|
||||
# Public: Destroy all items.
|
||||
destroyItems: ->
|
||||
@destroyItem(item) for item in @getItems()
|
||||
|
||||
# Public: Destroys all items but the active one.
|
||||
# Public: Destroy all items except for the active item.
|
||||
destroyInactiveItems: ->
|
||||
@destroyItem(item) for item in @getItems() when item isnt @activeItem
|
||||
|
||||
destroy: ->
|
||||
if @container?.isAlive() and @container.getPanes().length is 1
|
||||
@destroyItems()
|
||||
else
|
||||
super
|
||||
|
||||
# Called by model superclass.
|
||||
destroyed: ->
|
||||
@container.activateNextPane() if @isActive()
|
||||
item.destroy?() for item in @items.slice()
|
||||
|
||||
# Public: Prompts the user to save the given item if it can be saved and is
|
||||
# currently unsaved.
|
||||
promptToSaveItem: (item) ->
|
||||
return true unless item.shouldPromptToSave?()
|
||||
|
||||
@@ -270,18 +424,23 @@ class Pane extends Model
|
||||
when 1 then false
|
||||
when 2 then true
|
||||
|
||||
# Public: Saves the active item.
|
||||
saveActiveItem: ->
|
||||
@saveItem(@activeItem)
|
||||
# Public: Save the active item.
|
||||
saveActiveItem: (nextAction) ->
|
||||
@saveItem(@getActiveItem(), nextAction)
|
||||
|
||||
# Public: Saves the active item at a prompted-for location.
|
||||
saveActiveItemAs: ->
|
||||
@saveItemAs(@activeItem)
|
||||
# Public: Prompt the user for a location and save the active item with the
|
||||
# path they select.
|
||||
#
|
||||
# * `nextAction` (optional) {Function} which will be called after the item is
|
||||
# successfully saved.
|
||||
saveActiveItemAs: (nextAction) ->
|
||||
@saveItemAs(@getActiveItem(), nextAction)
|
||||
|
||||
# Public: Saves the specified item.
|
||||
# Public: Save the given item.
|
||||
#
|
||||
# * `item` The item to save.
|
||||
# * `nextAction` (optional) {Function} which will be called after the item is saved.
|
||||
# * `nextAction` (optional) {Function} which will be called after the item is
|
||||
# successfully saved.
|
||||
saveItem: (item, nextAction) ->
|
||||
if item?.getUri?()
|
||||
item.save?()
|
||||
@@ -289,10 +448,12 @@ class Pane extends Model
|
||||
else
|
||||
@saveItemAs(item, nextAction)
|
||||
|
||||
# Public: Saves the given item at a prompted-for location.
|
||||
# Public: Prompt the user for a location and save the active item with the
|
||||
# path they select.
|
||||
#
|
||||
# * `item` The item to save.
|
||||
# * `nextAction` (optional) {Function} which will be called after the item is saved.
|
||||
# * `nextAction` (optional) {Function} which will be called after the item is
|
||||
# successfully saved.
|
||||
saveItemAs: (item, nextAction) ->
|
||||
return unless item?.saveAs?
|
||||
|
||||
@@ -302,17 +463,20 @@ class Pane extends Model
|
||||
item.saveAs(newItemPath)
|
||||
nextAction?()
|
||||
|
||||
# Public: Saves all items.
|
||||
# Public: Save all items.
|
||||
saveItems: ->
|
||||
@saveItem(item) for item in @getItems()
|
||||
|
||||
# Public: Returns the first item that matches the given URI or undefined if
|
||||
# Public: Return the first item that matches the given URI or undefined if
|
||||
# none exists.
|
||||
#
|
||||
# * `uri` {String} containing a URI.
|
||||
itemForUri: (uri) ->
|
||||
find @items, (item) -> item.getUri?() is uri
|
||||
|
||||
# Public: Activates the first item that matches the given URI. Returns a
|
||||
# boolean indicating whether a matching item was found.
|
||||
# Public: Activate the first item that matches the given URI.
|
||||
#
|
||||
# Returns a {Boolean} indicating whether an item matching the URI was found.
|
||||
activateItemForUri: (uri) ->
|
||||
if item = @itemForUri(uri)
|
||||
@activateItem(item)
|
||||
@@ -324,19 +488,57 @@ class Pane extends Model
|
||||
if @activeItem?
|
||||
@activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize())
|
||||
|
||||
# Public: Creates a new pane to the left of the receiver.
|
||||
###
|
||||
Section: Lifecycle
|
||||
###
|
||||
|
||||
# Public: Determine whether the pane is active.
|
||||
#
|
||||
# * `params` {Object} with keys
|
||||
# * `items` (optional) {Array} of items with which to construct the new pane.
|
||||
# Returns a {Boolean}.
|
||||
isActive: ->
|
||||
@container?.getActivePane() is this
|
||||
|
||||
# Public: Makes this pane the *active* pane, causing it to gain focus.
|
||||
activate: ->
|
||||
@container?.setActivePane(this)
|
||||
@emit 'activated'
|
||||
@emitter.emit 'did-activate'
|
||||
|
||||
# Public: Close the pane and destroy all its items.
|
||||
#
|
||||
# If this is the last pane, all the items will be destroyed but the pane
|
||||
# itself will not be destroyed.
|
||||
destroy: ->
|
||||
if @container?.isAlive() and @container.getPanes().length is 1
|
||||
@destroyItems()
|
||||
else
|
||||
super
|
||||
|
||||
# Called by model superclass.
|
||||
destroyed: ->
|
||||
@container.activateNextPane() if @isActive()
|
||||
@emitter.emit 'did-destroy'
|
||||
item.destroy?() for item in @items.slice()
|
||||
|
||||
###
|
||||
Section: Splitting
|
||||
###
|
||||
|
||||
# Public: Create a new pane to the left of this pane.
|
||||
#
|
||||
# * `params` (optional) {Object} with the following keys:
|
||||
# * `items` (optional) {Array} of items to add to the new pane.
|
||||
# * `copyActiveItem` (optional) {Boolean} true will copy the active item into the new split pane
|
||||
#
|
||||
# Returns the new {Pane}.
|
||||
splitLeft: (params) ->
|
||||
@split('horizontal', 'before', params)
|
||||
|
||||
# Public: Creates a new pane to the right of the receiver.
|
||||
# Public: Create a new pane to the right of this pane.
|
||||
#
|
||||
# * `params` {Object} with keys:
|
||||
# * `items` (optional) {Array} of items with which to construct the new pane.
|
||||
# * `params` (optional) {Object} with the following keys:
|
||||
# * `items` (optional) {Array} of items to add to the new pane.
|
||||
# * `copyActiveItem` (optional) {Boolean} true will copy the active item into the new split pane
|
||||
#
|
||||
# Returns the new {Pane}.
|
||||
splitRight: (params) ->
|
||||
@@ -344,8 +546,9 @@ class Pane extends Model
|
||||
|
||||
# Public: Creates a new pane above the receiver.
|
||||
#
|
||||
# * `params` {Object} with keys:
|
||||
# * `items` (optional) {Array} of items with which to construct the new pane.
|
||||
# * `params` (optional) {Object} with the following keys:
|
||||
# * `items` (optional) {Array} of items to add to the new pane.
|
||||
# * `copyActiveItem` (optional) {Boolean} true will copy the active item into the new split pane
|
||||
#
|
||||
# Returns the new {Pane}.
|
||||
splitUp: (params) ->
|
||||
@@ -353,14 +556,19 @@ class Pane extends Model
|
||||
|
||||
# Public: Creates a new pane below the receiver.
|
||||
#
|
||||
# * `params` {Object} with keys:
|
||||
# * `items` (optional) {Array} of items with which to construct the new pane.
|
||||
# * `params` (optional) {Object} with the following keys:
|
||||
# * `items` (optional) {Array} of items to add to the new pane.
|
||||
# * `copyActiveItem` (optional) {Boolean} true will copy the active item into the new split pane
|
||||
#
|
||||
# Returns the new {Pane}.
|
||||
splitDown: (params) ->
|
||||
@split('vertical', 'after', params)
|
||||
|
||||
split: (orientation, side, params) ->
|
||||
if params?.copyActiveItem
|
||||
params.items ?= []
|
||||
params.items.push(@copyActiveItem())
|
||||
|
||||
if @parent.orientation isnt orientation
|
||||
@parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this]}))
|
||||
|
||||
|
||||
@@ -18,19 +18,6 @@ Git = require './git'
|
||||
# Extended: Represents a project that's opened in Atom.
|
||||
#
|
||||
# An instance of this class is always available as the `atom.project` global.
|
||||
#
|
||||
# ## Events
|
||||
#
|
||||
# ### path-changed
|
||||
#
|
||||
# Extended: Emit when the project's path has changed. Use {::getPath} to get the new path
|
||||
#
|
||||
# ### buffer-created
|
||||
#
|
||||
# Extended: Emit when a buffer is created. For example, when {::open} is called, this is fired.
|
||||
#
|
||||
# * `buffer` {TextBuffer} the new buffer that was created.
|
||||
#
|
||||
module.exports =
|
||||
class Project extends Model
|
||||
atom.deserializers.add(this)
|
||||
@@ -49,7 +36,7 @@ class Project extends Model
|
||||
|
||||
for buffer in @buffers
|
||||
do (buffer) =>
|
||||
buffer.once 'destroyed', => @removeBuffer(buffer)
|
||||
buffer.onDidDestroy => @removeBuffer(buffer)
|
||||
|
||||
@setPath(path)
|
||||
|
||||
@@ -216,11 +203,11 @@ class Project extends Model
|
||||
|
||||
addBuffer: (buffer, options={}) ->
|
||||
@addBufferAtIndex(buffer, @buffers.length, options)
|
||||
buffer.once 'destroyed', => @removeBuffer(buffer)
|
||||
buffer.onDidDestroy => @removeBuffer(buffer)
|
||||
|
||||
addBufferAtIndex: (buffer, index, options={}) ->
|
||||
@buffers.splice(index, 0, buffer)
|
||||
buffer.once 'destroyed', => @removeBuffer(buffer)
|
||||
buffer.onDidDestroy => @removeBuffer(buffer)
|
||||
@emit 'buffer-created', buffer
|
||||
buffer
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class SelectListView extends View
|
||||
# This method can be overridden by subclasses but `super` should always
|
||||
# be called.
|
||||
initialize: ->
|
||||
@filterEditorView.getEditor().getBuffer().on 'changed', =>
|
||||
@filterEditorView.getEditor().getBuffer().onDidChange =>
|
||||
@schedulePopulateList()
|
||||
@filterEditorView.hiddenInput.on 'focusout', =>
|
||||
@cancel() unless @cancelling
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
{Point, Range} = require 'text-buffer'
|
||||
{Model} = require 'theorist'
|
||||
{pick} = require 'underscore-plus'
|
||||
{Emitter} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
|
||||
# Extended: Represents a selection in the {Editor}.
|
||||
#
|
||||
# ## Events
|
||||
#
|
||||
# ### screen-range-changed
|
||||
#
|
||||
# Extended: Emit when the selection was moved.
|
||||
#
|
||||
# * `screenRange` {Range} indicating the new screenrange
|
||||
#
|
||||
# ### destroyed
|
||||
#
|
||||
# Extended: Emit when the selection was destroyed
|
||||
#
|
||||
module.exports =
|
||||
class Selection extends Model
|
||||
cursor: null
|
||||
@@ -26,15 +15,54 @@ class Selection extends Model
|
||||
needsAutoscroll: null
|
||||
|
||||
constructor: ({@cursor, @marker, @editor, id}) ->
|
||||
@emitter = new Emitter
|
||||
|
||||
@assignId(id)
|
||||
@cursor.selection = this
|
||||
@decoration = @editor.decorateMarker(@marker, type: 'highlight', class: 'selection')
|
||||
|
||||
@marker.on 'changed', => @screenRangeChanged()
|
||||
@marker.on 'destroyed', =>
|
||||
@destroyed = true
|
||||
@editor.removeSelection(this)
|
||||
@emit 'destroyed' unless @editor.isDestroyed()
|
||||
@marker.onDidChange => @screenRangeChanged()
|
||||
@marker.onDidDestroy =>
|
||||
unless @editor.isDestroyed()
|
||||
@destroyed = true
|
||||
@editor.removeSelection(this)
|
||||
@emit 'destroyed'
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Extended: Calls your `callback` when the selection was moved.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `screenRange` {Range} indicating the new screenrange
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeRange: (callback) ->
|
||||
@emitter.on 'did-change-range', callback
|
||||
|
||||
# Extended: Calls your `callback` when the selection was destroyed
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidDestroy: (callback) ->
|
||||
@emitter.on 'did-destroy', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'screen-range-changed'
|
||||
Grim.deprecate("Use Selection::onDidChangeRange instead. Call ::getScreenRange() yourself in your callback if you need the range.")
|
||||
when 'destroyed'
|
||||
Grim.deprecate("Use Selection::onDidDestroy instead.")
|
||||
|
||||
super
|
||||
|
||||
###
|
||||
Section: Methods
|
||||
###
|
||||
|
||||
destroy: ->
|
||||
@marker.destroy()
|
||||
@@ -198,18 +226,26 @@ class Selection extends Model
|
||||
@modifySelection => @cursor.setBufferPosition(position)
|
||||
|
||||
# Public: Selects the text one position right of the cursor.
|
||||
selectRight: ->
|
||||
@modifySelection => @cursor.moveRight()
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to select (default: 1)
|
||||
selectRight: (columnCount) ->
|
||||
@modifySelection => @cursor.moveRight(columnCount)
|
||||
|
||||
# Public: Selects the text one position left of the cursor.
|
||||
selectLeft: ->
|
||||
@modifySelection => @cursor.moveLeft()
|
||||
#
|
||||
# * `columnCount` (optional) {Number} number of columns to select (default: 1)
|
||||
selectLeft: (columnCount) ->
|
||||
@modifySelection => @cursor.moveLeft(columnCount)
|
||||
|
||||
# Public: Selects all the text one position above the cursor.
|
||||
#
|
||||
# * `rowCount` (optional) {Number} number of rows to select (default: 1)
|
||||
selectUp: (rowCount) ->
|
||||
@modifySelection => @cursor.moveUp(rowCount)
|
||||
|
||||
# Public: Selects all the text one position below the cursor.
|
||||
#
|
||||
# * `rowCount` (optional) {Number} number of rows to select (default: 1)
|
||||
selectDown: (rowCount) ->
|
||||
@modifySelection => @cursor.moveDown(rowCount)
|
||||
|
||||
@@ -657,6 +693,6 @@ class Selection extends Model
|
||||
@getBufferRange().compare(otherSelection.getBufferRange())
|
||||
|
||||
screenRangeChanged: ->
|
||||
screenRange = @getScreenRange()
|
||||
@emit 'screen-range-changed', screenRange
|
||||
@editor.selectionScreenRangeChanged(this)
|
||||
@emit 'screen-range-changed', @getScreenRange()
|
||||
@emitter.emit 'did-change-range'
|
||||
@editor.selectionRangeChanged(this)
|
||||
|
||||
@@ -1,48 +1,93 @@
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter} = require 'emissary'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
{File} = require 'pathwatcher'
|
||||
fs = require 'fs-plus'
|
||||
Q = require 'q'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
{$} = require './space-pen-extensions'
|
||||
Package = require './package'
|
||||
{File} = require 'pathwatcher'
|
||||
|
||||
# Extended: Handles loading and activating available themes.
|
||||
#
|
||||
# An instance of this class is always available as the `atom.themes` global.
|
||||
#
|
||||
# ## Events
|
||||
#
|
||||
# ### reloaded
|
||||
#
|
||||
# Extended: Emit when all styles have been reloaded.
|
||||
#
|
||||
# ### stylesheet-added
|
||||
#
|
||||
# Extended: Emit when a stylesheet has been added.
|
||||
#
|
||||
# * `stylesheet` {StyleSheet} object that was removed
|
||||
#
|
||||
# ### stylesheet-removed
|
||||
#
|
||||
# Extended: Emit when a stylesheet has been removed.
|
||||
#
|
||||
# * `stylesheet` {StyleSheet} object that was removed
|
||||
#
|
||||
# ### stylesheets-changed
|
||||
#
|
||||
# Extended: Emit anytime any style sheet is added or removed from the editor
|
||||
#
|
||||
module.exports =
|
||||
class ThemeManager
|
||||
Emitter.includeInto(this)
|
||||
EmitterMixin.includeInto(this)
|
||||
|
||||
constructor: ({@packageManager, @resourcePath, @configDirPath, @safeMode}) ->
|
||||
@emitter = new Emitter
|
||||
@lessCache = null
|
||||
@initialLoadComplete = false
|
||||
@packageManager.registerPackageActivator(this, ['theme'])
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Essential: Invoke `callback` when all styles have been reloaded.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
onDidReloadAll: (callback) ->
|
||||
@emitter.on 'did-reload-all', callback
|
||||
|
||||
# Essential: Invoke `callback` when a stylesheet has been added to the dom.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `stylesheet` {StyleSheet} the style node
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidAddStylesheet: (callback) ->
|
||||
@emitter.on 'did-add-stylesheet', callback
|
||||
|
||||
# Essential: Invoke `callback` when a stylesheet has been removed from the dom.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `stylesheet` {StyleSheet} the style node
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidRemoveStylesheet: (callback) ->
|
||||
@emitter.on 'did-remove-stylesheet', callback
|
||||
|
||||
# Essential: Invoke `callback` when a stylesheet has been updated.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `stylesheet` {StyleSheet} the style node
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidUpdateStylesheet: (callback) ->
|
||||
@emitter.on 'did-update-stylesheet', callback
|
||||
|
||||
# Essential: Invoke `callback` when any stylesheet has been updated, added, or removed.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeStylesheets: (callback) ->
|
||||
@emitter.on 'did-change-stylesheets', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'reloaded'
|
||||
deprecate 'Use ThemeManager::onDidReloadAll instead'
|
||||
when 'stylesheet-added'
|
||||
deprecate 'Use ThemeManager::onDidAddStylesheet instead'
|
||||
when 'stylesheet-removed'
|
||||
deprecate 'Use ThemeManager::onDidRemoveStylesheet instead'
|
||||
when 'stylesheet-updated'
|
||||
deprecate 'Use ThemeManager::onDidUpdateStylesheet instead'
|
||||
when 'stylesheets-changed'
|
||||
deprecate 'Use ThemeManager::onDidChangeStylesheets instead'
|
||||
else
|
||||
deprecate 'ThemeManager::on is deprecated. Use event subscription methods instead.'
|
||||
EmitterMixin::on.apply(this, arguments)
|
||||
|
||||
###
|
||||
Section: Instance Methods
|
||||
###
|
||||
|
||||
getAvailableNames: ->
|
||||
# TODO: Maybe should change to list all the available themes out there?
|
||||
@getLoadedNames()
|
||||
@@ -63,7 +108,7 @@ class ThemeManager
|
||||
getLoadedThemes: ->
|
||||
pack for pack in @packageManager.getLoadedPackages() when pack.isTheme()
|
||||
|
||||
activatePackages: (themePackages) -> @activateThemes()
|
||||
activatePackages: -> @activateThemes()
|
||||
|
||||
# Get the enabled theme names from the config.
|
||||
#
|
||||
@@ -121,7 +166,9 @@ class ThemeManager
|
||||
@refreshLessCache() # Update cache again now that @getActiveThemes() is populated
|
||||
@loadUserStylesheet()
|
||||
@reloadBaseStylesheets()
|
||||
@initialLoadComplete = true
|
||||
@emit 'reloaded'
|
||||
@emitter.emit 'did-reload-all'
|
||||
deferred.resolve()
|
||||
|
||||
deferred.promise
|
||||
@@ -132,6 +179,8 @@ class ThemeManager
|
||||
@packageManager.deactivatePackage(pack.name) for pack in @getActiveThemes()
|
||||
null
|
||||
|
||||
isInitialLoadComplete: -> @initialLoadComplete
|
||||
|
||||
addActiveThemeClasses: ->
|
||||
for pack in @getActiveThemes()
|
||||
atom.workspaceView?[0]?.classList.add("theme-#{pack.name}")
|
||||
@@ -197,8 +246,8 @@ class ThemeManager
|
||||
if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less'])
|
||||
@requireStylesheet(nativeStylesheetPath)
|
||||
|
||||
stylesheetElementForId: (id, htmlElement=$('html')) ->
|
||||
htmlElement.find("""head style[id="#{id}"]""")
|
||||
stylesheetElementForId: (id) ->
|
||||
document.head.querySelector("""style[id="#{id}"]""")
|
||||
|
||||
resolveStylesheet: (stylesheetPath) ->
|
||||
if path.extname(stylesheetPath).length > 0
|
||||
@@ -208,16 +257,16 @@ class ThemeManager
|
||||
|
||||
# Public: Resolve and apply the stylesheet specified by the path.
|
||||
#
|
||||
# This supports both CSS and LESS stylsheets.
|
||||
# This supports both CSS and Less stylsheets.
|
||||
#
|
||||
# * `stylesheetPath` A {String} path to the stylesheet that can be an absolute
|
||||
# path or a relative path that will be resolved against the load path.
|
||||
#
|
||||
# Returns the absolute path to the required stylesheet.
|
||||
requireStylesheet: (stylesheetPath, type = 'bundled', htmlElement) ->
|
||||
requireStylesheet: (stylesheetPath, type='bundled') ->
|
||||
if fullPath = @resolveStylesheet(stylesheetPath)
|
||||
content = @loadStylesheet(fullPath)
|
||||
@applyStylesheet(fullPath, content, type = 'bundled', htmlElement)
|
||||
@applyStylesheet(fullPath, content, type)
|
||||
else
|
||||
throw new Error("Could not find a file at path '#{stylesheetPath}'")
|
||||
|
||||
@@ -244,11 +293,11 @@ class ThemeManager
|
||||
@lessCache.cssForFile(lessStylesheetPath, [baseVarImports, less].join('\n'))
|
||||
else
|
||||
@lessCache.read(lessStylesheetPath)
|
||||
catch e
|
||||
catch error
|
||||
console.error """
|
||||
Error compiling less stylesheet: #{lessStylesheetPath}
|
||||
Line number: #{e.line}
|
||||
#{e.message}
|
||||
Error compiling Less stylesheet: #{lessStylesheetPath}
|
||||
Line number: #{error.line}
|
||||
#{error.message}
|
||||
"""
|
||||
|
||||
stringToId: (string) ->
|
||||
@@ -257,23 +306,49 @@ class ThemeManager
|
||||
removeStylesheet: (stylesheetPath) ->
|
||||
fullPath = @resolveStylesheet(stylesheetPath) ? stylesheetPath
|
||||
element = @stylesheetElementForId(@stringToId(fullPath))
|
||||
if element.length > 0
|
||||
stylesheet = element[0].sheet
|
||||
if element?
|
||||
{sheet} = element
|
||||
element.remove()
|
||||
@emit 'stylesheet-removed', stylesheet
|
||||
@emit 'stylesheet-removed', sheet
|
||||
@emitter.emit 'did-remove-stylesheet', sheet
|
||||
@emit 'stylesheets-changed'
|
||||
@emitter.emit 'did-change-stylesheets'
|
||||
|
||||
applyStylesheet: (path, text, type = 'bundled', htmlElement=$('html')) ->
|
||||
styleElement = @stylesheetElementForId(@stringToId(path), htmlElement)
|
||||
if styleElement.length
|
||||
@emit 'stylesheet-removed', styleElement[0].sheet
|
||||
styleElement.text(text)
|
||||
applyStylesheet: (path, text, type='bundled') ->
|
||||
styleId = @stringToId(path)
|
||||
styleElement = @stylesheetElementForId(styleId)
|
||||
|
||||
if styleElement?
|
||||
@emit 'stylesheet-removed', styleElement.sheet
|
||||
@emitter.emit 'did-remove-stylesheet', styleElement.sheet
|
||||
styleElement.textContent = text
|
||||
else
|
||||
styleElement = $("<style class='#{type}' id='#{@stringToId(path)}'>#{text}</style>")
|
||||
if htmlElement.find("head style.#{type}").length
|
||||
htmlElement.find("head style.#{type}:last").after(styleElement)
|
||||
else
|
||||
htmlElement.find("head").append(styleElement)
|
||||
styleElement = document.createElement('style')
|
||||
styleElement.setAttribute('class', type)
|
||||
styleElement.setAttribute('id', styleId)
|
||||
styleElement.textContent = text
|
||||
|
||||
@emit 'stylesheet-added', styleElement[0].sheet
|
||||
elementToInsertBefore = _.last(document.head.querySelectorAll("style.#{type}"))?.nextElementSibling
|
||||
if elementToInsertBefore?
|
||||
document.head.insertBefore(styleElement, elementToInsertBefore)
|
||||
else
|
||||
document.head.appendChild(styleElement)
|
||||
|
||||
@emit 'stylesheet-added', styleElement.sheet
|
||||
@emitter.emit 'did-add-stylesheet', styleElement.sheet
|
||||
@emit 'stylesheets-changed'
|
||||
@emitter.emit 'did-change-stylesheets'
|
||||
|
||||
updateGlobalEditorStyle: (property, value) ->
|
||||
unless styleNode = @stylesheetElementForId('global-editor-styles')
|
||||
@applyStylesheet('global-editor-styles', '.editor {}')
|
||||
styleNode = @stylesheetElementForId('global-editor-styles')
|
||||
|
||||
{sheet} = styleNode
|
||||
editorRule = sheet.cssRules[0]
|
||||
editorRule.style[property] = value
|
||||
|
||||
@emit 'stylesheet-updated', sheet
|
||||
@emitter.emit 'did-update-stylesheet', sheet
|
||||
@emit 'stylesheets-changed'
|
||||
@emitter.emit 'did-change-stylesheets'
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
_ = require 'underscore-plus'
|
||||
{Model} = require 'theorist'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter} = require 'event-kit'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
Serializable = require 'serializable'
|
||||
TokenizedLine = require './tokenized-line'
|
||||
Token = require './token'
|
||||
Grim = require 'grim'
|
||||
|
||||
module.exports =
|
||||
class TokenizedBuffer extends Model
|
||||
@@ -20,20 +23,15 @@ class TokenizedBuffer extends Model
|
||||
visible: false
|
||||
|
||||
constructor: ({@buffer, @tabLength, @invisibles}) ->
|
||||
@emitter = new Emitter
|
||||
|
||||
@tabLength ?= atom.config.getPositiveInt('editor.tabLength', 2)
|
||||
|
||||
@subscribe atom.syntax, 'grammar-added grammar-updated', (grammar) =>
|
||||
if grammar.injectionSelector?
|
||||
@retokenizeLines() if @hasTokenForSelector(grammar.injectionSelector)
|
||||
else
|
||||
newScore = grammar.getScore(@buffer.getPath(), @buffer.getText())
|
||||
@setGrammar(grammar, newScore) if newScore > @currentGrammarScore
|
||||
@subscribe atom.syntax.onDidAddGrammar(@grammarAddedOrUpdated)
|
||||
@subscribe atom.syntax.onDidUpdateGrammar(@grammarAddedOrUpdated)
|
||||
|
||||
@on 'grammar-changed grammar-updated', => @retokenizeLines()
|
||||
@subscribe @buffer, "changed", (e) => @handleBufferChange(e)
|
||||
@subscribe @buffer, "path-changed", =>
|
||||
@bufferPath = @buffer.getPath()
|
||||
@reloadGrammar()
|
||||
@subscribe @buffer.onDidChange (e) => @handleBufferChange(e)
|
||||
@subscribe @buffer.onDidChangePath (@bufferPath) => @reloadGrammar()
|
||||
|
||||
@subscribe @$tabLength.changes, (tabLength) => @retokenizeLines()
|
||||
|
||||
@@ -51,13 +49,44 @@ class TokenizedBuffer extends Model
|
||||
params.buffer = atom.project.bufferForPathSync(params.bufferPath)
|
||||
params
|
||||
|
||||
onDidChangeGrammar: (callback) ->
|
||||
@emitter.on 'did-change-grammar', callback
|
||||
|
||||
onDidChange: (callback) ->
|
||||
@emitter.on 'did-change', callback
|
||||
|
||||
onDidTokenize: (callback) ->
|
||||
@emitter.on 'did-tokenize', callback
|
||||
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'changed'
|
||||
Grim.deprecate("Use TokenizedBuffer::onDidChange instead")
|
||||
when 'grammar-changed'
|
||||
Grim.deprecate("Use TokenizedBuffer::onDidChangeGrammar instead")
|
||||
when 'tokenized'
|
||||
Grim.deprecate("Use TokenizedBuffer::onDidTokenize instead")
|
||||
else
|
||||
Grim.deprecate("TokenizedBuffer::on is deprecated. Use event subscription methods instead.")
|
||||
|
||||
EmitterMixin::on.apply(this, arguments)
|
||||
|
||||
grammarAddedOrUpdated: (grammar) =>
|
||||
if grammar.injectionSelector?
|
||||
@retokenizeLines() if @hasTokenForSelector(grammar.injectionSelector)
|
||||
else
|
||||
newScore = grammar.getScore(@buffer.getPath(), @buffer.getText())
|
||||
@setGrammar(grammar, newScore) if newScore > @currentGrammarScore
|
||||
|
||||
setGrammar: (grammar, score) ->
|
||||
return if grammar is @grammar
|
||||
@unsubscribe(@grammar) if @grammar
|
||||
@grammar = grammar
|
||||
@currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @buffer.getText())
|
||||
@subscribe @grammar, 'grammar-updated', => @retokenizeLines()
|
||||
@subscribe @grammar.onDidUpdate => @retokenizeLines()
|
||||
@retokenizeLines()
|
||||
@emit 'grammar-changed', grammar
|
||||
@emitter.emit 'did-change-grammar', grammar
|
||||
|
||||
reloadGrammar: ->
|
||||
if grammar = atom.syntax.selectGrammar(@buffer.getPath(), @buffer.getText())
|
||||
@@ -77,7 +106,9 @@ class TokenizedBuffer extends Model
|
||||
@invalidRows = []
|
||||
@invalidateRow(0)
|
||||
@fullyTokenized = false
|
||||
@emit "changed", {start: 0, end: lastRow, delta: 0}
|
||||
event = {start: 0, end: lastRow, delta: 0}
|
||||
@emit 'changed', event
|
||||
@emitter.emit 'did-change', event
|
||||
|
||||
setVisible: (@visible) ->
|
||||
@tokenizeInBackground() if @visible
|
||||
@@ -127,12 +158,16 @@ class TokenizedBuffer extends Model
|
||||
|
||||
@validateRow(row)
|
||||
@invalidateRow(row + 1) unless filledRegion
|
||||
@emit "changed", { start: invalidRow, end: row, delta: 0 }
|
||||
event = { start: invalidRow, end: row, delta: 0 }
|
||||
@emit 'changed', event
|
||||
@emitter.emit 'did-change', event
|
||||
|
||||
if @firstInvalidRow()?
|
||||
@tokenizeInBackground()
|
||||
else
|
||||
@emit "tokenized" unless @fullyTokenized
|
||||
unless @fullyTokenized
|
||||
@emit 'tokenized'
|
||||
@emitter.emit 'did-tokenize'
|
||||
@fullyTokenized = true
|
||||
|
||||
firstInvalidRow: ->
|
||||
@@ -173,7 +208,9 @@ class TokenizedBuffer extends Model
|
||||
if newEndStack and not _.isEqual(newEndStack, previousEndStack)
|
||||
@invalidateRow(end + delta + 1)
|
||||
|
||||
@emit "changed", { start, end, delta, bufferChange: e }
|
||||
event = { start, end, delta, bufferChange: e }
|
||||
@emit 'changed', event
|
||||
@emitter.emit 'did-change', event
|
||||
|
||||
retokenizeWhitespaceRowsIfIndentLevelChanged: (row, increment) ->
|
||||
line = @tokenizedLines[row]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
path = require 'path'
|
||||
{$} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
ipc = require 'ipc'
|
||||
@@ -28,9 +29,11 @@ class WindowEventHandler
|
||||
@subscribe $(window), 'blur', -> document.body.classList.add('is-blurred')
|
||||
|
||||
@subscribe $(window), 'window:open-path', (event, {pathToOpen, initialLine, initialColumn}) ->
|
||||
if fs.isDirectorySync(pathToOpen)
|
||||
atom.project.setPath(pathToOpen) unless atom.project.getPath()
|
||||
else
|
||||
unless atom.project?.getPath()
|
||||
if fs.existsSync(pathToOpen) or fs.existsSync(path.dirname(pathToOpen))
|
||||
atom.project?.setPath(pathToOpen)
|
||||
|
||||
unless fs.isDirectorySync(pathToOpen)
|
||||
atom.workspace?.open(pathToOpen, {initialLine, initialColumn})
|
||||
|
||||
@subscribe $(window), 'beforeunload', =>
|
||||
|
||||
@@ -84,7 +84,7 @@ class WorkspaceView extends View
|
||||
@panes.replaceWith(panes)
|
||||
@panes = panes
|
||||
|
||||
@subscribe @model, 'uri-opened', => @trigger 'uri-opened'
|
||||
@subscribe @model.onDidOpen => @trigger 'uri-opened'
|
||||
|
||||
@subscribe scrollbarStyle, (style) =>
|
||||
@removeClass('scrollbars-visible-always scrollbars-visible-when-scrolling')
|
||||
@@ -240,7 +240,8 @@ class WorkspaceView extends View
|
||||
#
|
||||
# Returns an {Array} of {EditorView}s.
|
||||
getEditorViews: ->
|
||||
@panes.find('.pane > .item-views > .editor').map(-> $(this).view()).toArray()
|
||||
for editorElement in @panes.element.querySelectorAll('.pane > .item-views > .editor')
|
||||
$(editorElement).view()
|
||||
|
||||
# Public: Prepend an element or view to the panels at the top of the
|
||||
# workspace.
|
||||
@@ -371,25 +372,14 @@ class WorkspaceView extends View
|
||||
beforeRemove: ->
|
||||
@model.destroy()
|
||||
|
||||
setEditorFontSize: (fontSize) =>
|
||||
@setEditorStyle('font-size', fontSize + 'px')
|
||||
setEditorFontSize: (fontSize) ->
|
||||
atom.themes.updateGlobalEditorStyle('font-size', fontSize + 'px')
|
||||
|
||||
setEditorFontFamily: (fontFamily) =>
|
||||
@setEditorStyle('font-family', fontFamily)
|
||||
setEditorFontFamily: (fontFamily) ->
|
||||
atom.themes.updateGlobalEditorStyle('font-family', fontFamily)
|
||||
|
||||
setEditorLineHeight: (lineHeight) =>
|
||||
@setEditorStyle('line-height', lineHeight)
|
||||
|
||||
setEditorStyle: (property, value) ->
|
||||
unless styleNode = atom.themes.stylesheetElementForId('global-editor-styles')[0]
|
||||
atom.themes.applyStylesheet('global-editor-styles', '.editor {}')
|
||||
styleNode = atom.themes.stylesheetElementForId('global-editor-styles')[0]
|
||||
|
||||
{sheet} = styleNode
|
||||
editorRule = sheet.cssRules[0]
|
||||
editorRule.style[property] = value
|
||||
atom.themes.emit 'stylesheet-updated', sheet
|
||||
atom.themes.emit 'stylesheets-changed'
|
||||
setEditorLineHeight: (lineHeight) ->
|
||||
atom.themes.updateGlobalEditorStyle('line-height', lineHeight)
|
||||
|
||||
# Deprecated
|
||||
eachPane: (callback) ->
|
||||
@@ -409,4 +399,4 @@ class WorkspaceView extends View
|
||||
# Deprecated: Call {Workspace::getActivePaneItem} instead.
|
||||
getActivePaneItem: ->
|
||||
deprecate("Use Workspace::getActivePaneItem instead")
|
||||
@model.activePaneItem
|
||||
@model.getActivePaneItem()
|
||||
|
||||
@@ -5,6 +5,7 @@ _ = require 'underscore-plus'
|
||||
Q = require 'q'
|
||||
Serializable = require 'serializable'
|
||||
Delegator = require 'delegato'
|
||||
{Emitter} = require 'event-kit'
|
||||
Editor = require './editor'
|
||||
PaneContainer = require './pane-container'
|
||||
Pane = require './pane'
|
||||
@@ -16,17 +17,6 @@ Pane = require './pane'
|
||||
# editors, and manipulate panes. To add panels, you'll need to use the
|
||||
# {WorkspaceView} class for now until we establish APIs at the model layer.
|
||||
#
|
||||
# ## Events
|
||||
#
|
||||
# ### uri-opened
|
||||
#
|
||||
# Extended: Emit when something has been opened. This can be anything, from an
|
||||
# editor to the settings view. You can get the new item via {::getActivePaneItem}
|
||||
#
|
||||
# ### editor-created
|
||||
#
|
||||
# Extended: Emit when an editor is created (a file opened).
|
||||
#
|
||||
# * `editor` {Editor} the new editor
|
||||
#
|
||||
module.exports =
|
||||
@@ -44,9 +34,11 @@ class Workspace extends Model
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
@emitter = new Emitter
|
||||
@openers = []
|
||||
|
||||
@subscribe @paneContainer, 'item-destroyed', @onPaneItemDestroyed
|
||||
@paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed)
|
||||
|
||||
@registerOpener (filePath) =>
|
||||
switch filePath
|
||||
when 'atom://.atom/stylesheet'
|
||||
@@ -83,34 +75,163 @@ class Workspace extends Model
|
||||
for scopeName in includedGrammarScopes ? []
|
||||
addGrammar(atom.syntax.grammarForScopeName(scopeName))
|
||||
|
||||
addGrammar(editor.getGrammar()) for editor in @getEditors()
|
||||
editors = @getTextEditors()
|
||||
addGrammar(editor.getGrammar()) for editor in editors
|
||||
|
||||
if editors.length > 0
|
||||
for grammar in atom.syntax.getGrammars() when grammar.injectionSelector
|
||||
addGrammar(grammar)
|
||||
|
||||
_.uniq(packageNames)
|
||||
|
||||
editorAdded: (editor) ->
|
||||
@emit 'editor-created', editor
|
||||
|
||||
# Public: Register a function to be called for every current and future
|
||||
# {Editor} in the workspace.
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Extended: Invoke the given callback when a pane is added to the workspace.
|
||||
#
|
||||
# * `callback` A {Function} with an {Editor} as its only argument.
|
||||
# * `callback` {Function} to be called panes are added.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `pane` The added pane.
|
||||
#
|
||||
# Returns a subscription object with an `.off` method that you can call to
|
||||
# unregister the callback.
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidAddPane: (callback) -> @paneContainer.onDidAddPane(callback)
|
||||
|
||||
# Extended: Invoke the given callback with all current and future panes in the
|
||||
# workspace.
|
||||
#
|
||||
# * `callback` {Function} to be called with current and future panes.
|
||||
# * `pane` A {Pane} that is present in {::getPanes} at the time of
|
||||
# subscription or that is added at some later time.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observePanes: (callback) -> @paneContainer.observePanes(callback)
|
||||
|
||||
# Extended: Invoke the given callback when the active pane changes.
|
||||
#
|
||||
# * `callback` {Function} to be called when the active pane changes.
|
||||
# * `pane` A {Pane} that is the current return value of {::getActivePane}.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeActivePane: (callback) -> @paneContainer.onDidChangeActivePane(callback)
|
||||
|
||||
# Extended: Invoke the given callback with the current active pane and when
|
||||
# the active pane changes.
|
||||
#
|
||||
# * `callback` {Function} to be called with the current and future active#
|
||||
# panes.
|
||||
# * `pane` A {Pane} that is the current return value of {::getActivePane}.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observeActivePane: (callback) -> @paneContainer.observeActivePane(callback)
|
||||
|
||||
# Extended: Invoke the given callback when a pane item is added to the
|
||||
# workspace.
|
||||
#
|
||||
# * `callback` {Function} to be called when panes are added.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `item` The added pane item.
|
||||
# * `pane` {Pane} containing the added item.
|
||||
# * `index` {Number} indicating the index of the added item in its pane.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidAddPaneItem: (callback) -> @paneContainer.onDidAddPaneItem(callback)
|
||||
|
||||
# Extended: Invoke the given callback when the active pane item changes.
|
||||
#
|
||||
# * `callback` {Function} to be called when the active pane item changes.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `activeItem` The active pane item.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeActivePaneItem: (callback) -> @paneContainer.onDidChangeActivePaneItem(callback)
|
||||
|
||||
# Extended: Invoke the given callback with all current and future panes items in
|
||||
# the workspace.
|
||||
#
|
||||
# * `callback` {Function} to be called with current and future pane items.
|
||||
# * `item` An item that is present in {::getPaneItems} at the time of
|
||||
# subscription or that is added at some later time.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observePaneItems: (callback) -> @paneContainer.observePaneItems(callback)
|
||||
|
||||
# Extended: Invoke the given callback when a text editor is added to the
|
||||
# workspace.
|
||||
#
|
||||
# * `callback` {Function} to be called panes are added.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `textEditor` {Editor} that was added.
|
||||
# * `pane` {Pane} containing the added text editor.
|
||||
# * `index` {Number} indicating the index of the added text editor in its
|
||||
# pane.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidAddTextEditor: (callback) ->
|
||||
@onDidAddPaneItem ({item, pane, index}) ->
|
||||
callback({textEditor: item, pane, index}) if item instanceof Editor
|
||||
|
||||
# Essential: Invoke the given callback with all current and future text
|
||||
# editors in the workspace.
|
||||
#
|
||||
# * `callback` {Function} to be called with current and future text editors.
|
||||
# * `editor` An {Editor} that is present in {::getTextEditors} at the time
|
||||
# of subscription or that is added at some later time.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
observeTextEditors: (callback) ->
|
||||
callback(textEditor) for textEditor in @getTextEditors()
|
||||
@onDidAddTextEditor ({textEditor}) -> callback(textEditor)
|
||||
|
||||
# Essential: Invoke the given callback whenever an item is opened. Unlike
|
||||
# ::onDidAddPaneItem, observers will be notified for items that are already
|
||||
# present in the workspace when they are reopened.
|
||||
#
|
||||
# * `callback` {Function} to be called whenever an item is opened.
|
||||
# * `event` {Object} with the following keys:
|
||||
# * `uri` {String} representing the opened URI. Could be `undefined`.
|
||||
# * `item` The opened item.
|
||||
# * `pane` The pane in which the item was opened.
|
||||
# * `index` The index of the opened item on its pane.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidOpen: (callback) ->
|
||||
@emitter.on 'did-open', callback
|
||||
|
||||
eachEditor: (callback) ->
|
||||
deprecate("Use Workspace::observeTextEditors instead")
|
||||
|
||||
callback(editor) for editor in @getEditors()
|
||||
@subscribe this, 'editor-created', (editor) -> callback(editor)
|
||||
|
||||
# Public: Get all current editors in the workspace.
|
||||
#
|
||||
# Returns an {Array} of {Editor}s.
|
||||
getEditors: ->
|
||||
deprecate("Use Workspace::getTextEditors instead")
|
||||
|
||||
editors = []
|
||||
for pane in @paneContainer.getPanes()
|
||||
editors.push(item) for item in pane.getItems() when item instanceof Editor
|
||||
|
||||
editors
|
||||
|
||||
# Public: Open a given a URI in Atom asynchronously.
|
||||
on: (eventName) ->
|
||||
switch eventName
|
||||
when 'editor-created'
|
||||
deprecate("Use Workspace::onDidAddTextEditor or Workspace::observeTextEditors instead.")
|
||||
when 'uri-opened'
|
||||
deprecate("Use Workspace::onDidAddPaneItem instead.")
|
||||
else
|
||||
deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.")
|
||||
|
||||
super
|
||||
|
||||
###
|
||||
Section: Opening
|
||||
###
|
||||
|
||||
# Essential: Open a given a URI in Atom asynchronously.
|
||||
#
|
||||
# * `uri` A {String} containing a URI.
|
||||
# * `options` (optional) {Object}
|
||||
@@ -137,11 +258,11 @@ class Workspace extends Model
|
||||
pane = @paneContainer.paneForUri(uri) if searchAllPanes
|
||||
pane ?= switch split
|
||||
when 'left'
|
||||
@activePane.findLeftmostSibling()
|
||||
@getActivePane().findLeftmostSibling()
|
||||
when 'right'
|
||||
@activePane.findOrCreateRightmostSibling()
|
||||
@getActivePane().findOrCreateRightmostSibling()
|
||||
else
|
||||
@activePane
|
||||
@getActivePane()
|
||||
|
||||
@openUriInPane(uri, pane, options)
|
||||
|
||||
@@ -195,12 +316,14 @@ class Workspace extends Model
|
||||
@itemOpened(item)
|
||||
pane.activateItem(item)
|
||||
pane.activate() if changeFocus
|
||||
index = pane.getActiveItemIndex()
|
||||
@emit "uri-opened"
|
||||
@emitter.emit 'did-open', {uri, pane, item, index}
|
||||
item
|
||||
.catch (error) ->
|
||||
console.error(error.stack ? error)
|
||||
|
||||
# Public: Asynchronously reopens the last-closed item's URI if it hasn't already been
|
||||
# Extended: Asynchronously reopens the last-closed item's URI if it hasn't already been
|
||||
# reopened.
|
||||
#
|
||||
# Returns a promise that is resolved when the item is opened
|
||||
@@ -216,7 +339,7 @@ class Workspace extends Model
|
||||
if uri = @destroyedItemUris.pop()
|
||||
@openSync(uri)
|
||||
|
||||
# Public: Register an opener for a uri.
|
||||
# Extended: Register an opener for a uri.
|
||||
#
|
||||
# An {Editor} will be used if no openers return a value.
|
||||
#
|
||||
@@ -232,52 +355,52 @@ class Workspace extends Model
|
||||
registerOpener: (opener) ->
|
||||
@openers.push(opener)
|
||||
|
||||
# Public: Unregister an opener registered with {::registerOpener}.
|
||||
# Extended: Unregister an opener registered with {::registerOpener}.
|
||||
unregisterOpener: (opener) ->
|
||||
_.remove(@openers, opener)
|
||||
|
||||
getOpeners: ->
|
||||
@openers
|
||||
|
||||
# Public: Get the active {Pane}.
|
||||
###
|
||||
Section: Pane Items
|
||||
###
|
||||
|
||||
# Essential: Get all pane items in the workspace.
|
||||
#
|
||||
# Returns a {Pane}.
|
||||
getActivePane: ->
|
||||
@paneContainer.activePane
|
||||
# Returns an {Array} of items.
|
||||
getPaneItems: ->
|
||||
@paneContainer.getPaneItems()
|
||||
|
||||
# Public: Get all {Pane}s.
|
||||
#
|
||||
# Returns an {Array} of {Pane}s.
|
||||
getPanes: ->
|
||||
@paneContainer.getPanes()
|
||||
|
||||
# Public: Save all pane items.
|
||||
saveAll: ->
|
||||
@paneContainer.saveAll()
|
||||
|
||||
# Public: Make the next pane active.
|
||||
activateNextPane: ->
|
||||
@paneContainer.activateNextPane()
|
||||
|
||||
# Public: Make the previous pane active.
|
||||
activatePreviousPane: ->
|
||||
@paneContainer.activatePreviousPane()
|
||||
|
||||
# Public: Get the first pane {Pane} with an item for the given URI.
|
||||
#
|
||||
# * `uri` {String} uri
|
||||
#
|
||||
# Returns a {Pane} or `undefined` if no pane exists for the given URI.
|
||||
paneForUri: (uri) ->
|
||||
@paneContainer.paneForUri(uri)
|
||||
|
||||
# Public: Get the active {Pane}'s active item.
|
||||
# Essential: Get the active {Pane}'s active item.
|
||||
#
|
||||
# Returns an pane item {Object}.
|
||||
getActivePaneItem: ->
|
||||
@paneContainer.getActivePane().getActiveItem()
|
||||
@paneContainer.getActivePaneItem()
|
||||
|
||||
# Public: Save the active pane item.
|
||||
# Essential: Get all text editors in the workspace.
|
||||
#
|
||||
# Returns an {Array} of {Editor}s.
|
||||
getTextEditors: ->
|
||||
@getPaneItems().filter (item) -> item instanceof Editor
|
||||
|
||||
# Essential: Get the active item if it is an {Editor}.
|
||||
#
|
||||
# Returns an {Editor} or `undefined` if the current active item is not an
|
||||
# {Editor}.
|
||||
getActiveTextEditor: ->
|
||||
activeItem = @getActivePaneItem()
|
||||
activeItem if activeItem instanceof Editor
|
||||
|
||||
# Deprecated:
|
||||
getActiveEditor: ->
|
||||
@activePane?.getActiveEditor()
|
||||
|
||||
# Extended: Save all pane items.
|
||||
saveAll: ->
|
||||
@paneContainer.saveAll()
|
||||
|
||||
# Save the active pane item.
|
||||
#
|
||||
# If the active pane item currently has a URI according to the item's
|
||||
# `.getUri` method, calls `.save` on the item. Otherwise
|
||||
@@ -286,7 +409,7 @@ class Workspace extends Model
|
||||
saveActivePaneItem: ->
|
||||
@activePane?.saveActiveItem()
|
||||
|
||||
# Public: Prompt the user for a path and save the active pane item to it.
|
||||
# Prompt the user for a path and save the active pane item to it.
|
||||
#
|
||||
# Opens a native dialog where the user selects a path on disk, then calls
|
||||
# `.saveAs` on the item with the selected path. This method does nothing if
|
||||
@@ -294,34 +417,59 @@ class Workspace extends Model
|
||||
saveActivePaneItemAs: ->
|
||||
@activePane?.saveActiveItemAs()
|
||||
|
||||
# Public: Destroy (close) the active pane item.
|
||||
# Destroy (close) the active pane item.
|
||||
#
|
||||
# Removes the active pane item and calls the `.destroy` method on it if one is
|
||||
# defined.
|
||||
destroyActivePaneItem: ->
|
||||
@activePane?.destroyActiveItem()
|
||||
|
||||
# Public: Destroy (close) the active pane.
|
||||
###
|
||||
Section: Panes
|
||||
###
|
||||
|
||||
# Extended: Get all panes in the workspace.
|
||||
#
|
||||
# Returns an {Array} of {Pane}s.
|
||||
getPanes: ->
|
||||
@paneContainer.getPanes()
|
||||
|
||||
# Extended: Get the active {Pane}.
|
||||
#
|
||||
# Returns a {Pane}.
|
||||
getActivePane: ->
|
||||
@paneContainer.getActivePane()
|
||||
|
||||
# Extended: Make the next pane active.
|
||||
activateNextPane: ->
|
||||
@paneContainer.activateNextPane()
|
||||
|
||||
# Extended: Make the previous pane active.
|
||||
activatePreviousPane: ->
|
||||
@paneContainer.activatePreviousPane()
|
||||
|
||||
# Extended: Get the first pane {Pane} with an item for the given URI.
|
||||
#
|
||||
# * `uri` {String} uri
|
||||
#
|
||||
# Returns a {Pane} or `undefined` if no pane exists for the given URI.
|
||||
paneForUri: (uri) ->
|
||||
@paneContainer.paneForUri(uri)
|
||||
|
||||
# Destroy (close) the active pane.
|
||||
destroyActivePane: ->
|
||||
@activePane?.destroy()
|
||||
|
||||
# Public: Get the active item if it is an {Editor}.
|
||||
#
|
||||
# Returns an {Editor} or `undefined` if the current active item is not an
|
||||
# {Editor}.
|
||||
getActiveEditor: ->
|
||||
@activePane?.getActiveEditor()
|
||||
|
||||
# Public: Increase the editor font size by 1px.
|
||||
# Increase the editor font size by 1px.
|
||||
increaseFontSize: ->
|
||||
atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1)
|
||||
|
||||
# Public: Decrease the editor font size by 1px.
|
||||
# Decrease the editor font size by 1px.
|
||||
decreaseFontSize: ->
|
||||
fontSize = atom.config.get("editor.fontSize")
|
||||
atom.config.set("editor.fontSize", fontSize - 1) if fontSize > 1
|
||||
|
||||
# Public: Restore to a default editor font size.
|
||||
# Restore to a default editor font size.
|
||||
resetFontSize: ->
|
||||
atom.config.restoreDefault("editor.fontSize")
|
||||
|
||||
|
||||
3
vendor/jasmine-jquery.js
vendored
3
vendor/jasmine-jquery.js
vendored
@@ -138,6 +138,9 @@ jasmine.JQuery.matchersClass = {};
|
||||
var builtInMatcher = jasmine.Matchers.prototype[methodName];
|
||||
|
||||
jasmine.JQuery.matchersClass[methodName] = function() {
|
||||
if (this.actual instanceof HTMLElement) {
|
||||
this.actual = jQuery(this.actual);
|
||||
}
|
||||
if (this.actual instanceof jQuery) {
|
||||
var result = jQueryMatchers[methodName].apply(this, arguments);
|
||||
this.actual = jasmine.JQuery.elementToString(this.actual);
|
||||
|
||||
Reference in New Issue
Block a user