mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-12-26 08:03:17 +03:00
Merge branch 'master' into platform-keybindings
This commit is contained in:
commit
6157fcaf73
@ -8,6 +8,10 @@ _ = require 'underscore-plus'
|
|||||||
packageJson = require './package.json'
|
packageJson = require './package.json'
|
||||||
|
|
||||||
module.exports = (grunt) ->
|
module.exports = (grunt) ->
|
||||||
|
if not grunt.option('verbose')
|
||||||
|
grunt.log.writeln = (args...) -> grunt.log
|
||||||
|
grunt.log.write = (args...) -> grunt.log
|
||||||
|
|
||||||
[major, minor, patch] = packageJson.version.split('.')
|
[major, minor, patch] = packageJson.version.split('.')
|
||||||
if process.platform is 'win32'
|
if process.platform is 'win32'
|
||||||
appName = 'Atom'
|
appName = 'Atom'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{Document, Point, Range, Site} = require 'telepath'
|
{Document, Point, Range} = require 'telepath'
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
_: require 'underscore-plus'
|
_: require 'underscore-plus'
|
||||||
@ -11,7 +11,6 @@ module.exports =
|
|||||||
Git: require '../src/git'
|
Git: require '../src/git'
|
||||||
Point: Point
|
Point: Point
|
||||||
Range: Range
|
Range: Range
|
||||||
Site: Site
|
|
||||||
|
|
||||||
# The following classes can't be used from a Task handler and should therefore
|
# The following classes can't be used from a Task handler and should therefore
|
||||||
# only be exported when not running as a child node process
|
# only be exported when not running as a child node process
|
||||||
@ -22,7 +21,6 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
|||||||
module.exports.$$ = $$
|
module.exports.$$ = $$
|
||||||
module.exports.$$$ = $$$
|
module.exports.$$$ = $$$
|
||||||
module.exports.Editor = require '../src/editor'
|
module.exports.Editor = require '../src/editor'
|
||||||
module.exports.pathForRepositoryUrl = require('../src/project').pathForRepositoryUrl
|
|
||||||
module.exports.RootView = require '../src/root-view'
|
module.exports.RootView = require '../src/root-view'
|
||||||
module.exports.SelectList = require '../src/select-list'
|
module.exports.SelectList = require '../src/select-list'
|
||||||
module.exports.ScrollView = require '../src/scroll-view'
|
module.exports.ScrollView = require '../src/scroll-view'
|
||||||
|
172
menus/win32.cson
Normal file
172
menus/win32.cson
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
'menu': [
|
||||||
|
{
|
||||||
|
label: '&File'
|
||||||
|
submenu: [
|
||||||
|
{ label: 'New &Window', command: 'application:new-window' }
|
||||||
|
{ label: '&New File', command: 'application:new-file' }
|
||||||
|
{ label: '&Open...', command: 'application:open' }
|
||||||
|
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: '&Preferences...', command: 'application:show-settings' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: '&Save', command: 'core:save' }
|
||||||
|
{ label: 'Save &As...', command: 'core:save-as' }
|
||||||
|
{ label: 'Save A&ll', command: 'window:save-all' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: '&Close Buffer', command: 'core:close' }
|
||||||
|
{ label: 'Close All &Buffers', command: 'pane:close' }
|
||||||
|
{ label: 'Clos&e Window', command: 'window:close' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'E&xit', command: 'application:quit' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
label: '&Edit'
|
||||||
|
submenu: [
|
||||||
|
{ label: '&Undo', command: 'core:undo' }
|
||||||
|
{ label: '&Redo', command: 'core:redo' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: '&Cut', command: 'core:cut' }
|
||||||
|
{ label: 'C&opy', command: 'core:copy' }
|
||||||
|
{ label: 'Copy Pat&h', command: 'editor:copy-path' }
|
||||||
|
{ label: '&Paste', command: 'core:paste' }
|
||||||
|
{ label: 'Select &All', command: 'core:select-all' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: '&Toggle Comments', command: 'editor:toggle-line-comments' }
|
||||||
|
{
|
||||||
|
label: 'Lines',
|
||||||
|
submenu: [
|
||||||
|
{ label: '&Indent', command: 'editor:indent-selected-rows' }
|
||||||
|
{ label: '&Outdent', command: 'editor:outdent-selected-rows' }
|
||||||
|
{ label: '&Auto Indent', command: 'editor:auto-indent' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'Move Line &Up', command: 'editor:move-line-up' }
|
||||||
|
{ label: 'Move Line &Down', command: 'editor:move-line-down' }
|
||||||
|
{ label: 'Du&plicate Line', command: 'editor:duplicate-line' }
|
||||||
|
{ label: 'D&elete Line', command: 'editor:delete-line' }
|
||||||
|
{ label: '&Join Lines', command: 'editor:join-line' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
label: 'Text',
|
||||||
|
submenu: [
|
||||||
|
{ label: '&Upper Case', command: 'editor:upper-case' }
|
||||||
|
{ label: '&Lower Case', command: 'editor:lower-case' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'Delete to End of &Word', command: 'editor:delete-to-end-of-word' }
|
||||||
|
{ label: '&Delete Line', command: 'editor:delete-line' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: '&Transpose', command: 'editor:transpose' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
label: 'Folding',
|
||||||
|
submenu: [
|
||||||
|
{ label: '&Fold', command: 'editor:fold-current-row' }
|
||||||
|
{ label: '&Unfold', command: 'editor:unfold-current-row' }
|
||||||
|
{ label: 'Unfold &All', command: 'editor:unfold-all' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'Fol&d All', command: 'editor:fold-all' }
|
||||||
|
{ label: 'Fold Level 1', command: 'editor:fold-at-indent-level-1' }
|
||||||
|
{ label: 'Fold Level 2', command: 'editor:fold-at-indent-level-2' }
|
||||||
|
{ label: 'Fold Level 3', command: 'editor:fold-at-indent-level-3' }
|
||||||
|
{ label: 'Fold Level 4', command: 'editor:fold-at-indent-level-4' }
|
||||||
|
{ label: 'Fold Level 5', command: 'editor:fold-at-indent-level-5' }
|
||||||
|
{ label: 'Fold Level 6', command: 'editor:fold-at-indent-level-6' }
|
||||||
|
{ label: 'Fold Level 7', command: 'editor:fold-at-indent-level-7' }
|
||||||
|
{ label: 'Fold Level 8', command: 'editor:fold-at-indent-level-8' }
|
||||||
|
{ label: 'Fold Level 9', command: 'editor:fold-at-indent-level-9' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
label: '&View'
|
||||||
|
submenu: [
|
||||||
|
{ label: '&Reload', command: 'window:reload' }
|
||||||
|
{ label: 'Toggle &Full Screen', command: 'window:toggle-full-screen' }
|
||||||
|
{
|
||||||
|
label: 'Developer'
|
||||||
|
submenu: [
|
||||||
|
{ label: 'Open In &Dev Mode...', command: 'application:open-dev' }
|
||||||
|
{ label: 'Run &Atom Specs', command: 'application:run-all-specs' }
|
||||||
|
{ label: 'Run Package &Specs', command: 'window:run-package-specs' }
|
||||||
|
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrap' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
label: '&Selection'
|
||||||
|
submenu: [
|
||||||
|
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
|
||||||
|
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'Select to &Top', command: 'core:select-to-top' }
|
||||||
|
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'Select &Line', command: 'editor:select-line' }
|
||||||
|
{ label: 'Select &Word', command: 'editor:select-word' }
|
||||||
|
{ label: 'Select to Beginning of W&ord', command: 'editor:select-to-beginning-of-word' }
|
||||||
|
{ label: 'Select to Beginning of L&ine', command: 'editor:select-to-beginning-of-line' }
|
||||||
|
{ label: 'Select to First &Character of Line', command: 'editor:select-to-first-character-of-line' }
|
||||||
|
{ label: 'Select to End of Wor&d', command: 'editor:select-to-end-of-word' }
|
||||||
|
{ label: 'Select to End of Lin&e', command: 'editor:select-to-end-of-line' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
label: '&Movement'
|
||||||
|
submenu: [
|
||||||
|
{ label: 'Move to &Top', command: 'core:move-to-top' }
|
||||||
|
{ label: 'Move to &Bottom', command: 'core:move-to-bottom' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'Move to Beginning of &Line', command: 'editor:move-to-beginning-of-line' }
|
||||||
|
{ label: 'Move to &First Character of Line', command: 'editor:move-to-first-character-of-line' }
|
||||||
|
{ label: 'Move to &End of Line', command: 'editor:move-to-end-of-line' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'Move to Beginning of &Word', command: 'editor:move-to-beginning-of-word' }
|
||||||
|
{ label: 'Move to End of Wor&d', command: 'editor:move-to-end-of-word' }
|
||||||
|
{ label: 'Move to &Next Word', command: 'editor:move-to-next-word-boundary' }
|
||||||
|
{ label: 'Move to &Previous Word', command: 'editor:move-to-previous-word-boundary' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
label: 'F&ind'
|
||||||
|
submenu: []
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
label: '&Packages'
|
||||||
|
submenu: []
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
label: '&Window'
|
||||||
|
submenu: [
|
||||||
|
{ label: 'Mi&nimize', command: 'application:minimize' }
|
||||||
|
{ label: 'Ma&ximize', command: 'application:zoom' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: 'Bring &All to Front', command: 'application:bring-all-windows-to-front' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
label: '&Help'
|
||||||
|
submenu: [
|
||||||
|
{ label: '&About Atom...', command: 'application:about' }
|
||||||
|
{ label: "VERSION", enabled: false }
|
||||||
|
{ label: "Install &update", command: 'application:install-update', visible: false }
|
||||||
|
{ type: 'separator' }
|
||||||
|
{ label: '&Documentation', command: 'application:open-documentation' }
|
||||||
|
{ label: 'Report an &Issue', command: 'application:report-issue' }
|
||||||
|
{ type: 'separator' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
24
package.json
24
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "atom",
|
"name": "atom",
|
||||||
"version": "35.0.0",
|
"version": "39.0.0",
|
||||||
"main": "./src/browser/main.js",
|
"main": "./src/browser/main.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -13,10 +13,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "0.2.6",
|
"async": "0.2.6",
|
||||||
"bootstrap": "git://github.com/twbs/bootstrap.git#v3.0.0",
|
"bootstrap": "git://github.com/twbs/bootstrap.git#v3.0.0",
|
||||||
"clear-cut": "0.1.0",
|
"clear-cut": "0.2.0",
|
||||||
"coffee-script": "1.6.3",
|
"coffee-script": "1.6.3",
|
||||||
"coffeestack": "0.6.0",
|
"coffeestack": "0.6.0",
|
||||||
"emissary": "0.9.0",
|
"emissary": "0.17.0",
|
||||||
"first-mate": "0.5.0",
|
"first-mate": "0.5.0",
|
||||||
"fs-plus": "0.9.0",
|
"fs-plus": "0.9.0",
|
||||||
"fuzzaldrin": "0.1.0",
|
"fuzzaldrin": "0.1.0",
|
||||||
@ -28,14 +28,14 @@
|
|||||||
"nslog": "0.1.0",
|
"nslog": "0.1.0",
|
||||||
"oniguruma": "0.24.0",
|
"oniguruma": "0.24.0",
|
||||||
"optimist": "0.4.0",
|
"optimist": "0.4.0",
|
||||||
"pathwatcher": "0.9.0",
|
"pathwatcher": "0.10.0",
|
||||||
"pegjs": "0.7.0",
|
"pegjs": "0.7.0",
|
||||||
"q": "0.9.7",
|
"q": "0.9.7",
|
||||||
"scandal": "0.8.0",
|
"scandal": "0.8.0",
|
||||||
"season": "0.14.0",
|
"season": "0.14.0",
|
||||||
"semver": "1.1.4",
|
"semver": "1.1.4",
|
||||||
"space-pen": "2.0.0",
|
"space-pen": "2.0.0",
|
||||||
"telepath": "0.23.0",
|
"telepath": "0.38.0",
|
||||||
"temp": "0.5.0",
|
"temp": "0.5.0",
|
||||||
"underscore-plus": "0.3.0"
|
"underscore-plus": "0.3.0"
|
||||||
},
|
},
|
||||||
@ -52,7 +52,6 @@
|
|||||||
"grunt-contrib-coffee": "~0.7.0",
|
"grunt-contrib-coffee": "~0.7.0",
|
||||||
"grunt-contrib-less": "~0.8.0",
|
"grunt-contrib-less": "~0.8.0",
|
||||||
"walkdir": "0.0.7",
|
"walkdir": "0.0.7",
|
||||||
"ws": "0.4.27",
|
|
||||||
"js-yaml": "~2.1.0",
|
"js-yaml": "~2.1.0",
|
||||||
"grunt-markdown": "~0.4.0",
|
"grunt-markdown": "~0.4.0",
|
||||||
"json-front-matter": "~0.1.3",
|
"json-front-matter": "~0.1.3",
|
||||||
@ -65,9 +64,9 @@
|
|||||||
"rimraf": "~2.2.2"
|
"rimraf": "~2.2.2"
|
||||||
},
|
},
|
||||||
"packageDependencies": {
|
"packageDependencies": {
|
||||||
"atom-light-ui": "0.6.0",
|
"atom-light-ui": "0.7.0",
|
||||||
"atom-light-syntax": "0.6.0",
|
"atom-light-syntax": "0.6.0",
|
||||||
"atom-dark-ui": "0.6.0",
|
"atom-dark-ui": "0.7.0",
|
||||||
"atom-dark-syntax": "0.6.0",
|
"atom-dark-syntax": "0.6.0",
|
||||||
"base16-tomorrow-dark-theme": "0.6.0",
|
"base16-tomorrow-dark-theme": "0.6.0",
|
||||||
"solarized-dark-syntax": "0.4.0",
|
"solarized-dark-syntax": "0.4.0",
|
||||||
@ -78,11 +77,11 @@
|
|||||||
"bookmarks": "0.10.0",
|
"bookmarks": "0.10.0",
|
||||||
"bracket-matcher": "0.11.0",
|
"bracket-matcher": "0.11.0",
|
||||||
"command-logger": "0.6.0",
|
"command-logger": "0.6.0",
|
||||||
"command-palette": "0.7.0",
|
"command-palette": "0.8.0",
|
||||||
"dev-live-reload": "0.15.0",
|
"dev-live-reload": "0.15.0",
|
||||||
"editor-stats": "0.5.0",
|
"editor-stats": "0.5.0",
|
||||||
"exception-reporting": "0.7.0",
|
"exception-reporting": "0.7.0",
|
||||||
"find-and-replace": "0.40.0",
|
"find-and-replace": "0.41.0",
|
||||||
"fuzzy-finder": "0.20.0",
|
"fuzzy-finder": "0.20.0",
|
||||||
"gists": "0.6.0",
|
"gists": "0.6.0",
|
||||||
"git-diff": "0.13.0",
|
"git-diff": "0.13.0",
|
||||||
@ -90,16 +89,17 @@
|
|||||||
"go-to-line": "0.8.0",
|
"go-to-line": "0.8.0",
|
||||||
"grammar-selector": "0.8.0",
|
"grammar-selector": "0.8.0",
|
||||||
"image-view": "0.7.0",
|
"image-view": "0.7.0",
|
||||||
|
"keybinding-resolver": "0.2.0",
|
||||||
"link": "0.7.0",
|
"link": "0.7.0",
|
||||||
"markdown-preview": "0.15.0",
|
"markdown-preview": "0.15.0",
|
||||||
"metrics": "0.11.0",
|
"metrics": "0.11.0",
|
||||||
"package-generator": "0.19.0",
|
"package-generator": "0.19.0",
|
||||||
"release-notes": "0.11.0",
|
"release-notes": "0.11.0",
|
||||||
"settings-view": "0.39.0",
|
"settings-view": "0.41.0",
|
||||||
"snippets": "0.13.0",
|
"snippets": "0.13.0",
|
||||||
"spell-check": "0.13.0",
|
"spell-check": "0.13.0",
|
||||||
"status-bar": "0.16.0",
|
"status-bar": "0.16.0",
|
||||||
"styleguide": "0.9.0",
|
"styleguide": "0.10.0",
|
||||||
"symbols-view": "0.19.0",
|
"symbols-view": "0.19.0",
|
||||||
"tabs": "0.8.0",
|
"tabs": "0.8.0",
|
||||||
"terminal": "0.16.0",
|
"terminal": "0.16.0",
|
||||||
|
@ -5,9 +5,15 @@ var path = require('path');
|
|||||||
// Executes an array of commands one by one.
|
// Executes an array of commands one by one.
|
||||||
function executeCommands(commands, done, index) {
|
function executeCommands(commands, done, index) {
|
||||||
index = (index == undefined ? 0 : index);
|
index = (index == undefined ? 0 : index);
|
||||||
if (index < commands.length)
|
if (index < commands.length) {
|
||||||
safeExec(commands[index], executeCommands.bind(this, commands, done, index + 1));
|
var command = commands[index];
|
||||||
else
|
var options = null;
|
||||||
|
if (typeof command !== 'string') {
|
||||||
|
options = command.options;
|
||||||
|
command = command.command;
|
||||||
|
}
|
||||||
|
safeExec(command, options, executeCommands.bind(this, commands, done, index + 1));
|
||||||
|
} else
|
||||||
done(null);
|
done(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,8 +27,8 @@ var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
|||||||
var commands = [
|
var commands = [
|
||||||
'git submodule --quiet sync',
|
'git submodule --quiet sync',
|
||||||
'git submodule --quiet update --recursive --init',
|
'git submodule --quiet update --recursive --init',
|
||||||
joinCommands('cd vendor/apm', 'npm install --silent .'),
|
{command: joinCommands('cd vendor/apm', 'npm install --silent .'), options: {ignoreStdout: true}},
|
||||||
'npm install --silent vendor/apm',
|
{command: 'npm install --silent vendor/apm', options: {ignoreStdout: true}},
|
||||||
echoNewLine,
|
echoNewLine,
|
||||||
'node node_modules/atom-package-manager/bin/apm clean',
|
'node node_modules/atom-package-manager/bin/apm clean',
|
||||||
'node node_modules/atom-package-manager/bin/apm install --silent',
|
'node node_modules/atom-package-manager/bin/apm install --silent',
|
||||||
|
@ -10,7 +10,7 @@ exports.safeExec = function(command, options, callback) {
|
|||||||
if (!options)
|
if (!options)
|
||||||
options = {};
|
options = {};
|
||||||
|
|
||||||
// This needed to be increase for `apm test` runs that generate tons of failures
|
// This needed to be increased for `apm test` runs that generate many failures
|
||||||
// The default is 200KB.
|
// The default is 200KB.
|
||||||
options.maxBuffer = 1024 * 1024;
|
options.maxBuffer = 1024 * 1024;
|
||||||
|
|
||||||
@ -21,7 +21,8 @@ exports.safeExec = function(command, options, callback) {
|
|||||||
callback(null);
|
callback(null);
|
||||||
});
|
});
|
||||||
child.stderr.pipe(process.stderr);
|
child.stderr.pipe(process.stderr);
|
||||||
child.stdout.pipe(process.stdout);
|
if (!options.ignoreStdout)
|
||||||
|
child.stdout.pipe(process.stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same with safeExec but call child_process.spawn instead.
|
// Same with safeExec but call child_process.spawn instead.
|
||||||
|
@ -137,28 +137,28 @@ describe "the `atom` global", ->
|
|||||||
element2 = $$ -> @div class: 'test-2'
|
element2 = $$ -> @div class: 'test-2'
|
||||||
element3 = $$ -> @div class: 'test-3'
|
element3 = $$ -> @div class: 'test-3'
|
||||||
|
|
||||||
expect(atom.keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0
|
||||||
expect(atom.keymap.bindingsForElement(element2)['ctrl-z']).toBeUndefined()
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)).toHaveLength 0
|
||||||
expect(atom.keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0
|
||||||
|
|
||||||
atom.activatePackage("package-with-keymaps")
|
atom.activatePackage("package-with-keymaps")
|
||||||
|
|
||||||
expect(atom.keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1"
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe "test-1"
|
||||||
expect(atom.keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2"
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)[0].command).toBe "test-2"
|
||||||
expect(atom.keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0
|
||||||
|
|
||||||
describe "when the metadata contains a 'keymaps' manifest", ->
|
describe "when the metadata contains a 'keymaps' manifest", ->
|
||||||
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
||||||
element1 = $$ -> @div class: 'test-1'
|
element1 = $$ -> @div class: 'test-1'
|
||||||
element3 = $$ -> @div class: 'test-3'
|
element3 = $$ -> @div class: 'test-3'
|
||||||
|
|
||||||
expect(atom.keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0
|
||||||
|
|
||||||
atom.activatePackage("package-with-keymaps-manifest")
|
atom.activatePackage("package-with-keymaps-manifest")
|
||||||
|
|
||||||
expect(atom.keymap.bindingsForElement(element1)['ctrl-z']).toBe 'keymap-1'
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe 'keymap-1'
|
||||||
expect(atom.keymap.bindingsForElement(element1)['ctrl-n']).toBe 'keymap-2'
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-n', element1)[0].command).toBe 'keymap-2'
|
||||||
expect(atom.keymap.bindingsForElement(element3)['ctrl-y']).toBeUndefined()
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-y', element3)).toHaveLength 0
|
||||||
|
|
||||||
describe "menu loading", ->
|
describe "menu loading", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@ -317,8 +317,8 @@ describe "the `atom` global", ->
|
|||||||
it "removes the package's keymaps", ->
|
it "removes the package's keymaps", ->
|
||||||
atom.activatePackage('package-with-keymaps')
|
atom.activatePackage('package-with-keymaps')
|
||||||
atom.deactivatePackage('package-with-keymaps')
|
atom.deactivatePackage('package-with-keymaps')
|
||||||
expect(atom.keymap.bindingsForElement($$ -> @div class: 'test-1')['ctrl-z']).toBeUndefined()
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-1')).toHaveLength 0
|
||||||
expect(atom.keymap.bindingsForElement($$ -> @div class: 'test-2')['ctrl-z']).toBeUndefined()
|
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-2')).toHaveLength 0
|
||||||
|
|
||||||
it "removes the package's stylesheets", ->
|
it "removes the package's stylesheets", ->
|
||||||
atom.activatePackage('package-with-stylesheets')
|
atom.activatePackage('package-with-stylesheets')
|
||||||
|
@ -158,12 +158,12 @@ describe "Config", ->
|
|||||||
expect(atom.config.get("foo.quux.y")).toBe 1
|
expect(atom.config.get("foo.quux.y")).toBe 1
|
||||||
|
|
||||||
describe ".observe(keyPath)", ->
|
describe ".observe(keyPath)", ->
|
||||||
observeHandler = null
|
[observeHandler, observeSubscription] = []
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
observeHandler = jasmine.createSpy("observeHandler")
|
observeHandler = jasmine.createSpy("observeHandler")
|
||||||
atom.config.set("foo.bar.baz", "value 1")
|
atom.config.set("foo.bar.baz", "value 1")
|
||||||
atom.config.observe "foo.bar.baz", observeHandler
|
observeSubscription = atom.config.observe "foo.bar.baz", observeHandler
|
||||||
|
|
||||||
it "fires the given callback with the current value at the keypath", ->
|
it "fires the given callback with the current value at the keypath", ->
|
||||||
expect(observeHandler).toHaveBeenCalledWith("value 1")
|
expect(observeHandler).toHaveBeenCalledWith("value 1")
|
||||||
@ -192,6 +192,12 @@ describe "Config", ->
|
|||||||
atom.config.set("foo.bar.baz", "i'm back")
|
atom.config.set("foo.bar.baz", "i'm back")
|
||||||
expect(observeHandler).toHaveBeenCalledWith("i'm back", {previous: undefined})
|
expect(observeHandler).toHaveBeenCalledWith("i'm back", {previous: undefined})
|
||||||
|
|
||||||
|
it "does not fire the callback once the observe subscription is off'ed", ->
|
||||||
|
observeHandler.reset() # clear the initial call
|
||||||
|
observeSubscription.off()
|
||||||
|
atom.config.set('foo.bar.baz', "value 2")
|
||||||
|
expect(observeHandler).not.toHaveBeenCalled()
|
||||||
|
|
||||||
describe ".initializeConfigDirectory()", ->
|
describe ".initializeConfigDirectory()", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
atom.config.configDirPath = dotAtomPath
|
atom.config.configDirPath = dotAtomPath
|
||||||
|
@ -2525,6 +2525,13 @@ describe "EditSession", ->
|
|||||||
editSession.insertText("foo", indentBasis: 5)
|
editSession.insertText("foo", indentBasis: 5)
|
||||||
expect(editSession.lineForBufferRow(5)).toBe " foo current = items.shift();"
|
expect(editSession.lineForBufferRow(5)).toBe " foo current = items.shift();"
|
||||||
|
|
||||||
|
it "does not adjust the whitespace if there are preceding characters", ->
|
||||||
|
copyText(" foo")
|
||||||
|
editSession.setCursorBufferPosition([5, 30])
|
||||||
|
editSession.pasteText()
|
||||||
|
|
||||||
|
expect(editSession.lineForBufferRow(5)).toBe " current = items.shift(); foo"
|
||||||
|
|
||||||
describe "when the inserted text contains newlines", ->
|
describe "when the inserted text contains newlines", ->
|
||||||
describe "when the cursor is preceded only by whitespace characters", ->
|
describe "when the cursor is preceded only by whitespace characters", ->
|
||||||
it "normalizes indented lines to the cursor's current indentation level", ->
|
it "normalizes indented lines to the cursor's current indentation level", ->
|
||||||
|
@ -2719,7 +2719,7 @@ describe "Editor", ->
|
|||||||
|
|
||||||
describe "when the escape key is pressed on the editor", ->
|
describe "when the escape key is pressed on the editor", ->
|
||||||
it "clears multiple selections if there are any, and otherwise allows other bindings to be handled", ->
|
it "clears multiple selections if there are any, and otherwise allows other bindings to be handled", ->
|
||||||
keymap.bindKeys '.editor', 'escape': 'test-event'
|
keymap.bindKeys 'name', '.editor', 'escape': 'test-event'
|
||||||
testEventHandler = jasmine.createSpy("testEventHandler")
|
testEventHandler = jasmine.createSpy("testEventHandler")
|
||||||
|
|
||||||
editor.on 'test-event', testEventHandler
|
editor.on 'test-event', testEventHandler
|
||||||
|
@ -257,7 +257,8 @@ describe "Git", ->
|
|||||||
|
|
||||||
it "subscribes to all the serialized buffers in the project", ->
|
it "subscribes to all the serialized buffers in the project", ->
|
||||||
project.openSync('sample.js')
|
project.openSync('sample.js')
|
||||||
project2 = deserialize(project.serialize())
|
#TODO Replace with atom.replicate().project when Atom is a telepath model
|
||||||
|
project2 = atom.replicate().get('project')
|
||||||
buffer = project2.getBuffers()[0]
|
buffer = project2.getBuffers()[0]
|
||||||
|
|
||||||
waitsFor ->
|
waitsFor ->
|
||||||
|
@ -21,25 +21,19 @@ describe "Keymap", ->
|
|||||||
describe ".handleKeyEvent(event)", ->
|
describe ".handleKeyEvent(event)", ->
|
||||||
deleteCharHandler = null
|
deleteCharHandler = null
|
||||||
insertCharHandler = null
|
insertCharHandler = null
|
||||||
|
metaZHandler = null
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
keymap.bindKeys '.command-mode', 'x': 'deleteChar'
|
keymap.bindKeys 'name', '.command-mode', 'x': 'deleteChar'
|
||||||
keymap.bindKeys '.insert-mode', 'x': 'insertChar'
|
keymap.bindKeys 'name', '.insert-mode', 'x': 'insertChar'
|
||||||
|
keymap.bindKeys 'name', '.command-mode', 'meta-z': 'metaZPressed'
|
||||||
|
|
||||||
deleteCharHandler = jasmine.createSpy('deleteCharHandler')
|
deleteCharHandler = jasmine.createSpy('deleteCharHandler')
|
||||||
insertCharHandler = jasmine.createSpy('insertCharHandler')
|
insertCharHandler = jasmine.createSpy('insertCharHandler')
|
||||||
|
metaZHandler = jasmine.createSpy('metaZHandler')
|
||||||
fragment.on 'deleteChar', deleteCharHandler
|
fragment.on 'deleteChar', deleteCharHandler
|
||||||
fragment.on 'insertChar', insertCharHandler
|
fragment.on 'insertChar', insertCharHandler
|
||||||
|
fragment.on 'metaZPressed', metaZHandler
|
||||||
it "adds a 'keystrokes' string to the event object", ->
|
|
||||||
event = keydownEvent('x', altKey: true, metaKey: true)
|
|
||||||
keymap.handleKeyEvent(event)
|
|
||||||
expect(event.keystrokes).toBe 'alt-meta-x'
|
|
||||||
|
|
||||||
event = keydownEvent(',', metaKey: true)
|
|
||||||
event.which = 188
|
|
||||||
keymap.handleKeyEvent(event)
|
|
||||||
expect(event.keystrokes).toBe 'meta-,'
|
|
||||||
|
|
||||||
describe "when no binding matches the event's keystroke", ->
|
describe "when no binding matches the event's keystroke", ->
|
||||||
it "does not return false so the event continues to propagate", ->
|
it "does not return false so the event continues to propagate", ->
|
||||||
@ -47,10 +41,11 @@ describe "Keymap", ->
|
|||||||
|
|
||||||
describe "when a non-English keyboard language is used", ->
|
describe "when a non-English keyboard language is used", ->
|
||||||
it "uses the physical character pressed instead of the character it maps to in the current language", ->
|
it "uses the physical character pressed instead of the character it maps to in the current language", ->
|
||||||
event = keydownEvent('U+03B6', metaKey: true) # This is the 'z' key using the Greek keyboard layout
|
event = keydownEvent('U+03B6', metaKey: true, which: 122, target: fragment[0]) # This is the 'z' key using the Greek keyboard layout
|
||||||
event.which = 122
|
result = keymap.handleKeyEvent(event)
|
||||||
keymap.handleKeyEvent(event)
|
|
||||||
expect(event.keystrokes).toBe 'meta-z'
|
expect(result).toBe(false)
|
||||||
|
expect(metaZHandler).toHaveBeenCalled()
|
||||||
|
|
||||||
describe "when at least one binding fully matches the event's keystroke", ->
|
describe "when at least one binding fully matches the event's keystroke", ->
|
||||||
describe "when the event's target node matches a selector with a matching binding", ->
|
describe "when the event's target node matches a selector with a matching binding", ->
|
||||||
@ -67,9 +62,6 @@ describe "Keymap", ->
|
|||||||
keymap.handleKeyEvent(event)
|
keymap.handleKeyEvent(event)
|
||||||
expect(deleteCharHandler).not.toHaveBeenCalled()
|
expect(deleteCharHandler).not.toHaveBeenCalled()
|
||||||
expect(insertCharHandler).toHaveBeenCalled()
|
expect(insertCharHandler).toHaveBeenCalled()
|
||||||
commandEvent = insertCharHandler.argsForCall[0][0]
|
|
||||||
expect(commandEvent.keyEvent).toBe event
|
|
||||||
expect(event.keystrokes).toBe 'x'
|
|
||||||
|
|
||||||
describe "when the event's target node *descends* from a selector with a matching binding", ->
|
describe "when the event's target node *descends* from a selector with a matching binding", ->
|
||||||
it "triggers the command event associated with that binding on the target node and returns false", ->
|
it "triggers the command event associated with that binding on the target node and returns false", ->
|
||||||
@ -88,7 +80,7 @@ describe "Keymap", ->
|
|||||||
|
|
||||||
describe "when the event's target node descends from multiple nodes that match selectors with a binding", ->
|
describe "when the event's target node descends from multiple nodes that match selectors with a binding", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
keymap.bindKeys '.child-node', 'x': 'foo'
|
keymap.bindKeys 'name', '.child-node', 'x': 'foo'
|
||||||
|
|
||||||
it "only triggers bindings on selectors associated with the closest ancestor node", ->
|
it "only triggers bindings on selectors associated with the closest ancestor node", ->
|
||||||
fooHandler = jasmine.createSpy 'fooHandler'
|
fooHandler = jasmine.createSpy 'fooHandler'
|
||||||
@ -125,10 +117,10 @@ describe "Keymap", ->
|
|||||||
describe "when the event bubbles to a node that matches multiple selectors", ->
|
describe "when the event bubbles to a node that matches multiple selectors", ->
|
||||||
describe "when the matching selectors differ in specificity", ->
|
describe "when the matching selectors differ in specificity", ->
|
||||||
it "triggers the binding for the most specific selector", ->
|
it "triggers the binding for the most specific selector", ->
|
||||||
keymap.bindKeys 'div .child-node', 'x': 'foo'
|
keymap.bindKeys 'name', 'div .child-node', 'x': 'foo'
|
||||||
keymap.bindKeys '.command-mode .child-node !important', 'x': 'baz'
|
keymap.bindKeys 'name', '.command-mode .child-node !important', 'x': 'baz'
|
||||||
keymap.bindKeys '.command-mode .child-node', 'x': 'quux'
|
keymap.bindKeys 'name', '.command-mode .child-node', 'x': 'quux'
|
||||||
keymap.bindKeys '.child-node', 'x': 'bar'
|
keymap.bindKeys 'name', '.child-node', 'x': 'bar'
|
||||||
|
|
||||||
fooHandler = jasmine.createSpy 'fooHandler'
|
fooHandler = jasmine.createSpy 'fooHandler'
|
||||||
barHandler = jasmine.createSpy 'barHandler'
|
barHandler = jasmine.createSpy 'barHandler'
|
||||||
@ -146,8 +138,8 @@ describe "Keymap", ->
|
|||||||
|
|
||||||
describe "when the matching selectors have the same specificity", ->
|
describe "when the matching selectors have the same specificity", ->
|
||||||
it "triggers the bindings for the most recently declared selector", ->
|
it "triggers the bindings for the most recently declared selector", ->
|
||||||
keymap.bindKeys '.child-node', 'x': 'foo', 'y': 'baz'
|
keymap.bindKeys 'name', '.child-node', 'x': 'foo', 'y': 'baz'
|
||||||
keymap.bindKeys '.child-node', 'x': 'bar'
|
keymap.bindKeys 'name', '.child-node', 'x': 'bar'
|
||||||
|
|
||||||
fooHandler = jasmine.createSpy 'fooHandler'
|
fooHandler = jasmine.createSpy 'fooHandler'
|
||||||
barHandler = jasmine.createSpy 'barHandler'
|
barHandler = jasmine.createSpy 'barHandler'
|
||||||
@ -168,7 +160,8 @@ describe "Keymap", ->
|
|||||||
describe "when the event's target is the document body", ->
|
describe "when the event's target is the document body", ->
|
||||||
it "triggers the mapped event on the rootView", ->
|
it "triggers the mapped event on the rootView", ->
|
||||||
window.rootView = new RootView
|
window.rootView = new RootView
|
||||||
keymap.bindKeys 'body', 'x': 'foo'
|
rootView.attachToDom()
|
||||||
|
keymap.bindKeys 'name', 'body', 'x': 'foo'
|
||||||
fooHandler = jasmine.createSpy("fooHandler")
|
fooHandler = jasmine.createSpy("fooHandler")
|
||||||
rootView.on 'foo', fooHandler
|
rootView.on 'foo', fooHandler
|
||||||
|
|
||||||
@ -180,7 +173,7 @@ describe "Keymap", ->
|
|||||||
|
|
||||||
describe "when the event matches a 'native!' binding", ->
|
describe "when the event matches a 'native!' binding", ->
|
||||||
it "returns true, allowing the browser's native key handling to process the event", ->
|
it "returns true, allowing the browser's native key handling to process the event", ->
|
||||||
keymap.bindKeys '.grandchild-node', 'x': 'native!'
|
keymap.bindKeys 'name', '.grandchild-node', 'x': 'native!'
|
||||||
nativeHandler = jasmine.createSpy("nativeHandler")
|
nativeHandler = jasmine.createSpy("nativeHandler")
|
||||||
fragment.on 'native!', nativeHandler
|
fragment.on 'native!', nativeHandler
|
||||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment.find('.grandchild-node')[0]))).toBe true
|
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment.find('.grandchild-node')[0]))).toBe true
|
||||||
@ -190,7 +183,7 @@ describe "Keymap", ->
|
|||||||
[quitHandler, closeOtherWindowsHandler] = []
|
[quitHandler, closeOtherWindowsHandler] = []
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
keymap.bindKeys "*",
|
keymap.bindKeys 'name', "*",
|
||||||
'ctrl-x ctrl-c': 'quit'
|
'ctrl-x ctrl-c': 'quit'
|
||||||
'ctrl-x 1': 'close-other-windows'
|
'ctrl-x 1': 'close-other-windows'
|
||||||
|
|
||||||
@ -220,7 +213,7 @@ describe "Keymap", ->
|
|||||||
expect(closeOtherWindowsHandler).toHaveBeenCalled()
|
expect(closeOtherWindowsHandler).toHaveBeenCalled()
|
||||||
|
|
||||||
describe "when a second keystroke added to the first doesn't match any bindings", ->
|
describe "when a second keystroke added to the first doesn't match any bindings", ->
|
||||||
it "clears the queued keystrokes without triggering any events", ->
|
it "clears the queued keystroke without triggering any events", ->
|
||||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment[0], ctrlKey: true))).toBe false
|
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment[0], ctrlKey: true))).toBe false
|
||||||
expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).toBe false
|
expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).toBe false
|
||||||
expect(quitHandler).not.toHaveBeenCalled()
|
expect(quitHandler).not.toHaveBeenCalled()
|
||||||
@ -230,7 +223,7 @@ describe "Keymap", ->
|
|||||||
|
|
||||||
describe "when the event's target node descends from multiple nodes that match selectors with a partial binding match", ->
|
describe "when the event's target node descends from multiple nodes that match selectors with a partial binding match", ->
|
||||||
it "allows any of the bindings to be triggered upon a second keystroke, favoring the most specific selector", ->
|
it "allows any of the bindings to be triggered upon a second keystroke, favoring the most specific selector", ->
|
||||||
keymap.bindKeys ".grandchild-node", 'ctrl-x ctrl-c': 'more-specific-quit'
|
keymap.bindKeys 'name', ".grandchild-node", 'ctrl-x ctrl-c': 'more-specific-quit'
|
||||||
grandchildNode = fragment.find('.grandchild-node')[0]
|
grandchildNode = fragment.find('.grandchild-node')[0]
|
||||||
moreSpecificQuitHandler = jasmine.createSpy('moreSpecificQuitHandler')
|
moreSpecificQuitHandler = jasmine.createSpy('moreSpecificQuitHandler')
|
||||||
fragment.on 'more-specific-quit', moreSpecificQuitHandler
|
fragment.on 'more-specific-quit', moreSpecificQuitHandler
|
||||||
@ -254,39 +247,17 @@ describe "Keymap", ->
|
|||||||
describe "when there is a complete binding with a more specific selector", ->
|
describe "when there is a complete binding with a more specific selector", ->
|
||||||
it "favors the more specific complete match", ->
|
it "favors the more specific complete match", ->
|
||||||
|
|
||||||
describe "when a tab keystroke does not match any bindings", ->
|
describe ".bindKeys(name, selector, bindings)", ->
|
||||||
it "returns false to prevent the browser from transferring focus", ->
|
|
||||||
expect(keymap.handleKeyEvent(keydownEvent('U+0009', target: fragment[0]))).toBe false
|
|
||||||
|
|
||||||
describe ".keystrokesByCommandForSelector(selector)", ->
|
|
||||||
it "returns a hash of all commands and their keybindings", ->
|
|
||||||
keymap.bindKeys 'body', 'a': 'letter'
|
|
||||||
keymap.bindKeys '.editor', 'b': 'letter'
|
|
||||||
keymap.bindKeys '.editor', '1': 'number'
|
|
||||||
keymap.bindKeys '.editor', 'meta-alt-1': 'number-with-modifiers'
|
|
||||||
|
|
||||||
expect(keymap.keystrokesByCommandForSelector()).toEqual
|
|
||||||
'letter': ['b', 'a']
|
|
||||||
'number': ['1']
|
|
||||||
'number-with-modifiers': ['alt-meta-1']
|
|
||||||
|
|
||||||
expect(keymap.keystrokesByCommandForSelector('.editor')).toEqual
|
|
||||||
'letter': ['b']
|
|
||||||
'number': ['1']
|
|
||||||
'number-with-modifiers': ['alt-meta-1']
|
|
||||||
|
|
||||||
|
|
||||||
describe ".bindKeys(selector, bindings)", ->
|
|
||||||
it "normalizes the key patterns in the hash to put the modifiers in alphabetical order", ->
|
it "normalizes the key patterns in the hash to put the modifiers in alphabetical order", ->
|
||||||
fooHandler = jasmine.createSpy('fooHandler')
|
fooHandler = jasmine.createSpy('fooHandler')
|
||||||
fragment.on 'foo', fooHandler
|
fragment.on 'foo', fooHandler
|
||||||
keymap.bindKeys '*', 'ctrl-alt-delete': 'foo'
|
keymap.bindKeys 'name', '*', 'ctrl-alt-delete': 'foo'
|
||||||
result = keymap.handleKeyEvent(keydownEvent('delete', ctrlKey: true, altKey: true, target: fragment[0]))
|
result = keymap.handleKeyEvent(keydownEvent('delete', ctrlKey: true, altKey: true, target: fragment[0]))
|
||||||
expect(result).toBe(false)
|
expect(result).toBe(false)
|
||||||
expect(fooHandler).toHaveBeenCalled()
|
expect(fooHandler).toHaveBeenCalled()
|
||||||
|
|
||||||
fooHandler.reset()
|
fooHandler.reset()
|
||||||
keymap.bindKeys '*', 'ctrl-alt--': 'foo'
|
keymap.bindKeys 'name', '*', 'ctrl-alt--': 'foo'
|
||||||
result = keymap.handleKeyEvent(keydownEvent('-', ctrlKey: true, altKey: true, target: fragment[0]))
|
result = keymap.handleKeyEvent(keydownEvent('-', ctrlKey: true, altKey: true, target: fragment[0]))
|
||||||
expect(result).toBe(false)
|
expect(result).toBe(false)
|
||||||
expect(fooHandler).toHaveBeenCalled()
|
expect(fooHandler).toHaveBeenCalled()
|
||||||
@ -299,15 +270,17 @@ describe "Keymap", ->
|
|||||||
'.brown':
|
'.brown':
|
||||||
'ctrl-h': 'harvest'
|
'ctrl-h': 'harvest'
|
||||||
|
|
||||||
expect(keymap.bindingsForElement($$ -> @div class: 'green')).toEqual { 'ctrl-c': 'cultivate' }
|
keymap.add 'medical',
|
||||||
expect(keymap.bindingsForElement($$ -> @div class: 'brown')).toEqual { 'ctrl-h': 'harvest' }
|
'.green':
|
||||||
|
'ctrl-v': 'vomit'
|
||||||
|
|
||||||
|
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'green')).toHaveLength 2
|
||||||
|
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'brown')).toHaveLength 1
|
||||||
|
|
||||||
keymap.remove('nature')
|
keymap.remove('nature')
|
||||||
|
|
||||||
expect(keymap.bindingsForElement($$ -> @div class: 'green')).toEqual {}
|
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'green')).toHaveLength 1
|
||||||
expect(keymap.bindingsForElement($$ -> @div class: 'brown')).toEqual {}
|
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'brown')).toEqual []
|
||||||
expect(keymap.bindingSetsByFirstKeystroke['ctrl-c']).toEqual []
|
|
||||||
expect(keymap.bindingSetsByFirstKeystroke['ctrl-h']).toEqual []
|
|
||||||
|
|
||||||
describe ".keystrokeStringForEvent(event)", ->
|
describe ".keystrokeStringForEvent(event)", ->
|
||||||
describe "when no modifiers are pressed", ->
|
describe "when no modifiers are pressed", ->
|
||||||
@ -332,54 +305,22 @@ describe "Keymap", ->
|
|||||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left', shiftKey: true))).toBe 'shift-left'
|
expect(keymap.keystrokeStringForEvent(keydownEvent('left', shiftKey: true))).toBe 'shift-left'
|
||||||
expect(keymap.keystrokeStringForEvent(keydownEvent('Left', shiftKey: true))).toBe 'shift-left'
|
expect(keymap.keystrokeStringForEvent(keydownEvent('Left', shiftKey: true))).toBe 'shift-left'
|
||||||
|
|
||||||
describe ".bindingsForElement(element)", ->
|
describe ".keyBindingsMatchingElement(element)", ->
|
||||||
it "returns the matching bindings for the element", ->
|
it "returns the matching bindings for the element", ->
|
||||||
keymap.bindKeys '.command-mode', 'c': 'c'
|
keymap.bindKeys 'name', '.command-mode', 'c': 'c'
|
||||||
keymap.bindKeys '.grandchild-node', 'g': 'g'
|
keymap.bindKeys 'name', '.grandchild-node', 'g': 'g'
|
||||||
|
|
||||||
bindings = keymap.bindingsForElement(fragment.find('.grandchild-node'))
|
bindings = keymap.keyBindingsMatchingElement(fragment.find('.grandchild-node'))
|
||||||
expect(Object.keys(bindings).length).toBe 2
|
expect(bindings).toHaveLength 2
|
||||||
expect(bindings['c']).toEqual "c"
|
expect(bindings[0].command).toEqual "g"
|
||||||
expect(bindings['g']).toEqual "g"
|
expect(bindings[1].command).toEqual "c"
|
||||||
|
|
||||||
describe "when multiple bindings match a keystroke", ->
|
describe "when multiple bindings match a keystroke", ->
|
||||||
it "only returns bindings that match the most specific selector", ->
|
it "only returns bindings that match the most specific selector", ->
|
||||||
keymap.bindKeys '.command-mode', 'g': 'command-mode'
|
keymap.bindKeys 'name', '.command-mode', 'g': 'command-mode'
|
||||||
keymap.bindKeys '.command-mode .grandchild-node', 'g': 'command-and-grandchild-node'
|
keymap.bindKeys 'name', '.command-mode .grandchild-node', 'g': 'command-and-grandchild-node'
|
||||||
keymap.bindKeys '.grandchild-node', 'g': 'grandchild-node'
|
keymap.bindKeys 'name', '.grandchild-node', 'g': 'grandchild-node'
|
||||||
|
|
||||||
bindings = keymap.bindingsForElement(fragment.find('.grandchild-node'))
|
bindings = keymap.keyBindingsMatchingElement(fragment.find('.grandchild-node'))
|
||||||
expect(Object.keys(bindings).length).toBe 1
|
expect(bindings).toHaveLength 3
|
||||||
expect(bindings['g']).toEqual "command-and-grandchild-node"
|
expect(bindings[0].command).toEqual "command-and-grandchild-node"
|
||||||
|
|
||||||
describe ".getAllKeyMappings", ->
|
|
||||||
it "returns the all bindings", ->
|
|
||||||
keymap.bindKeys path.join('~', '.atom', 'packages', 'dummy', 'keymaps', 'a.cson'), '.command-mode', 'k': 'c'
|
|
||||||
|
|
||||||
mappings = keymap.getAllKeyMappings()
|
|
||||||
expect(mappings.length).toBe 1
|
|
||||||
expect(mappings[0].source).toEqual 'dummy'
|
|
||||||
expect(mappings[0].keystrokes).toEqual 'k'
|
|
||||||
expect(mappings[0].command).toEqual 'c'
|
|
||||||
expect(mappings[0].selector).toEqual '.command-mode'
|
|
||||||
|
|
||||||
describe ".determineSource", ->
|
|
||||||
describe "for a package", ->
|
|
||||||
it "returns '<package-name>'", ->
|
|
||||||
expect(keymap.determineSource(path.join('~', '.atom', 'packages', 'dummy', 'keymaps', 'a.cson'))).toEqual 'dummy'
|
|
||||||
|
|
||||||
describe "for a linked package", ->
|
|
||||||
it "returns '<package-name>'", ->
|
|
||||||
expect(keymap.determineSource(path.join('Users', 'john', 'github', 'dummy', 'keymaps', 'a.cson'))).toEqual 'dummy'
|
|
||||||
|
|
||||||
describe "for a user defined keymap", ->
|
|
||||||
it "returns 'User'", ->
|
|
||||||
expect(keymap.determineSource(path.join('~', '.atom', 'keymaps', 'a.cson'))).toEqual 'User'
|
|
||||||
|
|
||||||
describe "for a core keymap", ->
|
|
||||||
it "returns 'Core'", ->
|
|
||||||
expect(keymap.determineSource(path.join('Applications', 'Atom.app', '..', 'node_modules', 'dummy', 'keymaps', 'a.cson'))).toEqual 'Core'
|
|
||||||
|
|
||||||
describe "for a linked core keymap", ->
|
|
||||||
it "returns 'Core'", ->
|
|
||||||
expect(keymap.determineSource(path.join('Users', 'john', 'github', 'atom', 'keymaps', 'a.cson'))).toEqual 'Core'
|
|
||||||
|
@ -699,11 +699,11 @@ describe "Pane", ->
|
|||||||
|
|
||||||
it "focuses the pane after attach only if had focus when serialized", ->
|
it "focuses the pane after attach only if had focus when serialized", ->
|
||||||
reloadContainer = ->
|
reloadContainer = ->
|
||||||
projectState = project.serialize()
|
projectReplica = atom.replicate().get('project')
|
||||||
containerState = container.serialize()
|
containerState = container.serialize()
|
||||||
container.remove()
|
container.remove()
|
||||||
project.destroy()
|
project.destroy()
|
||||||
window.project = deserialize(projectState)
|
window.project = projectReplica
|
||||||
container = deserialize(containerState)
|
container = deserialize(containerState)
|
||||||
pane = container.getRoot()
|
pane = container.getRoot()
|
||||||
container.attachToDom()
|
container.attachToDom()
|
||||||
|
@ -19,10 +19,21 @@ describe "Project", ->
|
|||||||
it "destroys unretained buffers and does not include them in the serialized state", ->
|
it "destroys unretained buffers and does not include them in the serialized state", ->
|
||||||
project.bufferForPathSync('a')
|
project.bufferForPathSync('a')
|
||||||
expect(project.getBuffers().length).toBe 1
|
expect(project.getBuffers().length).toBe 1
|
||||||
deserializedProject = deserialize(project.serialize())
|
project.getState().serializeForPersistence()
|
||||||
|
deserializedProject = atom.replicate().get('project')
|
||||||
expect(deserializedProject.getBuffers().length).toBe 0
|
expect(deserializedProject.getBuffers().length).toBe 0
|
||||||
expect(project.getBuffers().length).toBe 0
|
expect(project.getBuffers().length).toBe 0
|
||||||
|
|
||||||
|
it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", ->
|
||||||
|
project.openSync('a')
|
||||||
|
expect(project.getBuffers().length).toBe 1
|
||||||
|
project.getState().serializeForPersistence()
|
||||||
|
deserializedProject = atom.replicate().get('project')
|
||||||
|
|
||||||
|
expect(deserializedProject.getBuffers().length).toBe 1
|
||||||
|
deserializedProject.getBuffers()[0].destroy()
|
||||||
|
expect(deserializedProject.getBuffers().length).toBe 0
|
||||||
|
|
||||||
describe "when an edit session is destroyed", ->
|
describe "when an edit session is destroyed", ->
|
||||||
it "removes edit session and calls destroy on buffer (if buffer is not referenced by other edit sessions)", ->
|
it "removes edit session and calls destroy on buffer (if buffer is not referenced by other edit sessions)", ->
|
||||||
editSession = project.openSync("a")
|
editSession = project.openSync("a")
|
||||||
|
@ -20,10 +20,11 @@ describe "RootView", ->
|
|||||||
|
|
||||||
refreshRootViewAndProject = ->
|
refreshRootViewAndProject = ->
|
||||||
rootViewState = rootView.serialize()
|
rootViewState = rootView.serialize()
|
||||||
projectState = project.serialize()
|
project.getState().serializeForPersistence()
|
||||||
|
project2 = atom.replicate().get('project')
|
||||||
rootView.remove()
|
rootView.remove()
|
||||||
project.destroy()
|
project.destroy()
|
||||||
window.project = deserialize(projectState)
|
window.project = project2
|
||||||
window.rootView = deserialize(rootViewState)
|
window.rootView = deserialize(rootViewState)
|
||||||
rootView.attachToDom()
|
rootView.attachToDom()
|
||||||
|
|
||||||
@ -136,7 +137,7 @@ describe "RootView", ->
|
|||||||
commandHandler = jasmine.createSpy('commandHandler')
|
commandHandler = jasmine.createSpy('commandHandler')
|
||||||
rootView.on('foo-command', commandHandler)
|
rootView.on('foo-command', commandHandler)
|
||||||
|
|
||||||
atom.keymap.bindKeys('*', 'x': 'foo-command')
|
atom.keymap.bindKeys('name', '*', 'x': 'foo-command')
|
||||||
|
|
||||||
describe "when a keydown event is triggered in the RootView", ->
|
describe "when a keydown event is triggered in the RootView", ->
|
||||||
it "triggers matching keybindings for that event", ->
|
it "triggers matching keybindings for that event", ->
|
||||||
|
@ -4,7 +4,7 @@ describe "Selection", ->
|
|||||||
[buffer, editSession, selection] = []
|
[buffer, editSession, selection] = []
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
buffer = project.buildBufferSync('sample.js')
|
buffer = project.bufferForPathSync('sample.js')
|
||||||
editSession = new EditSession(buffer: buffer, tabLength: 2)
|
editSession = new EditSession(buffer: buffer, tabLength: 2)
|
||||||
selection = editSession.getSelection()
|
selection = editSession.getSelection()
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ atom.themes.requireStylesheet '../static/jasmine'
|
|||||||
fixturePackagesPath = path.resolve(__dirname, './fixtures/packages')
|
fixturePackagesPath = path.resolve(__dirname, './fixtures/packages')
|
||||||
atom.packages.packageDirPaths.unshift(fixturePackagesPath)
|
atom.packages.packageDirPaths.unshift(fixturePackagesPath)
|
||||||
atom.keymap.loadBundledKeymaps()
|
atom.keymap.loadBundledKeymaps()
|
||||||
[bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = []
|
keyBindingsToRestore = atom.keymap.getKeyBindings()
|
||||||
|
|
||||||
$(window).on 'core:close', -> window.close()
|
$(window).on 'core:close', -> window.close()
|
||||||
$(window).on 'unload', ->
|
$(window).on 'unload', ->
|
||||||
@ -47,11 +47,10 @@ if specDirectory = atom.getLoadSettings().specDirectory
|
|||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
$.fx.off = true
|
$.fx.off = true
|
||||||
if specProjectPath
|
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
|
||||||
atom.project = new Project(specProjectPath)
|
atom.project = atom.getWindowState().set('project', new Project(path: projectPath))
|
||||||
else
|
|
||||||
atom.project = new Project(path.join(@specDirectory, 'fixtures'))
|
|
||||||
window.project = atom.project
|
window.project = atom.project
|
||||||
|
atom.keymap.keyBindings = _.clone(keyBindingsToRestore)
|
||||||
|
|
||||||
window.resetTimeouts()
|
window.resetTimeouts()
|
||||||
atom.packages.packageStates = {}
|
atom.packages.packageStates = {}
|
||||||
@ -66,10 +65,6 @@ beforeEach ->
|
|||||||
resolvePackagePath(packageName)
|
resolvePackagePath(packageName)
|
||||||
resolvePackagePath = _.bind(spy.originalValue, atom.packages)
|
resolvePackagePath = _.bind(spy.originalValue, atom.packages)
|
||||||
|
|
||||||
# used to reset keymap after each spec
|
|
||||||
bindingSetsToRestore = _.clone(atom.keymap.bindingSets)
|
|
||||||
bindingSetsByFirstKeystrokeToRestore = _.clone(atom.keymap.bindingSetsByFirstKeystroke)
|
|
||||||
|
|
||||||
# prevent specs from modifying Atom's menus
|
# prevent specs from modifying Atom's menus
|
||||||
spyOn(atom.menu, 'sendToBrowserProcess')
|
spyOn(atom.menu, 'sendToBrowserProcess')
|
||||||
|
|
||||||
@ -108,8 +103,6 @@ beforeEach ->
|
|||||||
addCustomMatchers(this)
|
addCustomMatchers(this)
|
||||||
|
|
||||||
afterEach ->
|
afterEach ->
|
||||||
atom.keymap.bindingSets = bindingSetsToRestore
|
|
||||||
atom.keymap.bindingSetsByFirstKeystroke = bindingSetsByFirstKeystrokeToRestore
|
|
||||||
atom.deactivatePackages()
|
atom.deactivatePackages()
|
||||||
atom.menu.template = []
|
atom.menu.template = []
|
||||||
|
|
||||||
|
@ -1,54 +1,53 @@
|
|||||||
measure 'spec suite require time', ->
|
{_, fs, Git} = require 'atom'
|
||||||
{_, fs, Git} = require 'atom'
|
path = require 'path'
|
||||||
path = require 'path'
|
require './spec-helper'
|
||||||
require './spec-helper'
|
|
||||||
|
|
||||||
requireSpecs = (specDirectory, specType) ->
|
requireSpecs = (specDirectory, specType) ->
|
||||||
for specFilePath in fs.listTreeSync(specDirectory) when /-spec\.coffee$/.test specFilePath
|
for specFilePath in fs.listTreeSync(specDirectory) when /-spec\.coffee$/.test specFilePath
|
||||||
require specFilePath
|
require specFilePath
|
||||||
|
|
||||||
# Set spec directory on spec for setting up the project in spec-helper
|
# Set spec directory on spec for setting up the project in spec-helper
|
||||||
setSpecDirectory(specDirectory)
|
setSpecDirectory(specDirectory)
|
||||||
|
|
||||||
setSpecField = (name, value) ->
|
setSpecField = (name, value) ->
|
||||||
specs = jasmine.getEnv().currentRunner().specs()
|
specs = jasmine.getEnv().currentRunner().specs()
|
||||||
return if specs.length is 0
|
return if specs.length is 0
|
||||||
for index in [specs.length-1..0]
|
for index in [specs.length-1..0]
|
||||||
break if specs[index][name]?
|
break if specs[index][name]?
|
||||||
specs[index][name] = value
|
specs[index][name] = value
|
||||||
|
|
||||||
setSpecType = (specType) ->
|
setSpecType = (specType) ->
|
||||||
setSpecField('specType', specType)
|
setSpecField('specType', specType)
|
||||||
|
|
||||||
setSpecDirectory = (specDirectory) ->
|
setSpecDirectory = (specDirectory) ->
|
||||||
setSpecField('specDirectory', specDirectory)
|
setSpecField('specDirectory', specDirectory)
|
||||||
|
|
||||||
runAllSpecs = ->
|
runAllSpecs = ->
|
||||||
# Only run core specs when resource path is the Atom repository
|
# Only run core specs when resource path is the Atom repository
|
||||||
if Git.exists(window.resourcePath)
|
if Git.exists(window.resourcePath)
|
||||||
requireSpecs(path.join(window.resourcePath, 'spec'))
|
requireSpecs(path.join(window.resourcePath, 'spec'))
|
||||||
setSpecType('core')
|
setSpecType('core')
|
||||||
|
|
||||||
fixturesPackagesPath = path.join(__dirname, 'fixtures', 'packages')
|
fixturesPackagesPath = path.join(__dirname, 'fixtures', 'packages')
|
||||||
packagePaths = atom.getAvailablePackageNames().map (packageName) -> atom.resolvePackagePath(packageName)
|
packagePaths = atom.getAvailablePackageNames().map (packageName) -> atom.resolvePackagePath(packageName)
|
||||||
packagePaths = _.groupBy packagePaths, (packagePath) ->
|
packagePaths = _.groupBy packagePaths, (packagePath) ->
|
||||||
if packagePath.indexOf("#{fixturesPackagesPath}#{path.sep}") is 0
|
if packagePath.indexOf("#{fixturesPackagesPath}#{path.sep}") is 0
|
||||||
'fixtures'
|
'fixtures'
|
||||||
else if packagePath.indexOf("#{window.resourcePath}#{path.sep}") is 0
|
else if packagePath.indexOf("#{window.resourcePath}#{path.sep}") is 0
|
||||||
'bundled'
|
'bundled'
|
||||||
else
|
else
|
||||||
'user'
|
'user'
|
||||||
|
|
||||||
# Run bundled package specs
|
# Run bundled package specs
|
||||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.bundled ? []
|
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.bundled ? []
|
||||||
setSpecType('bundled')
|
setSpecType('bundled')
|
||||||
|
|
||||||
# Run user package specs
|
# Run user package specs
|
||||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.user ? []
|
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.user ? []
|
||||||
setSpecType('user')
|
setSpecType('user')
|
||||||
|
|
||||||
if specDirectory = atom.getLoadSettings().specDirectory
|
if specDirectory = atom.getLoadSettings().specDirectory
|
||||||
requireSpecs(specDirectory)
|
requireSpecs(specDirectory)
|
||||||
setSpecType('user')
|
setSpecType('user')
|
||||||
else
|
else
|
||||||
runAllSpecs()
|
runAllSpecs()
|
||||||
|
@ -13,11 +13,11 @@ describe 'TextBuffer', ->
|
|||||||
buffer = project.bufferForPathSync(filePath)
|
buffer = project.bufferForPathSync(filePath)
|
||||||
|
|
||||||
afterEach ->
|
afterEach ->
|
||||||
buffer?.release()
|
buffer?.destroy()
|
||||||
|
|
||||||
describe 'constructor', ->
|
describe 'constructor', ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
buffer.release()
|
buffer.destroy()
|
||||||
buffer = null
|
buffer = null
|
||||||
|
|
||||||
describe "when given a path", ->
|
describe "when given a path", ->
|
||||||
@ -311,8 +311,8 @@ describe 'TextBuffer', ->
|
|||||||
|
|
||||||
it "returns false until the buffer is fully loaded", ->
|
it "returns false until the buffer is fully loaded", ->
|
||||||
buffer.release()
|
buffer.release()
|
||||||
filePath = temp.openSync('atom').path
|
buffer = new TextBuffer({filePath: temp.openSync('atom').path})
|
||||||
buffer = new TextBuffer({project, filePath})
|
project.addBuffer(buffer)
|
||||||
|
|
||||||
expect(buffer.isModified()).toBeFalsy()
|
expect(buffer.isModified()).toBeFalsy()
|
||||||
|
|
||||||
@ -554,35 +554,6 @@ describe 'TextBuffer', ->
|
|||||||
waitsFor ->
|
waitsFor ->
|
||||||
changeHandler.callCount > 0
|
changeHandler.callCount > 0
|
||||||
|
|
||||||
describe ".getRelativePath()", ->
|
|
||||||
[filePath, newPath, bufferToChange, eventHandler] = []
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
filePath = path.join(__dirname, "fixtures", "atom-manipulate-me")
|
|
||||||
newPath = "#{filePath}-i-moved"
|
|
||||||
fs.writeFileSync(filePath, "")
|
|
||||||
bufferToChange = project.bufferForPathSync(filePath)
|
|
||||||
eventHandler = jasmine.createSpy('eventHandler')
|
|
||||||
bufferToChange.on 'path-changed', eventHandler
|
|
||||||
|
|
||||||
afterEach ->
|
|
||||||
bufferToChange.destroy()
|
|
||||||
fs.removeSync(filePath) if fs.existsSync(filePath)
|
|
||||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
|
||||||
|
|
||||||
it "updates when the text buffer's file is moved", ->
|
|
||||||
expect(bufferToChange.getRelativePath()).toBe('atom-manipulate-me')
|
|
||||||
|
|
||||||
jasmine.unspy(window, "setTimeout")
|
|
||||||
eventHandler.reset()
|
|
||||||
fs.moveSync(filePath, newPath)
|
|
||||||
|
|
||||||
waitsFor "buffer path change", ->
|
|
||||||
eventHandler.callCount > 0
|
|
||||||
|
|
||||||
runs ->
|
|
||||||
expect(bufferToChange.getRelativePath()).toBe('atom-manipulate-me-i-moved')
|
|
||||||
|
|
||||||
describe ".getTextInRange(range)", ->
|
describe ".getTextInRange(range)", ->
|
||||||
describe "when range is empty", ->
|
describe "when range is empty", ->
|
||||||
it "returns an empty string", ->
|
it "returns an empty string", ->
|
||||||
@ -926,22 +897,28 @@ describe 'TextBuffer', ->
|
|||||||
expect(buffer.getText()).toBe "\ninitialtexthello\n1\n2\n"
|
expect(buffer.getText()).toBe "\ninitialtexthello\n1\n2\n"
|
||||||
|
|
||||||
describe "serialization", ->
|
describe "serialization", ->
|
||||||
buffer2 = null
|
[buffer2, project2] = []
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
buffer.destroy()
|
||||||
|
|
||||||
|
filePath = temp.openSync('atom').path
|
||||||
|
fs.writeFileSync(filePath, "words")
|
||||||
|
buffer = project.bufferForPathSync(filePath)
|
||||||
|
|
||||||
afterEach ->
|
afterEach ->
|
||||||
buffer2?.release()
|
buffer2?.release()
|
||||||
|
project2?.destroy()
|
||||||
|
|
||||||
describe "when the serialized buffer had no unsaved changes", ->
|
describe "when the serialized buffer had no unsaved changes", ->
|
||||||
it "loads the current contents of the file at the serialized path", ->
|
it "loads the current contents of the file at the serialized path", ->
|
||||||
expect(buffer.isModified()).toBeFalsy()
|
expect(buffer.isModified()).toBeFalsy()
|
||||||
|
|
||||||
state = buffer.serialize()
|
project2 = atom.replicate().get('project')
|
||||||
state.get('text').insertTextAtPoint([0, 0], 'simulate divergence of on-disk contents from serialized contents')
|
buffer2 = project2.getBuffers()[0]
|
||||||
|
|
||||||
buffer2 = deserialize(state, {project})
|
waitsForPromise ->
|
||||||
|
buffer2.load()
|
||||||
waitsFor ->
|
|
||||||
buffer2.cachedDiskContents
|
|
||||||
|
|
||||||
runs ->
|
runs ->
|
||||||
expect(buffer2.isModified()).toBeFalsy()
|
expect(buffer2.isModified()).toBeFalsy()
|
||||||
@ -951,18 +928,11 @@ describe 'TextBuffer', ->
|
|||||||
describe "when the serialized buffer had unsaved changes", ->
|
describe "when the serialized buffer had unsaved changes", ->
|
||||||
describe "when the disk contents were changed since serialization", ->
|
describe "when the disk contents were changed since serialization", ->
|
||||||
it "loads the disk contents instead of the previous unsaved state", ->
|
it "loads the disk contents instead of the previous unsaved state", ->
|
||||||
buffer.release()
|
|
||||||
|
|
||||||
filePath = temp.openSync('atom').path
|
|
||||||
fs.writeFileSync(filePath, "words")
|
|
||||||
{buffer} = project.openSync(filePath)
|
|
||||||
buffer.setText("BUFFER CHANGE")
|
buffer.setText("BUFFER CHANGE")
|
||||||
|
|
||||||
state = buffer.serialize()
|
|
||||||
expect(state.getObject('text')).toBe 'BUFFER CHANGE'
|
|
||||||
fs.writeFileSync(filePath, "DISK CHANGE")
|
fs.writeFileSync(filePath, "DISK CHANGE")
|
||||||
|
|
||||||
buffer2 = deserialize(state, {project})
|
project2 = atom.replicate().get('project')
|
||||||
|
buffer2 = project2.getBuffers()[0]
|
||||||
|
|
||||||
waitsFor ->
|
waitsFor ->
|
||||||
buffer2.cachedDiskContents
|
buffer2.cachedDiskContents
|
||||||
@ -976,14 +946,14 @@ describe 'TextBuffer', ->
|
|||||||
it "restores the previous unsaved state of the buffer", ->
|
it "restores the previous unsaved state of the buffer", ->
|
||||||
previousText = buffer.getText()
|
previousText = buffer.getText()
|
||||||
buffer.setText("abc")
|
buffer.setText("abc")
|
||||||
|
buffer.retain()
|
||||||
|
|
||||||
state = buffer.serialize()
|
buffer.getState().serializeForPersistence()
|
||||||
expect(state.getObject('text')).toBe 'abc'
|
project2 = atom.replicate().get('project')
|
||||||
|
buffer2 = project2.getBuffers()[0]
|
||||||
|
|
||||||
buffer2 = deserialize(state, {project})
|
waitsForPromise ->
|
||||||
|
buffer2.load()
|
||||||
waitsFor ->
|
|
||||||
buffer2.cachedDiskContents
|
|
||||||
|
|
||||||
runs ->
|
runs ->
|
||||||
expect(buffer2.getPath()).toBe(buffer.getPath())
|
expect(buffer2.getPath()).toBe(buffer.getPath())
|
||||||
@ -999,10 +969,10 @@ describe 'TextBuffer', ->
|
|||||||
buffer = project.bufferForPathSync()
|
buffer = project.bufferForPathSync()
|
||||||
buffer.setText("abc")
|
buffer.setText("abc")
|
||||||
|
|
||||||
state = buffer.serialize()
|
state = buffer.getState().clone()
|
||||||
expect(state.get('path')).toBeUndefined()
|
expect(state.get('path')).toBeUndefined()
|
||||||
expect(state.getObject('text')).toBe 'abc'
|
expect(state.getObject('text')).toBe 'abc'
|
||||||
|
|
||||||
buffer2 = deserialize(state)
|
buffer2 = project.addBuffer(new TextBuffer(state))
|
||||||
expect(buffer2.getPath()).toBeUndefined()
|
expect(buffer2.getPath()).toBeUndefined()
|
||||||
expect(buffer2.getText()).toBe("abc")
|
expect(buffer2.getText()).toBe("abc")
|
||||||
|
@ -350,7 +350,7 @@ describe "TokenizedBuffer", ->
|
|||||||
describe "when the buffer contains surrogate pairs", ->
|
describe "when the buffer contains surrogate pairs", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
atom.activatePackage('language-javascript', sync: true)
|
atom.activatePackage('language-javascript', sync: true)
|
||||||
buffer = project.buildBufferSync 'sample-with-pairs.js'
|
buffer = project.bufferForPathSync 'sample-with-pairs.js'
|
||||||
buffer.setText """
|
buffer.setText """
|
||||||
'abc\uD835\uDF97def'
|
'abc\uD835\uDF97def'
|
||||||
//\uD835\uDF97xyz
|
//\uD835\uDF97xyz
|
||||||
|
@ -126,11 +126,10 @@ class Atom
|
|||||||
|
|
||||||
deserializeProject: ->
|
deserializeProject: ->
|
||||||
Project = require './project'
|
Project = require './project'
|
||||||
state = @getWindowState()
|
@project = @getWindowState('project')
|
||||||
@project = deserialize(state.get('project'))
|
unless @project instanceof Project
|
||||||
unless @project?
|
@project = new Project(path: @getLoadSettings().initialPath)
|
||||||
@project = new Project(@getLoadSettings().initialPath)
|
@setWindowState('project', @project)
|
||||||
state.set('project', @project.getState())
|
|
||||||
|
|
||||||
deserializeRootView: ->
|
deserializeRootView: ->
|
||||||
RootView = require './root-view'
|
RootView = require './root-view'
|
||||||
@ -301,6 +300,7 @@ class Atom
|
|||||||
|
|
||||||
doc = Document.deserialize(documentState) if documentState?
|
doc = Document.deserialize(documentState) if documentState?
|
||||||
doc ?= Document.create()
|
doc ?= Document.create()
|
||||||
|
doc.registerModelClasses(require('./text-buffer'), require('./project'))
|
||||||
# TODO: Remove this when everything is using telepath models
|
# TODO: Remove this when everything is using telepath models
|
||||||
if @site?
|
if @site?
|
||||||
@site.setRootDocument(doc)
|
@site.setRootDocument(doc)
|
||||||
@ -322,6 +322,10 @@ class Atom
|
|||||||
else
|
else
|
||||||
@windowState
|
@windowState
|
||||||
|
|
||||||
|
# Private: Returns a replicated copy of the current state.
|
||||||
|
replicate: ->
|
||||||
|
@getWindowState().replicate()
|
||||||
|
|
||||||
crashMainProcess: ->
|
crashMainProcess: ->
|
||||||
remote.process.crash()
|
remote.process.crash()
|
||||||
|
|
||||||
@ -333,7 +337,7 @@ class Atom
|
|||||||
@rootView.trigger 'beep'
|
@rootView.trigger 'beep'
|
||||||
|
|
||||||
requireUserInitScript: ->
|
requireUserInitScript: ->
|
||||||
userInitScriptPath = path.join(@config.configDirPath, "user.coffee")
|
userInitScriptPath = path.join(@getConfigDirPath(), "user.coffee")
|
||||||
try
|
try
|
||||||
require userInitScriptPath if fs.isFileSync(userInitScriptPath)
|
require userInitScriptPath if fs.isFileSync(userInitScriptPath)
|
||||||
catch error
|
catch error
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
{$} = require './space-pen-extensions'
|
|
||||||
_ = require 'underscore-plus'
|
|
||||||
fs = require 'fs-plus'
|
|
||||||
{specificity} = require 'clear-cut'
|
|
||||||
PEG = require 'pegjs'
|
|
||||||
|
|
||||||
### Internal ###
|
|
||||||
|
|
||||||
module.exports =
|
|
||||||
class BindingSet
|
|
||||||
|
|
||||||
@parser: null
|
|
||||||
|
|
||||||
selector: null
|
|
||||||
commandsByKeystrokes: null
|
|
||||||
parser: null
|
|
||||||
name: null
|
|
||||||
|
|
||||||
constructor: (selector, commandsByKeystrokes, @index, @name) ->
|
|
||||||
keystrokePattern = fs.readFileSync(require.resolve('./keystroke-pattern.pegjs'), 'utf8')
|
|
||||||
BindingSet.parser ?= PEG.buildParser(keystrokePattern)
|
|
||||||
@specificity = specificity(selector)
|
|
||||||
@selector = selector.replace(/!important/g, '')
|
|
||||||
@commandsByKeystrokes = @normalizeCommandsByKeystrokes(commandsByKeystrokes)
|
|
||||||
|
|
||||||
# Private:
|
|
||||||
getName: ->
|
|
||||||
@name
|
|
||||||
|
|
||||||
# Private:
|
|
||||||
getSelector: ->
|
|
||||||
@selector
|
|
||||||
|
|
||||||
# Private:
|
|
||||||
getCommandsByKeystrokes: ->
|
|
||||||
@commandsByKeystrokes
|
|
||||||
|
|
||||||
commandForEvent: (event) ->
|
|
||||||
for keystrokes, command of @commandsByKeystrokes
|
|
||||||
return command if event.keystrokes == keystrokes
|
|
||||||
null
|
|
||||||
|
|
||||||
matchesKeystrokePrefix: (event) ->
|
|
||||||
eventKeystrokes = event.keystrokes.split(' ')
|
|
||||||
for keystrokes, command of @commandsByKeystrokes
|
|
||||||
bindingKeystrokes = keystrokes.split(' ')
|
|
||||||
continue unless eventKeystrokes.length < bindingKeystrokes.length
|
|
||||||
return true if _.isEqual(eventKeystrokes, bindingKeystrokes[0...eventKeystrokes.length])
|
|
||||||
false
|
|
||||||
|
|
||||||
normalizeCommandsByKeystrokes: (commandsByKeystrokes) ->
|
|
||||||
normalizedCommandsByKeystrokes = {}
|
|
||||||
for keystrokes, command of commandsByKeystrokes
|
|
||||||
normalizedCommandsByKeystrokes[@normalizeKeystrokes(keystrokes)] = command
|
|
||||||
normalizedCommandsByKeystrokes
|
|
||||||
|
|
||||||
normalizeKeystrokes: (keystrokes) ->
|
|
||||||
normalizedKeystrokes = keystrokes.split(/\s+/).map (keystroke) =>
|
|
||||||
@normalizeKeystroke(keystroke)
|
|
||||||
normalizedKeystrokes.join(' ')
|
|
||||||
|
|
||||||
normalizeKeystroke: (keystroke) ->
|
|
||||||
keys = BindingSet.parser.parse(keystroke)
|
|
||||||
modifiers = keys[0...-1]
|
|
||||||
modifiers.sort()
|
|
||||||
[modifiers..., _.last(keys)].join('-')
|
|
@ -22,7 +22,7 @@ class ApplicationMenu
|
|||||||
# The Object which describes the menu to display.
|
# The Object which describes the menu to display.
|
||||||
# * keystrokesByCommand:
|
# * keystrokesByCommand:
|
||||||
# An Object where the keys are commands and the values are Arrays containing
|
# An Object where the keys are commands and the values are Arrays containing
|
||||||
# the keystrokes.
|
# the keystroke.
|
||||||
update: (template, keystrokesByCommand) ->
|
update: (template, keystrokesByCommand) ->
|
||||||
@translateTemplate(template, keystrokesByCommand)
|
@translateTemplate(template, keystrokesByCommand)
|
||||||
@substituteVersion(template)
|
@substituteVersion(template)
|
||||||
@ -97,14 +97,14 @@ class ApplicationMenu
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
# Private: Combines a menu template with the appropriate keystrokes.
|
# Private: Combines a menu template with the appropriate keystroke.
|
||||||
#
|
#
|
||||||
# * template:
|
# * template:
|
||||||
# An Object conforming to atom-shell's menu api but lacking accelerator and
|
# An Object conforming to atom-shell's menu api but lacking accelerator and
|
||||||
# click properties.
|
# click properties.
|
||||||
# * keystrokesByCommand:
|
# * keystrokesByCommand:
|
||||||
# An Object where the keys are commands and the values are Arrays containing
|
# An Object where the keys are commands and the values are Arrays containing
|
||||||
# the keystrokes.
|
# the keystroke.
|
||||||
#
|
#
|
||||||
# Returns a complete menu configuration object for atom-shell's menu API.
|
# Returns a complete menu configuration object for atom-shell's menu API.
|
||||||
translateTemplate: (template, keystrokesByCommand) ->
|
translateTemplate: (template, keystrokesByCommand) ->
|
||||||
@ -123,15 +123,15 @@ class ApplicationMenu
|
|||||||
# The name of the command.
|
# The name of the command.
|
||||||
# * keystrokesByCommand:
|
# * keystrokesByCommand:
|
||||||
# An Object where the keys are commands and the values are Arrays containing
|
# An Object where the keys are commands and the values are Arrays containing
|
||||||
# the keystrokes.
|
# the keystroke.
|
||||||
#
|
#
|
||||||
# Returns a String containing the keystroke in a format that can be interpreted
|
# Returns a String containing the keystroke in a format that can be interpreted
|
||||||
# by atom shell to provide nice icons where available.
|
# by atom shell to provide nice icons where available.
|
||||||
acceleratorForCommand: (command, keystrokesByCommand) ->
|
acceleratorForCommand: (command, keystrokesByCommand) ->
|
||||||
keystroke = keystrokesByCommand[command]?[0]
|
firstKeystroke = keystrokesByCommand[command]?[0]
|
||||||
return null unless keystroke
|
return null unless firstKeystroke
|
||||||
|
|
||||||
modifiers = keystroke.split('-')
|
modifiers = firstKeystroke.split('-')
|
||||||
key = modifiers.pop()
|
key = modifiers.pop()
|
||||||
|
|
||||||
modifiers.push("Shift") if key != key.toLowerCase()
|
modifiers.push("Shift") if key != key.toLowerCase()
|
||||||
|
@ -62,7 +62,7 @@ delegate.browserMainParts.preMainMessageLoopRun = ->
|
|||||||
AtomApplication = require './atom-application'
|
AtomApplication = require './atom-application'
|
||||||
|
|
||||||
AtomApplication.open(args)
|
AtomApplication.open(args)
|
||||||
console.log("App load time: #{Date.now() - startTime}ms")
|
console.log("App load time: #{Date.now() - startTime}ms") unless args.test
|
||||||
|
|
||||||
global.devResourcePath = path.join(app.getHomeDir(), 'github', 'atom')
|
global.devResourcePath = path.join(app.getHomeDir(), 'github', 'atom')
|
||||||
|
|
||||||
|
@ -5,5 +5,5 @@ module.exports =
|
|||||||
|
|
||||||
unobserveConfig: ->
|
unobserveConfig: ->
|
||||||
if @configSubscriptions?
|
if @configSubscriptions?
|
||||||
subscription.cancel() for keyPath, subscription of @configSubscriptions
|
subscription.off() for keyPath, subscription of @configSubscriptions
|
||||||
@configSubscriptions = null
|
@configSubscriptions = null
|
||||||
|
@ -216,8 +216,7 @@ class Config
|
|||||||
callback(value, {previous})
|
callback(value, {previous})
|
||||||
|
|
||||||
eventName = "updated.#{keyPath.replace(/\./, '-')}"
|
eventName = "updated.#{keyPath.replace(/\./, '-')}"
|
||||||
subscription = { cancel: => @off eventName, updateCallback }
|
subscription = @on eventName, updateCallback
|
||||||
@on eventName, updateCallback
|
|
||||||
callback(value) if options.callNow ? true
|
callback(value) if options.callNow ? true
|
||||||
subscription
|
subscription
|
||||||
|
|
||||||
|
@ -466,3 +466,15 @@ class Cursor
|
|||||||
# Returns an {Array} of {String}s.
|
# Returns an {Array} of {String}s.
|
||||||
getScopes: ->
|
getScopes: ->
|
||||||
@editSession.scopesForBufferPosition(@getBufferPosition())
|
@editSession.scopesForBufferPosition(@getBufferPosition())
|
||||||
|
|
||||||
|
# Public: Returns true if this cursor has no non-whitespace characters before
|
||||||
|
# its current position.
|
||||||
|
hasPrecedingCharactersOnLine: ->
|
||||||
|
bufferPosition = @getBufferPosition()
|
||||||
|
line = @editSession.lineForBufferRow(bufferPosition.row)
|
||||||
|
firstCharacterColumn = line.search(/\S/)
|
||||||
|
|
||||||
|
if firstCharacterColumn is -1
|
||||||
|
false
|
||||||
|
else
|
||||||
|
bufferPosition.column > firstCharacterColumn
|
||||||
|
@ -365,7 +365,9 @@ class DisplayBuffer
|
|||||||
setTabLength: (tabLength) ->
|
setTabLength: (tabLength) ->
|
||||||
@tokenizedBuffer.setTabLength(tabLength)
|
@tokenizedBuffer.setTabLength(tabLength)
|
||||||
|
|
||||||
# Retrieves the grammar for the buffer.
|
# Get the grammar for this buffer.
|
||||||
|
#
|
||||||
|
# Returns the current {TextMateGrammar} or the {NullGrammar}.
|
||||||
getGrammar: ->
|
getGrammar: ->
|
||||||
@tokenizedBuffer.grammar
|
@tokenizedBuffer.grammar
|
||||||
|
|
||||||
|
@ -335,9 +335,6 @@ class EditSession
|
|||||||
# {Delegates to: TextBuffer.getPath}
|
# {Delegates to: TextBuffer.getPath}
|
||||||
getPath: -> @buffer.getPath()
|
getPath: -> @buffer.getPath()
|
||||||
|
|
||||||
# {Delegates to: TextBuffer.getRelativePath}
|
|
||||||
getRelativePath: -> @buffer.getRelativePath()
|
|
||||||
|
|
||||||
# {Delegates to: TextBuffer.getText}
|
# {Delegates to: TextBuffer.getText}
|
||||||
getText: -> @buffer.getText()
|
getText: -> @buffer.getText()
|
||||||
|
|
||||||
@ -567,8 +564,11 @@ class EditSession
|
|||||||
pasteText: (options={}) ->
|
pasteText: (options={}) ->
|
||||||
[text, metadata] = atom.pasteboard.read()
|
[text, metadata] = atom.pasteboard.read()
|
||||||
|
|
||||||
|
containsNewlines = text.indexOf('\n') isnt -1
|
||||||
|
|
||||||
if atom.config.get('editor.normalizeIndentOnPaste') and metadata
|
if atom.config.get('editor.normalizeIndentOnPaste') and metadata
|
||||||
options.indentBasis ?= metadata.indentBasis
|
if !@getCursor().hasPrecedingCharactersOnLine() or containsNewlines
|
||||||
|
options.indentBasis ?= metadata.indentBasis
|
||||||
|
|
||||||
@insertText(text, options)
|
@insertText(text, options)
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class Editor extends View
|
|||||||
@edit(editSession)
|
@edit(editSession)
|
||||||
else if @mini
|
else if @mini
|
||||||
@edit(new EditSession
|
@edit(new EditSession
|
||||||
buffer: new TextBuffer
|
buffer: TextBuffer.createAsRoot()
|
||||||
softWrap: false
|
softWrap: false
|
||||||
tabLength: 2
|
tabLength: 2
|
||||||
softTabs: true
|
softTabs: true
|
||||||
@ -586,8 +586,10 @@ class Editor extends View
|
|||||||
@showIndentGuide = showIndentGuide
|
@showIndentGuide = showIndentGuide
|
||||||
@resetDisplay()
|
@resetDisplay()
|
||||||
|
|
||||||
# {Delegates to: TextBuffer.checkoutHead}
|
# Checkout the HEAD revision of this editor's file.
|
||||||
checkoutHead: -> @getBuffer().checkoutHead()
|
checkoutHead: ->
|
||||||
|
if path = @getPath()
|
||||||
|
atom.project.getRepo()?.checkoutHead(path)
|
||||||
|
|
||||||
# {Delegates to: EditSession.setText}
|
# {Delegates to: EditSession.setText}
|
||||||
setText: (text) -> @activeEditSession.setText(text)
|
setText: (text) -> @activeEditSession.setText(text)
|
||||||
@ -598,9 +600,6 @@ class Editor extends View
|
|||||||
# {Delegates to: EditSession.getPath}
|
# {Delegates to: EditSession.getPath}
|
||||||
getPath: -> @activeEditSession?.getPath()
|
getPath: -> @activeEditSession?.getPath()
|
||||||
|
|
||||||
# {Delegates to: EditSession.getRelativePath}
|
|
||||||
getRelativePath: -> @activeEditSession?.getRelativePath()
|
|
||||||
|
|
||||||
# {Delegates to: TextBuffer.getLineCount}
|
# {Delegates to: TextBuffer.getLineCount}
|
||||||
getLineCount: -> @getBuffer().getLineCount()
|
getLineCount: -> @getBuffer().getLineCount()
|
||||||
|
|
||||||
@ -1807,13 +1806,6 @@ class Editor extends View
|
|||||||
else
|
else
|
||||||
' '
|
' '
|
||||||
|
|
||||||
bindToKeyedEvent: (key, event, callback) ->
|
|
||||||
binding = {}
|
|
||||||
binding[key] = event
|
|
||||||
atom.keymap.bindKeys '.editor', binding
|
|
||||||
@on event, =>
|
|
||||||
callback(this, event)
|
|
||||||
|
|
||||||
replaceSelectedText: (replaceFn) ->
|
replaceSelectedText: (replaceFn) ->
|
||||||
selection = @getSelection()
|
selection = @getSelection()
|
||||||
return false if selection.isEmpty()
|
return false if selection.isEmpty()
|
||||||
|
@ -71,8 +71,7 @@ class Git
|
|||||||
@refreshStatus()
|
@refreshStatus()
|
||||||
|
|
||||||
if project?
|
if project?
|
||||||
@subscribeToBuffer(buffer) for buffer in project.getBuffers()
|
@subscribe project.buffers.onEach (buffer) => @subscribeToBuffer(buffer)
|
||||||
@subscribe project, 'buffer-created', (buffer) => @subscribeToBuffer(buffer)
|
|
||||||
|
|
||||||
# Private: Subscribes to buffer events.
|
# Private: Subscribes to buffer events.
|
||||||
subscribeToBuffer: (buffer) ->
|
subscribeToBuffer: (buffer) ->
|
||||||
|
48
src/key-binding.coffee
Normal file
48
src/key-binding.coffee
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{$} = require './space-pen-extensions'
|
||||||
|
_ = require 'underscore-plus'
|
||||||
|
fs = require 'fs-plus'
|
||||||
|
{specificity} = require 'clear-cut'
|
||||||
|
PEG = require 'pegjs'
|
||||||
|
|
||||||
|
### Internal ###
|
||||||
|
|
||||||
|
module.exports =
|
||||||
|
class KeyBinding
|
||||||
|
@parser: null
|
||||||
|
@currentIndex: 1
|
||||||
|
|
||||||
|
@normalizeKeystroke: (keystroke) ->
|
||||||
|
normalizedKeystroke = keystroke.split(/\s+/).map (keystroke) =>
|
||||||
|
keys = @getParser().parse(keystroke)
|
||||||
|
modifiers = keys[0...-1]
|
||||||
|
modifiers.sort()
|
||||||
|
[modifiers..., _.last(keys)].join('-')
|
||||||
|
normalizedKeystroke.join(' ')
|
||||||
|
|
||||||
|
@getParser: ->
|
||||||
|
if not KeyBinding.parser
|
||||||
|
keystrokePattern = fs.readFileSync(require.resolve('./keystroke-pattern.pegjs'), 'utf8')
|
||||||
|
KeyBinding.parser = PEG.buildParser(keystrokePattern)
|
||||||
|
|
||||||
|
KeyBinding.parser
|
||||||
|
|
||||||
|
constructor: (source, command, keystroke, selector) ->
|
||||||
|
@source = source
|
||||||
|
@command = command
|
||||||
|
@keystroke = KeyBinding.normalizeKeystroke(keystroke)
|
||||||
|
@selector = selector.replace(/!important/g, '')
|
||||||
|
@specificity = specificity(selector)
|
||||||
|
@index = KeyBinding.currentIndex++
|
||||||
|
|
||||||
|
matches: (keystroke) ->
|
||||||
|
multiKeystroke = /\s/.test keystroke
|
||||||
|
if multiKeystroke
|
||||||
|
keystroke == @keystroke
|
||||||
|
else
|
||||||
|
keystroke.split(' ')[0] == @keystroke.split(' ')[0]
|
||||||
|
|
||||||
|
compare: (keyBinding) ->
|
||||||
|
if keyBinding.specificity == @specificity
|
||||||
|
keyBinding.index - @index
|
||||||
|
else
|
||||||
|
keyBinding.specificity - @specificity
|
@ -3,7 +3,7 @@ _ = require 'underscore-plus'
|
|||||||
fs = require 'fs-plus'
|
fs = require 'fs-plus'
|
||||||
path = require 'path'
|
path = require 'path'
|
||||||
CSON = require 'season'
|
CSON = require 'season'
|
||||||
BindingSet = require './binding-set'
|
KeyBinding = require './key-binding'
|
||||||
{Emitter} = require 'emissary'
|
{Emitter} = require 'emissary'
|
||||||
|
|
||||||
Modifiers = ['alt', 'control', 'ctrl', 'shift', 'meta']
|
Modifiers = ['alt', 'control', 'ctrl', 'shift', 'meta']
|
||||||
@ -25,166 +25,46 @@ module.exports =
|
|||||||
class Keymap
|
class Keymap
|
||||||
Emitter.includeInto(this)
|
Emitter.includeInto(this)
|
||||||
|
|
||||||
bindingSets: null
|
|
||||||
nextBindingSetIndex: 0
|
|
||||||
bindingSetsByFirstKeystroke: null
|
|
||||||
queuedKeystrokes: null
|
|
||||||
|
|
||||||
constructor: ({@resourcePath, @configDirPath})->
|
constructor: ({@resourcePath, @configDirPath})->
|
||||||
@bindingSets = []
|
@keyBindings = []
|
||||||
@bindingSetsByFirstKeystroke = {}
|
|
||||||
|
|
||||||
loadBundledKeymaps: ->
|
# Public: Returns an array of all {KeyBinding}s.
|
||||||
@loadDirectory(path.join(@resourcePath, 'keymaps'))
|
getKeyBindings: ->
|
||||||
@emit('bundled-keymaps-loaded')
|
_.clone(@keyBindings)
|
||||||
|
|
||||||
loadUserKeymap: ->
|
# Public: Returns a array of {KeyBinding}s (sorted by selector specificity)
|
||||||
userKeymapPath = CSON.resolve(path.join(@configDirPath, 'keymap'))
|
# that match a keystroke and element.
|
||||||
@load(userKeymapPath) if userKeymapPath
|
|
||||||
|
|
||||||
loadDirectory: (directoryPath) ->
|
|
||||||
@load(filePath) for filePath in fs.listSync(directoryPath, ['.cson', '.json'])
|
|
||||||
|
|
||||||
load: (path) ->
|
|
||||||
@add(path, CSON.readFileSync(path))
|
|
||||||
|
|
||||||
add: (args...) ->
|
|
||||||
name = args.shift() if args.length > 1
|
|
||||||
keymap = args.shift()
|
|
||||||
for selector, bindings of keymap
|
|
||||||
@bindKeys(name, selector, bindings)
|
|
||||||
|
|
||||||
remove: (name) ->
|
|
||||||
for bindingSet in @bindingSets.filter((bindingSet) -> bindingSet.name is name)
|
|
||||||
_.remove(@bindingSets, bindingSet)
|
|
||||||
for keystrokes of bindingSet.commandsByKeystrokes
|
|
||||||
keystroke = keystrokes.split(' ')[0]
|
|
||||||
_.remove(@bindingSetsByFirstKeystroke[keystroke], bindingSet)
|
|
||||||
|
|
||||||
# Public: Returns an array of objects that represent every keystroke to
|
|
||||||
# command mapping. Each object contains the following keys `source`,
|
|
||||||
# `selector`, `command`, `keystrokes`.
|
|
||||||
getAllKeyMappings: ->
|
|
||||||
mappings = []
|
|
||||||
for bindingSet in @bindingSets
|
|
||||||
selector = bindingSet.getSelector()
|
|
||||||
source = @determineSource(bindingSet.getName())
|
|
||||||
for keystrokes, command of bindingSet.getCommandsByKeystrokes()
|
|
||||||
mappings.push {keystrokes, command, selector, source}
|
|
||||||
|
|
||||||
mappings
|
|
||||||
|
|
||||||
# Private: Returns a user friendly description of where a keybinding was
|
|
||||||
# loaded from.
|
|
||||||
#
|
#
|
||||||
# * filePath:
|
# * keystroke:
|
||||||
# The absolute path from which the keymap was loaded
|
# The string representing the keys pressed (e.g. ctrl-P).
|
||||||
|
# * element:
|
||||||
|
# The DOM node that will match a {KeyBinding}'s selector.
|
||||||
|
keyBindingsForKeystrokeMatchingElement: (keystroke, element) ->
|
||||||
|
keyBindings = @keyBindingsForKeystroke(keystroke)
|
||||||
|
@keyBindingsMatchingElement(element, keyBindings)
|
||||||
|
|
||||||
|
# Public: Returns an array of {KeyBinding}s that match a keystroke
|
||||||
|
# * keystroke:
|
||||||
|
# The string representing the keys pressed (e.g. ctrl-P)
|
||||||
|
keyBindingsForKeystroke: (keystroke) ->
|
||||||
|
keystroke = KeyBinding.normalizeKeystroke(keystroke)
|
||||||
|
keyBindings = @keyBindings.filter (keyBinding) -> keyBinding.matches(keystroke)
|
||||||
|
|
||||||
|
# Public: Returns a array of {KeyBinding}s (sorted by selector specificity)
|
||||||
|
# whos selector matches the element.
|
||||||
#
|
#
|
||||||
# Returns one of:
|
# * element:
|
||||||
# * `Core` indicates it comes from a bundled package.
|
# The DOM node that will match a {KeyBinding}'s selector.
|
||||||
# * `User` indicates that it was defined by a user.
|
keyBindingsMatchingElement: (element, keyBindings=@keyBindings) ->
|
||||||
# * `<package-name>` the package which defined it.
|
keyBindings = keyBindings.filter ({selector}) -> $(element).closest(selector).length > 0
|
||||||
# * `Unknown` if an invalid path was passed in.
|
keyBindings.sort (a, b) -> a.compare(b)
|
||||||
determineSource: (filePath) ->
|
|
||||||
return 'Unknown' unless filePath
|
|
||||||
|
|
||||||
pathParts = filePath.split(path.sep)
|
# Public: Returns a keystroke string derived from an event.
|
||||||
if _.contains(pathParts, 'node_modules') or _.contains(pathParts, 'atom') or _.contains(pathParts, 'src')
|
# * event:
|
||||||
'Core'
|
# A DOM or jQuery event
|
||||||
else if _.contains(pathParts, '.atom') and _.contains(pathParts, 'keymaps') and !_.contains(pathParts, 'packages')
|
# * previousKeystroke:
|
||||||
'User'
|
# An optional string used for multiKeystrokes
|
||||||
else
|
keystrokeStringForEvent: (event, previousKeystroke) ->
|
||||||
packageNameIndex = pathParts.length - 3
|
|
||||||
pathParts[packageNameIndex]
|
|
||||||
|
|
||||||
bindKeys: (args...) ->
|
|
||||||
name = args.shift() if args.length > 2
|
|
||||||
[selector, bindings] = args
|
|
||||||
bindingSet = new BindingSet(selector, bindings, @nextBindingSetIndex++, name)
|
|
||||||
@bindingSets.unshift(bindingSet)
|
|
||||||
for keystrokes of bindingSet.commandsByKeystrokes
|
|
||||||
keystroke = keystrokes.split(' ')[0] # only index by first keystroke
|
|
||||||
@bindingSetsByFirstKeystroke[keystroke] ?= []
|
|
||||||
@bindingSetsByFirstKeystroke[keystroke].push(bindingSet)
|
|
||||||
|
|
||||||
unbindKeys: (selector, bindings) ->
|
|
||||||
bindingSet = _.detect @bindingSets, (bindingSet) ->
|
|
||||||
bindingSet.selector is selector and bindingSet.bindings is bindings
|
|
||||||
|
|
||||||
if bindingSet
|
|
||||||
_.remove(@bindingSets, bindingSet)
|
|
||||||
|
|
||||||
bindingsForElement: (element) ->
|
|
||||||
keystrokeMap = {}
|
|
||||||
currentNode = $(element)
|
|
||||||
|
|
||||||
while currentNode.length
|
|
||||||
bindingSets = @bindingSetsForNode(currentNode)
|
|
||||||
_.defaults(keystrokeMap, set.commandsByKeystrokes) for set in bindingSets
|
|
||||||
currentNode = currentNode.parent()
|
|
||||||
|
|
||||||
keystrokeMap
|
|
||||||
|
|
||||||
handleKeyEvent: (event) =>
|
|
||||||
event.keystrokes = @multiKeystrokeStringForEvent(event)
|
|
||||||
isMultiKeystroke = @queuedKeystrokes?
|
|
||||||
@queuedKeystrokes = null
|
|
||||||
|
|
||||||
firstKeystroke = event.keystrokes.split(' ')[0]
|
|
||||||
bindingSetsForFirstKeystroke = @bindingSetsByFirstKeystroke[firstKeystroke]
|
|
||||||
if bindingSetsForFirstKeystroke?
|
|
||||||
currentNode = $(event.target)
|
|
||||||
currentNode = rootView if currentNode is $('body')[0]
|
|
||||||
while currentNode.length
|
|
||||||
candidateBindingSets = @bindingSetsForNode(currentNode, bindingSetsForFirstKeystroke)
|
|
||||||
for bindingSet in candidateBindingSets
|
|
||||||
command = bindingSet.commandForEvent(event)
|
|
||||||
if command is 'native!'
|
|
||||||
return true
|
|
||||||
else if command
|
|
||||||
continue if @triggerCommandEvent(event, command)
|
|
||||||
return false
|
|
||||||
else if command == false
|
|
||||||
return false
|
|
||||||
|
|
||||||
if bindingSet.matchesKeystrokePrefix(event)
|
|
||||||
@queuedKeystrokes = event.keystrokes
|
|
||||||
return false
|
|
||||||
currentNode = currentNode.parent()
|
|
||||||
|
|
||||||
return false if isMultiKeystroke
|
|
||||||
return false if firstKeystroke is 'tab'
|
|
||||||
|
|
||||||
bindingSetsForNode: (node, candidateBindingSets = @bindingSets) ->
|
|
||||||
bindingSets = candidateBindingSets.filter (set) -> node.is(set.selector)
|
|
||||||
bindingSets.sort (a, b) ->
|
|
||||||
if b.specificity == a.specificity
|
|
||||||
b.index - a.index
|
|
||||||
else
|
|
||||||
b.specificity - a.specificity
|
|
||||||
|
|
||||||
triggerCommandEvent: (keyEvent, commandName) ->
|
|
||||||
keyEvent.target = rootView[0] if keyEvent.target == document.body and window.rootView
|
|
||||||
commandEvent = $.Event(commandName)
|
|
||||||
commandEvent.keyEvent = keyEvent
|
|
||||||
aborted = false
|
|
||||||
commandEvent.abortKeyBinding = ->
|
|
||||||
@stopImmediatePropagation()
|
|
||||||
aborted = true
|
|
||||||
$(keyEvent.target).trigger(commandEvent)
|
|
||||||
aborted
|
|
||||||
|
|
||||||
multiKeystrokeStringForEvent: (event) ->
|
|
||||||
currentKeystroke = @keystrokeStringForEvent(event)
|
|
||||||
if @queuedKeystrokes
|
|
||||||
if currentKeystroke in Modifiers
|
|
||||||
@queuedKeystrokes
|
|
||||||
else
|
|
||||||
@queuedKeystrokes + ' ' + currentKeystroke
|
|
||||||
else
|
|
||||||
currentKeystroke
|
|
||||||
|
|
||||||
keystrokeStringForEvent: (event) ->
|
|
||||||
if event.originalEvent.keyIdentifier.indexOf('U+') == 0
|
if event.originalEvent.keyIdentifier.indexOf('U+') == 0
|
||||||
hexCharCode = event.originalEvent.keyIdentifier[2..]
|
hexCharCode = event.originalEvent.keyIdentifier[2..]
|
||||||
charCode = parseInt(hexCharCode, 16)
|
charCode = parseInt(hexCharCode, 16)
|
||||||
@ -207,16 +87,73 @@ class Keymap
|
|||||||
else
|
else
|
||||||
key = key.toLowerCase()
|
key = key.toLowerCase()
|
||||||
|
|
||||||
[modifiers..., key].join('-')
|
keystroke = [modifiers..., key].join('-')
|
||||||
|
|
||||||
keystrokesByCommandForSelector: (selector)->
|
if previousKeystroke
|
||||||
keystrokesByCommand = {}
|
if keystroke in Modifiers
|
||||||
for bindingSet in @bindingSets
|
previousKeystroke
|
||||||
for keystroke, command of bindingSet.commandsByKeystrokes
|
else
|
||||||
continue if selector? and selector != bindingSet.selector
|
"#{previousKeystroke} #{keystroke}"
|
||||||
keystrokesByCommand[command] ?= []
|
else
|
||||||
keystrokesByCommand[command].push keystroke
|
keystroke
|
||||||
keystrokesByCommand
|
|
||||||
|
loadBundledKeymaps: ->
|
||||||
|
@loadDirectory(path.join(@resourcePath, 'keymaps'))
|
||||||
|
@emit('bundled-keymaps-loaded')
|
||||||
|
|
||||||
|
loadUserKeymap: ->
|
||||||
|
userKeymapPath = CSON.resolve(path.join(@configDirPath, 'keymap'))
|
||||||
|
@load(userKeymapPath) if userKeymapPath
|
||||||
|
|
||||||
|
loadDirectory: (directoryPath) ->
|
||||||
|
@load(filePath) for filePath in fs.listSync(directoryPath, ['.cson', '.json'])
|
||||||
|
|
||||||
|
load: (path) ->
|
||||||
|
@add(path, CSON.readFileSync(path))
|
||||||
|
|
||||||
|
add: (source, keyMappingsBySelector) ->
|
||||||
|
for selector, keyMappings of keyMappingsBySelector
|
||||||
|
@bindKeys(source, selector, keyMappings)
|
||||||
|
|
||||||
|
remove: (source) ->
|
||||||
|
@keyBindings = @keyBindings.filter (keyBinding) -> keyBinding.source isnt source
|
||||||
|
|
||||||
|
bindKeys: (source, selector, keyMappings) ->
|
||||||
|
for keystroke, command of keyMappings
|
||||||
|
@keyBindings.push new KeyBinding(source, command, keystroke, selector)
|
||||||
|
|
||||||
|
handleKeyEvent: (event) ->
|
||||||
|
element = event.target
|
||||||
|
element = rootView if element == document.body
|
||||||
|
keystroke = @keystrokeStringForEvent(event, @queuedKeystroke)
|
||||||
|
keyBindings = @keyBindingsForKeystrokeMatchingElement(keystroke, element)
|
||||||
|
|
||||||
|
if keyBindings.length == 0 and @queuedKeystroke
|
||||||
|
@queuedKeystroke = null
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
@queuedKeystroke = null
|
||||||
|
|
||||||
|
for keyBinding in keyBindings
|
||||||
|
partialMatch = keyBinding.keystroke isnt keystroke
|
||||||
|
if partialMatch
|
||||||
|
@queuedKeystroke = keystroke
|
||||||
|
shouldBubble = false
|
||||||
|
else
|
||||||
|
if keyBinding.command is 'native!'
|
||||||
|
shouldBubble = true
|
||||||
|
else if @triggerCommandEvent(element, keyBinding.command)
|
||||||
|
shouldBubble = false
|
||||||
|
|
||||||
|
break if shouldBubble?
|
||||||
|
|
||||||
|
shouldBubble ? true
|
||||||
|
|
||||||
|
triggerCommandEvent: (element, commandName) ->
|
||||||
|
commandEvent = $.Event(commandName)
|
||||||
|
commandEvent.abortKeyBinding = -> commandEvent.stopImmediatePropagation()
|
||||||
|
$(element).trigger(commandEvent)
|
||||||
|
not commandEvent.isImmediatePropagationStopped()
|
||||||
|
|
||||||
isAscii: (charCode) ->
|
isAscii: (charCode) ->
|
||||||
0 <= charCode <= 127
|
0 <= charCode <= 127
|
||||||
|
@ -14,7 +14,7 @@ class MenuManager
|
|||||||
# Private:
|
# Private:
|
||||||
constructor: ({@resourcePath}) ->
|
constructor: ({@resourcePath}) ->
|
||||||
@template = []
|
@template = []
|
||||||
atom.keymap.on 'bundled-keymaps-loaded', => @loadCoreItems()
|
atom.keymap.on 'bundled-keymaps-loaded', => @loadPlatformItems()
|
||||||
|
|
||||||
# Public: Adds the given item definition to the existing template.
|
# Public: Adds the given item definition to the existing template.
|
||||||
#
|
#
|
||||||
@ -29,33 +29,34 @@ class MenuManager
|
|||||||
|
|
||||||
# Public: Refreshes the currently visible menu.
|
# Public: Refreshes the currently visible menu.
|
||||||
update: ->
|
update: ->
|
||||||
keystrokesByCommand = atom.keymap.keystrokesByCommandForSelector('body')
|
keystrokesByCommand = {}
|
||||||
_.extend(keystrokesByCommand, atom.keymap.keystrokesByCommandForSelector('.editor'))
|
selectors = ['body', '.editor', '.editor:not(.mini)']
|
||||||
_.extend(keystrokesByCommand, atom.keymap.keystrokesByCommandForSelector('.editor:not(.mini)'))
|
for binding in atom.keymap.getKeyBindings() when binding.selector in selectors
|
||||||
|
keystrokesByCommand[binding.command] ?= []
|
||||||
|
keystrokesByCommand[binding.command].push binding.keystroke
|
||||||
@sendToBrowserProcess(@template, keystrokesByCommand)
|
@sendToBrowserProcess(@template, keystrokesByCommand)
|
||||||
|
|
||||||
# Private
|
# Private
|
||||||
loadCoreItems: ->
|
loadPlatformItems: ->
|
||||||
menusDirPath = path.join(@resourcePath, 'menus')
|
menusDirPath = path.join(@resourcePath, 'menus')
|
||||||
menuPaths = fs.listSync(menusDirPath, ['cson', 'json'])
|
platformMenuPath = fs.resolve(menusDirPath, process.platform, ['cson', 'json'])
|
||||||
for menuPath in menuPaths
|
data = CSON.readFileSync(platformMenuPath)
|
||||||
data = CSON.readFileSync(menuPath)
|
@add(data.menu)
|
||||||
@add(data.menu)
|
|
||||||
|
|
||||||
# Private: Merges an item in a submenu aware way such that new items are always
|
# Private: Merges an item in a submenu aware way such that new items are always
|
||||||
# appended to the bottom of existing menus where possible.
|
# appended to the bottom of existing menus where possible.
|
||||||
merge: (menu, item) ->
|
merge: (menu, item) ->
|
||||||
item = _.deepClone(item)
|
item = _.deepClone(item)
|
||||||
|
|
||||||
if item.submenu? and match = _.find(menu, (i) -> i.submenu? and i.label == item.label)
|
if item.submenu? and match = _.find(menu, (i) => i.submenu? and @normalizeLabel(i.label) == @normalizeLabel(item.label))
|
||||||
@merge(match.submenu, i) for i in item.submenu
|
@merge(match.submenu, i) for i in item.submenu
|
||||||
else
|
else
|
||||||
menu.push(item) unless _.find(menu, (i) -> i.label == item.label)
|
menu.push(item) unless _.find(menu, (i) => @normalizeLabel(i.label) == @normalizeLabel(item.label))
|
||||||
|
|
||||||
# Private: OSX can't handle displaying accelerators for multiple keystrokes.
|
# Private: OSX can't handle displaying accelerators for multiple keystrokes.
|
||||||
# If they are sent across, it will stop processing accelerators for the rest
|
# If they are sent across, it will stop processing accelerators for the rest
|
||||||
# of the menu items.
|
# of the menu items.
|
||||||
filterMultipleKeystrokes: (keystrokesByCommand) ->
|
filterMultipleKeystroke: (keystrokesByCommand) ->
|
||||||
filtered = {}
|
filtered = {}
|
||||||
for key, bindings of keystrokesByCommand
|
for key, bindings of keystrokesByCommand
|
||||||
for binding in bindings
|
for binding in bindings
|
||||||
@ -67,5 +68,12 @@ class MenuManager
|
|||||||
|
|
||||||
# Private
|
# Private
|
||||||
sendToBrowserProcess: (template, keystrokesByCommand) ->
|
sendToBrowserProcess: (template, keystrokesByCommand) ->
|
||||||
keystrokesByCommand = @filterMultipleKeystrokes(keystrokesByCommand)
|
keystrokesByCommand = @filterMultipleKeystroke(keystrokesByCommand)
|
||||||
ipc.sendChannel 'update-application-menu', template, keystrokesByCommand
|
ipc.sendChannel 'update-application-menu', template, keystrokesByCommand
|
||||||
|
|
||||||
|
# Private
|
||||||
|
normalizeLabel: (label) ->
|
||||||
|
if process.platform is 'win32'
|
||||||
|
label.replace(/\&/g, '')
|
||||||
|
else
|
||||||
|
label
|
||||||
|
@ -5,7 +5,6 @@ _ = require 'underscore-plus'
|
|||||||
fs = require 'fs-plus'
|
fs = require 'fs-plus'
|
||||||
Q = require 'q'
|
Q = require 'q'
|
||||||
telepath = require 'telepath'
|
telepath = require 'telepath'
|
||||||
{Range} = telepath
|
|
||||||
|
|
||||||
TextBuffer = require './text-buffer'
|
TextBuffer = require './text-buffer'
|
||||||
EditSession = require './edit-session'
|
EditSession = require './edit-session'
|
||||||
@ -19,16 +18,12 @@ Git = require './git'
|
|||||||
# Ultimately, a project is a git directory that's been opened. It's a collection
|
# Ultimately, a project is a git directory that's been opened. It's a collection
|
||||||
# of directories and files that you can operate on.
|
# of directories and files that you can operate on.
|
||||||
module.exports =
|
module.exports =
|
||||||
class Project
|
class Project extends telepath.Model
|
||||||
Emitter.includeInto(this)
|
Emitter.includeInto(this)
|
||||||
|
|
||||||
@acceptsDocuments: true
|
@properties
|
||||||
@version: 1
|
buffers: []
|
||||||
|
path: null
|
||||||
registerDeserializer(this)
|
|
||||||
|
|
||||||
# Private:
|
|
||||||
@deserialize: (state) -> new Project(state)
|
|
||||||
|
|
||||||
# Public: Find the local path for the given repository URL.
|
# Public: Find the local path for the given repository URL.
|
||||||
@pathForRepositoryUrl: (repoUrl) ->
|
@pathForRepositoryUrl: (repoUrl) ->
|
||||||
@ -36,10 +31,18 @@ class Project
|
|||||||
repoName = repoName.replace(/\.git$/, '')
|
repoName = repoName.replace(/\.git$/, '')
|
||||||
path.join(atom.config.get('core.projectHome'), repoName)
|
path.join(atom.config.get('core.projectHome'), repoName)
|
||||||
|
|
||||||
rootDirectory: null
|
# Private: Called by telepath.
|
||||||
editSessions: null
|
attached: ->
|
||||||
ignoredPathRegexes: null
|
for buffer in @buffers.getValues()
|
||||||
openers: null
|
buffer.once 'destroyed', => @removeBuffer(buffer)
|
||||||
|
|
||||||
|
@openers = []
|
||||||
|
@editSessions = []
|
||||||
|
@setPath(@path)
|
||||||
|
|
||||||
|
# Private: Called by telepath.
|
||||||
|
beforePersistence: ->
|
||||||
|
@destroyUnretainedBuffers()
|
||||||
|
|
||||||
# Public:
|
# Public:
|
||||||
registerOpener: (opener) -> @openers.push(opener)
|
registerOpener: (opener) -> @openers.push(opener)
|
||||||
@ -59,51 +62,10 @@ class Project
|
|||||||
@repo.destroy()
|
@repo.destroy()
|
||||||
@repo = null
|
@repo = null
|
||||||
|
|
||||||
# Public: Establishes a new project at a given path.
|
|
||||||
#
|
|
||||||
# path - The {String} name of the path
|
|
||||||
constructor: (pathOrState) ->
|
|
||||||
@openers = []
|
|
||||||
@editSessions = []
|
|
||||||
@buffers = []
|
|
||||||
|
|
||||||
if pathOrState instanceof telepath.Document
|
|
||||||
@state = pathOrState
|
|
||||||
if projectPath = @state.remove('path')
|
|
||||||
@setPath(projectPath)
|
|
||||||
else
|
|
||||||
@setPath(@constructor.pathForRepositoryUrl(@state.get('repoUrl')))
|
|
||||||
|
|
||||||
@state.get('buffers').each (bufferState) =>
|
|
||||||
if buffer = deserialize(bufferState, project: this)
|
|
||||||
@addBuffer(buffer, updateState: false)
|
|
||||||
else
|
|
||||||
@state = atom.site.createDocument(deserializer: @constructor.name, version: @constructor.version, buffers: [])
|
|
||||||
@setPath(pathOrState)
|
|
||||||
|
|
||||||
@state.get('buffers').on 'changed', ({index, insertedValues, removedValues, siteId}) =>
|
|
||||||
return if siteId is @state.siteId
|
|
||||||
|
|
||||||
for removedBuffer in removedValues
|
|
||||||
@removeBufferAtIndex(index, updateState: false)
|
|
||||||
for insertedBuffer, i in insertedValues
|
|
||||||
@addBufferAtIndex(deserialize(insertedBuffer, project: this), index + i, updateState: false)
|
|
||||||
|
|
||||||
# Private:
|
|
||||||
serialize: ->
|
|
||||||
state = @state.clone()
|
|
||||||
state.set('path', @getPath())
|
|
||||||
@destroyUnretainedBuffers()
|
|
||||||
state.set('buffers', buffer.serialize() for buffer in @getBuffers())
|
|
||||||
state
|
|
||||||
|
|
||||||
# Private:
|
# Private:
|
||||||
destroyUnretainedBuffers: ->
|
destroyUnretainedBuffers: ->
|
||||||
buffer.destroy() for buffer in @getBuffers() when not buffer.isRetained()
|
buffer.destroy() for buffer in @getBuffers() when not buffer.isRetained()
|
||||||
|
|
||||||
# Public: ?
|
|
||||||
getState: -> @state
|
|
||||||
|
|
||||||
# Public: Returns the {Git} repository if available.
|
# Public: Returns the {Git} repository if available.
|
||||||
getRepo: -> @repo
|
getRepo: -> @repo
|
||||||
|
|
||||||
@ -113,6 +75,7 @@ class Project
|
|||||||
|
|
||||||
# Public: Sets the project's fullpath.
|
# Public: Sets the project's fullpath.
|
||||||
setPath: (projectPath) ->
|
setPath: (projectPath) ->
|
||||||
|
@path = projectPath
|
||||||
@rootDirectory?.off()
|
@rootDirectory?.off()
|
||||||
|
|
||||||
@destroyRepo()
|
@destroyRepo()
|
||||||
@ -125,9 +88,6 @@ class Project
|
|||||||
else
|
else
|
||||||
@rootDirectory = null
|
@rootDirectory = null
|
||||||
|
|
||||||
if originUrl = @repo?.getOriginUrl()
|
|
||||||
@state.set('repoUrl', originUrl)
|
|
||||||
|
|
||||||
@emit "path-changed"
|
@emit "path-changed"
|
||||||
|
|
||||||
# Public: Returns the name of the root directory.
|
# Public: Returns the name of the root directory.
|
||||||
@ -220,20 +180,18 @@ class Project
|
|||||||
#
|
#
|
||||||
# Returns an {Array} of {TextBuffer}s.
|
# Returns an {Array} of {TextBuffer}s.
|
||||||
getBuffers: ->
|
getBuffers: ->
|
||||||
new Array(@buffers...)
|
new Array(@buffers.getValues()...)
|
||||||
|
|
||||||
isPathModified: (filePath) ->
|
isPathModified: (filePath) ->
|
||||||
absoluteFilePath = @resolve(filePath)
|
@findBufferForPath(@resolve(filePath))?.isModified()
|
||||||
existingBuffer = _.find @buffers, (buffer) -> buffer.getPath() == absoluteFilePath
|
|
||||||
existingBuffer?.isModified()
|
findBufferForPath: (filePath) ->
|
||||||
|
_.find @buffers.getValues(), (buffer) -> buffer.getPath() == filePath
|
||||||
|
|
||||||
# Private: Only to be used in specs
|
# Private: Only to be used in specs
|
||||||
bufferForPathSync: (filePath) ->
|
bufferForPathSync: (filePath) ->
|
||||||
absoluteFilePath = @resolve(filePath)
|
absoluteFilePath = @resolve(filePath)
|
||||||
|
existingBuffer = @findBufferForPath(absoluteFilePath) if filePath
|
||||||
if filePath
|
|
||||||
existingBuffer = _.find @buffers, (buffer) -> buffer.getPath() == absoluteFilePath
|
|
||||||
|
|
||||||
existingBuffer ? @buildBufferSync(absoluteFilePath)
|
existingBuffer ? @buildBufferSync(absoluteFilePath)
|
||||||
|
|
||||||
# Private: Given a file path, this retrieves or creates a new {TextBuffer}.
|
# Private: Given a file path, this retrieves or creates a new {TextBuffer}.
|
||||||
@ -246,9 +204,7 @@ class Project
|
|||||||
# Returns a promise that resolves to the {TextBuffer}.
|
# Returns a promise that resolves to the {TextBuffer}.
|
||||||
bufferForPath: (filePath) ->
|
bufferForPath: (filePath) ->
|
||||||
absoluteFilePath = @resolve(filePath)
|
absoluteFilePath = @resolve(filePath)
|
||||||
if absoluteFilePath
|
existingBuffer = @findBufferForPath(absoluteFilePath) if absoluteFilePath
|
||||||
existingBuffer = _.find @buffers, (buffer) -> buffer.getPath() == absoluteFilePath
|
|
||||||
|
|
||||||
Q(existingBuffer ? @buildBuffer(absoluteFilePath))
|
Q(existingBuffer ? @buildBuffer(absoluteFilePath))
|
||||||
|
|
||||||
# Private:
|
# Private:
|
||||||
@ -257,9 +213,9 @@ class Project
|
|||||||
|
|
||||||
# Private: DEPRECATED
|
# Private: DEPRECATED
|
||||||
buildBufferSync: (absoluteFilePath) ->
|
buildBufferSync: (absoluteFilePath) ->
|
||||||
buffer = new TextBuffer({project: this, filePath: absoluteFilePath})
|
buffer = new TextBuffer({filePath: absoluteFilePath})
|
||||||
buffer.loadSync()
|
|
||||||
@addBuffer(buffer)
|
@addBuffer(buffer)
|
||||||
|
buffer.loadSync()
|
||||||
buffer
|
buffer
|
||||||
|
|
||||||
# Private: Given a file path, this sets its {TextBuffer}.
|
# Private: Given a file path, this sets its {TextBuffer}.
|
||||||
@ -269,10 +225,11 @@ class Project
|
|||||||
#
|
#
|
||||||
# Returns a promise that resolves to the {TextBuffer}.
|
# Returns a promise that resolves to the {TextBuffer}.
|
||||||
buildBuffer: (absoluteFilePath) ->
|
buildBuffer: (absoluteFilePath) ->
|
||||||
buffer = new TextBuffer({project: this, filePath: absoluteFilePath})
|
buffer = new TextBuffer({filePath: absoluteFilePath})
|
||||||
buffer.load().then (buffer) =>
|
@addBuffer(buffer)
|
||||||
@addBuffer(buffer)
|
buffer.load()
|
||||||
buffer
|
.then((buffer) -> buffer)
|
||||||
|
.catch(=> @removeBuffer(buffer))
|
||||||
|
|
||||||
# Private:
|
# Private:
|
||||||
addBuffer: (buffer, options={}) ->
|
addBuffer: (buffer, options={}) ->
|
||||||
@ -280,9 +237,10 @@ class Project
|
|||||||
|
|
||||||
# Private:
|
# Private:
|
||||||
addBufferAtIndex: (buffer, index, options={}) ->
|
addBufferAtIndex: (buffer, index, options={}) ->
|
||||||
@buffers[index] = buffer
|
buffer = @buffers.insert(index, buffer)
|
||||||
@state.get('buffers').insert(index, buffer.getState()) if options.updateState ? true
|
buffer.once 'destroyed', => @removeBuffer(buffer)
|
||||||
@emit 'buffer-created', buffer
|
@emit 'buffer-created', buffer
|
||||||
|
buffer
|
||||||
|
|
||||||
# Private: Removes a {TextBuffer} association from the project.
|
# Private: Removes a {TextBuffer} association from the project.
|
||||||
#
|
#
|
||||||
@ -294,7 +252,6 @@ class Project
|
|||||||
# Private:
|
# Private:
|
||||||
removeBufferAtIndex: (index, options={}) ->
|
removeBufferAtIndex: (index, options={}) ->
|
||||||
[buffer] = @buffers.splice(index, 1)
|
[buffer] = @buffers.splice(index, 1)
|
||||||
@state.get('buffers')?.remove(index) if options.updateState ? true
|
|
||||||
buffer?.destroy()
|
buffer?.destroy()
|
||||||
|
|
||||||
# Public: Performs a search across all the files in the project.
|
# Public: Performs a search across all the files in the project.
|
||||||
@ -329,7 +286,7 @@ class Project
|
|||||||
task.on 'scan:paths-searched', (numberOfPathsSearched) ->
|
task.on 'scan:paths-searched', (numberOfPathsSearched) ->
|
||||||
options.onPathsSearched(numberOfPathsSearched)
|
options.onPathsSearched(numberOfPathsSearched)
|
||||||
|
|
||||||
for buffer in @buffers when buffer.isModified()
|
for buffer in @buffers.getValues() when buffer.isModified()
|
||||||
filePath = buffer.getPath()
|
filePath = buffer.getPath()
|
||||||
matches = []
|
matches = []
|
||||||
buffer.scan regex, (match) -> matches.push match
|
buffer.scan regex, (match) -> matches.push match
|
||||||
@ -346,7 +303,7 @@ class Project
|
|||||||
replace: (regex, replacementText, filePaths, iterator) ->
|
replace: (regex, replacementText, filePaths, iterator) ->
|
||||||
deferred = Q.defer()
|
deferred = Q.defer()
|
||||||
|
|
||||||
openPaths = (buffer.getPath() for buffer in @buffers)
|
openPaths = (buffer.getPath() for buffer in @buffers.getValues())
|
||||||
outOfProcessPaths = _.difference(filePaths, openPaths)
|
outOfProcessPaths = _.difference(filePaths, openPaths)
|
||||||
|
|
||||||
inProcessFinished = !openPaths.length
|
inProcessFinished = !openPaths.length
|
||||||
@ -364,7 +321,7 @@ class Project
|
|||||||
|
|
||||||
task.on 'replace:path-replaced', iterator
|
task.on 'replace:path-replaced', iterator
|
||||||
|
|
||||||
for buffer in @buffers
|
for buffer in @buffers.getValues()
|
||||||
replacements = buffer.replace(regex, replacementText, iterator)
|
replacements = buffer.replace(regex, replacementText, iterator)
|
||||||
iterator({filePath: buffer.getPath(), replacements}) if replacements
|
iterator({filePath: buffer.getPath(), replacements}) if replacements
|
||||||
|
|
||||||
|
@ -1,32 +1,28 @@
|
|||||||
crypto = require 'crypto'
|
_ = require 'underscore-plus'
|
||||||
{Emitter, Subscriber} = require 'emissary'
|
{Emitter, Subscriber} = require 'emissary'
|
||||||
guid = require 'guid'
|
|
||||||
Q = require 'q'
|
Q = require 'q'
|
||||||
{P} = require 'scandal'
|
{P} = require 'scandal'
|
||||||
telepath = require 'telepath'
|
telepath = require 'telepath'
|
||||||
|
|
||||||
_ = require 'underscore-plus'
|
|
||||||
File = require './file'
|
File = require './file'
|
||||||
|
|
||||||
{Point, Range} = telepath
|
{Point, Range} = telepath
|
||||||
|
|
||||||
# Private: Represents the contents of a file.
|
# Private: Represents the contents of a file.
|
||||||
#
|
#
|
||||||
# The `Buffer` is often associated with a {File}. However, this is not always
|
# The `TextBuffer` is often associated with a {File}. However, this is not always
|
||||||
# the case, as a `Buffer` could be an unsaved chunk of text.
|
# the case, as a `TextBuffer` could be an unsaved chunk of text.
|
||||||
module.exports =
|
module.exports =
|
||||||
class TextBuffer
|
class TextBuffer extends telepath.Model
|
||||||
Emitter.includeInto(this)
|
Emitter.includeInto(this)
|
||||||
Subscriber.includeInto(this)
|
Subscriber.includeInto(this)
|
||||||
|
|
||||||
@acceptsDocuments: true
|
@properties
|
||||||
@version: 2
|
text: -> new telepath.String('', replicated: false)
|
||||||
registerDeserializer(this)
|
filePath: null
|
||||||
|
relativePath: null
|
||||||
@deserialize: (state, params) ->
|
modifiedWhenLastPersisted: false
|
||||||
buffer = new this(state, params)
|
digestWhenLastPersisted: null
|
||||||
buffer.load()
|
|
||||||
buffer
|
|
||||||
|
|
||||||
stoppedChangingDelay: 300
|
stoppedChangingDelay: 300
|
||||||
stoppedChangingTimeout: null
|
stoppedChangingTimeout: null
|
||||||
@ -36,34 +32,28 @@ class TextBuffer
|
|||||||
file: null
|
file: null
|
||||||
refcount: 0
|
refcount: 0
|
||||||
|
|
||||||
# Creates a new buffer.
|
constructor: ->
|
||||||
#
|
super
|
||||||
# * optionsOrState - An {Object} or a telepath.Document
|
|
||||||
# + filePath - A {String} representing the file path
|
|
||||||
constructor: (optionsOrState={}, params={}) ->
|
|
||||||
if optionsOrState instanceof telepath.Document
|
|
||||||
{@project} = params
|
|
||||||
@state = optionsOrState
|
|
||||||
@id = @state.get('id')
|
|
||||||
filePath = @state.get('relativePath')
|
|
||||||
@text = @state.get('text')
|
|
||||||
@useSerializedText = @state.get('isModified') != false
|
|
||||||
else
|
|
||||||
{@project, filePath} = optionsOrState
|
|
||||||
@text = new telepath.String(initialText ? '', replicated: false)
|
|
||||||
@id = guid.create().toString()
|
|
||||||
@state = atom.site.createDocument
|
|
||||||
id: @id
|
|
||||||
deserializer: @constructor.name
|
|
||||||
version: @constructor.version
|
|
||||||
text: @text
|
|
||||||
|
|
||||||
|
@loadWhenAttached = @getState()?
|
||||||
|
|
||||||
|
# Private: Called by telepath.
|
||||||
|
attached: ->
|
||||||
@loaded = false
|
@loaded = false
|
||||||
|
@useSerializedText = @modifiedWhenLastPersisted != false
|
||||||
|
|
||||||
@subscribe @text, 'changed', @handleTextChange
|
@subscribe @text, 'changed', @handleTextChange
|
||||||
@subscribe @text, 'marker-created', (marker) => @emit 'marker-created', marker
|
@subscribe @text, 'marker-created', (marker) => @emit 'marker-created', marker
|
||||||
@subscribe @text, 'markers-updated', => @emit 'markers-updated'
|
@subscribe @text, 'markers-updated', => @emit 'markers-updated'
|
||||||
|
|
||||||
@setPath(@project.resolve(filePath)) if @project
|
@setPath(@filePath)
|
||||||
|
|
||||||
|
@load() if @loadWhenAttached
|
||||||
|
|
||||||
|
# Private: Called by telepath.
|
||||||
|
beforePersistence: ->
|
||||||
|
@modifiedWhenLastPersisted = @isModified()
|
||||||
|
@digestWhenLastPersisted = @file?.getDigest()
|
||||||
|
|
||||||
loadSync: ->
|
loadSync: ->
|
||||||
@updateCachedDiskContentsSync()
|
@updateCachedDiskContentsSync()
|
||||||
@ -74,7 +64,7 @@ class TextBuffer
|
|||||||
|
|
||||||
finishLoading: ->
|
finishLoading: ->
|
||||||
@loaded = true
|
@loaded = true
|
||||||
if @useSerializedText and @state.get('diskContentsDigest') == @file?.getDigest()
|
if @useSerializedText and @digestWhenLastPersisted is @file?.getDigest()
|
||||||
@emitModifiedStatusChanged(true)
|
@emitModifiedStatusChanged(true)
|
||||||
else
|
else
|
||||||
@reload()
|
@reload()
|
||||||
@ -92,10 +82,10 @@ class TextBuffer
|
|||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
unless @destroyed
|
unless @destroyed
|
||||||
|
@cancelStoppedChangingTimeout()
|
||||||
@file?.off()
|
@file?.off()
|
||||||
@unsubscribe()
|
@unsubscribe()
|
||||||
@destroyed = true
|
@destroyed = true
|
||||||
@project?.removeBuffer(this)
|
|
||||||
@emit 'destroyed'
|
@emit 'destroyed'
|
||||||
|
|
||||||
isRetained: -> @refcount > 0
|
isRetained: -> @refcount > 0
|
||||||
@ -109,16 +99,6 @@ class TextBuffer
|
|||||||
@destroy() unless @isRetained()
|
@destroy() unless @isRetained()
|
||||||
this
|
this
|
||||||
|
|
||||||
serialize: ->
|
|
||||||
state = @state.clone()
|
|
||||||
state.set('isModified', @isModified())
|
|
||||||
state.set('diskContentsDigest', @file.getDigest()) if @file
|
|
||||||
for marker in state.get('text').getMarkers() when marker.isRemote()
|
|
||||||
marker.destroy()
|
|
||||||
state
|
|
||||||
|
|
||||||
getState: -> @state
|
|
||||||
|
|
||||||
subscribeToFile: ->
|
subscribeToFile: ->
|
||||||
@file.on "contents-changed", =>
|
@file.on "contents-changed", =>
|
||||||
@conflict = true if @isModified()
|
@conflict = true if @isModified()
|
||||||
@ -140,7 +120,6 @@ class TextBuffer
|
|||||||
@emitModifiedStatusChanged(@isModified())
|
@emitModifiedStatusChanged(@isModified())
|
||||||
|
|
||||||
@file.on "moved", =>
|
@file.on "moved", =>
|
||||||
@state.set('relativePath', @project.relativize(@getPath()))
|
|
||||||
@emit "path-changed", this
|
@emit "path-changed", this
|
||||||
|
|
||||||
### Public ###
|
### Public ###
|
||||||
@ -183,10 +162,7 @@ class TextBuffer
|
|||||||
@file?.getPath()
|
@file?.getPath()
|
||||||
|
|
||||||
getUri: ->
|
getUri: ->
|
||||||
@getRelativePath()
|
atom.project.relativize(@getPath())
|
||||||
|
|
||||||
getRelativePath: ->
|
|
||||||
@state.get('relativePath')
|
|
||||||
|
|
||||||
# Sets the path for the file.
|
# Sets the path for the file.
|
||||||
#
|
#
|
||||||
@ -202,7 +178,6 @@ class TextBuffer
|
|||||||
else
|
else
|
||||||
@file = null
|
@file = null
|
||||||
|
|
||||||
@state.set('relativePath', @project.relativize(path))
|
|
||||||
@emit "path-changed", this
|
@emit "path-changed", this
|
||||||
|
|
||||||
# Retrieves the current buffer's file extension.
|
# Retrieves the current buffer's file extension.
|
||||||
@ -411,12 +386,12 @@ class TextBuffer
|
|||||||
saveAs: (path) ->
|
saveAs: (path) ->
|
||||||
unless path then throw new Error("Can't save buffer with no file path")
|
unless path then throw new Error("Can't save buffer with no file path")
|
||||||
|
|
||||||
@emit 'will-be-saved'
|
@emit 'will-be-saved', this
|
||||||
@setPath(path)
|
@setPath(path)
|
||||||
@cachedDiskContents = @getText()
|
@cachedDiskContents = @getText()
|
||||||
@file.write(@getText())
|
@file.write(@getText())
|
||||||
@emitModifiedStatusChanged(false)
|
@emitModifiedStatusChanged(false)
|
||||||
@emit 'saved'
|
@emit 'saved', this
|
||||||
|
|
||||||
# Identifies if the buffer was modified.
|
# Identifies if the buffer was modified.
|
||||||
#
|
#
|
||||||
@ -653,12 +628,6 @@ class TextBuffer
|
|||||||
return match[0][0] != '\t'
|
return match[0][0] != '\t'
|
||||||
undefined
|
undefined
|
||||||
|
|
||||||
# Checks out the current `HEAD` revision of the file.
|
|
||||||
checkoutHead: ->
|
|
||||||
path = @getPath()
|
|
||||||
return unless path
|
|
||||||
@project.getRepo()?.checkoutHead(path)
|
|
||||||
|
|
||||||
### Internal ###
|
### Internal ###
|
||||||
|
|
||||||
transact: (fn) -> @text.transact fn
|
transact: (fn) -> @text.transact fn
|
||||||
@ -680,8 +649,11 @@ class TextBuffer
|
|||||||
else
|
else
|
||||||
text
|
text
|
||||||
|
|
||||||
scheduleModifiedEvents: ->
|
cancelStoppedChangingTimeout: ->
|
||||||
clearTimeout(@stoppedChangingTimeout) if @stoppedChangingTimeout
|
clearTimeout(@stoppedChangingTimeout) if @stoppedChangingTimeout
|
||||||
|
|
||||||
|
scheduleModifiedEvents: ->
|
||||||
|
@cancelStoppedChangingTimeout()
|
||||||
stoppedChangingCallback = =>
|
stoppedChangingCallback = =>
|
||||||
@stoppedChangingTimeout = null
|
@stoppedChangingTimeout = null
|
||||||
modifiedStatus = @isModified()
|
modifiedStatus = @isModified()
|
||||||
@ -700,7 +672,7 @@ class TextBuffer
|
|||||||
console.log row, line, line.length
|
console.log row, line, line.length
|
||||||
|
|
||||||
getDebugSnapshot: ->
|
getDebugSnapshot: ->
|
||||||
lines = ['Buffer:']
|
lines = ['TextBuffer:']
|
||||||
for row in [0..@getLastRow()]
|
for row in [0..@getLastRow()]
|
||||||
lines.push "#{row}: #{@lineForRow(row)}"
|
lines.push "#{row}: #{@lineForRow(row)}"
|
||||||
lines.join('\n')
|
lines.join('\n')
|
||||||
|
@ -36,7 +36,7 @@ class TokenizedBuffer
|
|||||||
{ @buffer, tabLength } = optionsOrState
|
{ @buffer, tabLength } = optionsOrState
|
||||||
@state = atom.site.createDocument
|
@state = atom.site.createDocument
|
||||||
deserializer: @constructor.name
|
deserializer: @constructor.name
|
||||||
bufferPath: @buffer.getRelativePath()
|
bufferPath: @buffer.getPath()
|
||||||
tabLength: tabLength ? atom.config.get('editor.tabLength') ? 2
|
tabLength: tabLength ? atom.config.get('editor.tabLength') ? 2
|
||||||
|
|
||||||
@subscribe syntax, 'grammar-added grammar-updated', (grammar) =>
|
@subscribe syntax, 'grammar-added grammar-updated', (grammar) =>
|
||||||
@ -48,7 +48,7 @@ class TokenizedBuffer
|
|||||||
|
|
||||||
@on 'grammar-changed grammar-updated', => @resetTokenizedLines()
|
@on 'grammar-changed grammar-updated', => @resetTokenizedLines()
|
||||||
@subscribe @buffer, "changed", (e) => @handleBufferChange(e)
|
@subscribe @buffer, "changed", (e) => @handleBufferChange(e)
|
||||||
@subscribe @buffer, "path-changed", => @state.set('bufferPath', @buffer.getRelativePath())
|
@subscribe @buffer, "path-changed", => @state.set('bufferPath', @buffer.getPath())
|
||||||
|
|
||||||
@reloadGrammar()
|
@reloadGrammar()
|
||||||
|
|
||||||
|
@ -54,7 +54,8 @@ class WindowEventHandler
|
|||||||
|
|
||||||
@subscribeToCommand $(document), 'core:focus-previous', @focusPrevious
|
@subscribeToCommand $(document), 'core:focus-previous', @focusPrevious
|
||||||
|
|
||||||
@subscribe $(document), 'keydown', atom.keymap.handleKeyEvent
|
@subscribe $(document), 'keydown', (event) ->
|
||||||
|
atom.keymap.handleKeyEvent(event)
|
||||||
|
|
||||||
@subscribe $(document), 'drop', (e) ->
|
@subscribe $(document), 'drop', (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -69,7 +69,7 @@ window.startEditorWindow = ->
|
|||||||
window.unloadEditorWindow = ->
|
window.unloadEditorWindow = ->
|
||||||
return if not atom.project and not atom.rootView
|
return if not atom.project and not atom.rootView
|
||||||
windowState = atom.getWindowState()
|
windowState = atom.getWindowState()
|
||||||
windowState.set('project', atom.project.serialize())
|
windowState.set('project', atom.project)
|
||||||
windowState.set('syntax', atom.syntax.serialize())
|
windowState.set('syntax', atom.syntax.serialize())
|
||||||
windowState.set('rootView', atom.rootView.serialize())
|
windowState.set('rootView', atom.rootView.serialize())
|
||||||
atom.packages.deactivatePackages()
|
atom.packages.deactivatePackages()
|
||||||
|
@ -30,16 +30,16 @@ module.exports = (grunt) ->
|
|||||||
themeMains.push(mainPath) if grunt.file.isFile(mainPath)
|
themeMains.push(mainPath) if grunt.file.isFile(mainPath)
|
||||||
importPaths.unshift(stylesheetsDir) if grunt.file.isDir(stylesheetsDir)
|
importPaths.unshift(stylesheetsDir) if grunt.file.isDir(stylesheetsDir)
|
||||||
|
|
||||||
grunt.log.writeln("Building LESS cache for #{configuration.join(', ').yellow}")
|
grunt.verbose.writeln("Building LESS cache for #{configuration.join(', ').yellow}")
|
||||||
lessCache = new LessCache
|
lessCache = new LessCache
|
||||||
cacheDir: directory
|
cacheDir: directory
|
||||||
resourcePath: path.resolve('.')
|
resourcePath: path.resolve('.')
|
||||||
importPaths: importPaths
|
importPaths: importPaths
|
||||||
|
|
||||||
for file in @filesSrc
|
for file in @filesSrc
|
||||||
grunt.log.writeln("File #{file.cyan} created in cache.")
|
grunt.verbose.writeln("File #{file.cyan} created in cache.")
|
||||||
lessCache.readFileSync(file)
|
lessCache.readFileSync(file)
|
||||||
|
|
||||||
for file in themeMains
|
for file in themeMains
|
||||||
grunt.log.writeln("File #{file.cyan} created in cache.")
|
grunt.verbose.writeln("File #{file.cyan} created in cache.")
|
||||||
lessCache.readFileSync(file)
|
lessCache.readFileSync(file)
|
||||||
|
@ -84,7 +84,7 @@ module.exports = (grunt) ->
|
|||||||
for name, version of packageDependencies
|
for name, version of packageDependencies
|
||||||
do (name, version) ->
|
do (name, version) ->
|
||||||
tasks.push (callback) ->
|
tasks.push (callback) ->
|
||||||
grunt.log.writeln("Publishing #{name}@#{version}")
|
grunt.verbose.writeln("Publishing #{name}@#{version}")
|
||||||
tag = "v#{version}"
|
tag = "v#{version}"
|
||||||
packageExists name, token, (error, exists) ->
|
packageExists name, token, (error, exists) ->
|
||||||
if error?
|
if error?
|
||||||
|
@ -2,6 +2,7 @@ fs = require 'fs'
|
|||||||
path = require 'path'
|
path = require 'path'
|
||||||
|
|
||||||
_ = require 'underscore-plus'
|
_ = require 'underscore-plus'
|
||||||
|
|
||||||
async = require 'async'
|
async = require 'async'
|
||||||
|
|
||||||
module.exports = (grunt) ->
|
module.exports = (grunt) ->
|
||||||
@ -10,7 +11,7 @@ module.exports = (grunt) ->
|
|||||||
packageSpecQueue = null
|
packageSpecQueue = null
|
||||||
|
|
||||||
runPackageSpecs = (callback) ->
|
runPackageSpecs = (callback) ->
|
||||||
passed = true
|
failedPackages = []
|
||||||
rootDir = grunt.config.get('atom.shellAppDir')
|
rootDir = grunt.config.get('atom.shellAppDir')
|
||||||
appDir = grunt.config.get('atom.appDir')
|
appDir = grunt.config.get('atom.appDir')
|
||||||
atomPath = path.join(appDir, 'atom.sh')
|
atomPath = path.join(appDir, 'atom.sh')
|
||||||
@ -23,10 +24,10 @@ module.exports = (grunt) ->
|
|||||||
opts:
|
opts:
|
||||||
cwd: packagePath
|
cwd: packagePath
|
||||||
env: _.extend({}, process.env, ATOM_PATH: rootDir)
|
env: _.extend({}, process.env, ATOM_PATH: rootDir)
|
||||||
grunt.log.writeln("Launching #{path.basename(packagePath)} specs.")
|
grunt.verbose.writeln "Launching #{path.basename(packagePath)} specs."
|
||||||
spawn options, (error, results, code) ->
|
spawn options, (error, results, code) ->
|
||||||
grunt.log.writeln()
|
|
||||||
passed = passed and not error and code is 0
|
failedPackages.push path.basename(packagePath) if error
|
||||||
callback()
|
callback()
|
||||||
|
|
||||||
modulesDirectory = path.resolve('node_modules')
|
modulesDirectory = path.resolve('node_modules')
|
||||||
@ -37,7 +38,7 @@ module.exports = (grunt) ->
|
|||||||
packageSpecQueue.push(packagePath)
|
packageSpecQueue.push(packagePath)
|
||||||
|
|
||||||
packageSpecQueue.concurrency = 1
|
packageSpecQueue.concurrency = 1
|
||||||
packageSpecQueue.drain = -> callback(null, passed)
|
packageSpecQueue.drain = -> callback(null, failedPackages)
|
||||||
|
|
||||||
runCoreSpecs = (callback) ->
|
runCoreSpecs = (callback) ->
|
||||||
contentsDir = grunt.config.get('atom.contentsDir')
|
contentsDir = grunt.config.get('atom.contentsDir')
|
||||||
@ -49,15 +50,20 @@ module.exports = (grunt) ->
|
|||||||
cmd: appPath
|
cmd: appPath
|
||||||
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}"]
|
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}"]
|
||||||
spawn options, (error, results, code) ->
|
spawn options, (error, results, code) ->
|
||||||
grunt.log.writeln()
|
|
||||||
packageSpecQueue.concurrency = 2
|
packageSpecQueue.concurrency = 2
|
||||||
callback(null, not error and code is 0)
|
callback(null, error)
|
||||||
|
|
||||||
grunt.registerTask 'run-specs', 'Run the specs', ->
|
grunt.registerTask 'run-specs', 'Run the specs', ->
|
||||||
done = @async()
|
done = @async()
|
||||||
startTime = Date.now()
|
startTime = Date.now()
|
||||||
|
|
||||||
async.parallel [runCoreSpecs, runPackageSpecs], (error, results) ->
|
async.parallel [runCoreSpecs, runPackageSpecs], (error, results) ->
|
||||||
[coreSpecPassed, packageSpecsPassed] = results
|
[coreSpecFailed, failedPackages] = results
|
||||||
elapsedTime = Math.round((Date.now() - startTime) / 100) / 10
|
elapsedTime = Math.round((Date.now() - startTime) / 100) / 10
|
||||||
grunt.log.writeln("Total spec time: #{elapsedTime}s")
|
grunt.verbose.writeln("Total spec time: #{elapsedTime}s")
|
||||||
done(coreSpecPassed and packageSpecsPassed)
|
failures = failedPackages
|
||||||
|
failures.push "atom core" if coreSpecFailed
|
||||||
|
|
||||||
|
grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0
|
||||||
|
|
||||||
|
done(!coreSpecFailed and failedPackages.length == 0)
|
||||||
|
@ -23,7 +23,7 @@ module.exports = (grunt) ->
|
|||||||
catch error
|
catch error
|
||||||
grunt.fatal(error)
|
grunt.fatal(error)
|
||||||
|
|
||||||
grunt.log.writeln("Copied #{source.cyan} to #{destination.cyan}.")
|
grunt.verbose.writeln("Copied #{source.cyan} to #{destination.cyan}.")
|
||||||
|
|
||||||
mkdir: (args...) ->
|
mkdir: (args...) ->
|
||||||
grunt.file.mkdir(args...)
|
grunt.file.mkdir(args...)
|
||||||
@ -32,9 +32,18 @@ module.exports = (grunt) ->
|
|||||||
grunt.file.delete(args..., force: true) if grunt.file.exists(args...)
|
grunt.file.delete(args..., force: true) if grunt.file.exists(args...)
|
||||||
|
|
||||||
spawn: (options, callback) ->
|
spawn: (options, callback) ->
|
||||||
grunt.util.spawn options, (error, results, code) ->
|
childProcess = require 'child_process'
|
||||||
grunt.log.errorlns results.stderr if results.stderr
|
stdout = []
|
||||||
callback(error, results, code)
|
stderr = []
|
||||||
|
error = null
|
||||||
|
proc = childProcess.spawn(options.cmd, options.args, options.opts)
|
||||||
|
proc.stdout.on 'data', (data) -> stdout.push(data.toString())
|
||||||
|
proc.stderr.on 'data', (data) -> stderr.push(data.toString())
|
||||||
|
proc.on 'exit', (exitCode, signal) ->
|
||||||
|
error = new Error(signal) if exitCode != 0
|
||||||
|
results = {stderr: stderr.join(''), stdout: stdout.join(''), code: exitCode}
|
||||||
|
grunt.log.error results.stderr if exitCode != 0
|
||||||
|
callback(error, results, exitCode)
|
||||||
|
|
||||||
isAtomPackage: (packagePath) ->
|
isAtomPackage: (packagePath) ->
|
||||||
try
|
try
|
||||||
|
@ -112,7 +112,7 @@ module.exports = (grunt) ->
|
|||||||
# Manually handle redirection so headers would not be sent for S3.
|
# Manually handle redirection so headers would not be sent for S3.
|
||||||
downloadAtomShell(version, response.headers.location, callback)
|
downloadAtomShell(version, response.headers.location, callback)
|
||||||
else if response.statusCode is 200
|
else if response.statusCode is 200
|
||||||
grunt.log.writeln("Downloading atom-shell version #{version.cyan}")
|
grunt.verbose.writeln("Downloading atom-shell version #{version.cyan}")
|
||||||
cacheDirectory = getCachePath(version)
|
cacheDirectory = getCachePath(version)
|
||||||
rm(cacheDirectory)
|
rm(cacheDirectory)
|
||||||
mkdir(cacheDirectory)
|
mkdir(cacheDirectory)
|
||||||
@ -146,7 +146,7 @@ module.exports = (grunt) ->
|
|||||||
downloadAtomShell version, url, callback
|
downloadAtomShell version, url, callback
|
||||||
|
|
||||||
unzipAtomShell = (zipPath, callback) ->
|
unzipAtomShell = (zipPath, callback) ->
|
||||||
grunt.log.writeln('Unzipping atom-shell')
|
grunt.verbose.writeln('Unzipping atom-shell')
|
||||||
directoryPath = path.dirname(zipPath)
|
directoryPath = path.dirname(zipPath)
|
||||||
|
|
||||||
if process.platform is 'darwin'
|
if process.platform is 'darwin'
|
||||||
@ -167,7 +167,7 @@ module.exports = (grunt) ->
|
|||||||
rebuildNativeModules = (previousVersion, callback) ->
|
rebuildNativeModules = (previousVersion, callback) ->
|
||||||
newVersion = getAtomShellVersion()
|
newVersion = getAtomShellVersion()
|
||||||
if newVersion and newVersion isnt previousVersion
|
if newVersion and newVersion isnt previousVersion
|
||||||
grunt.log.writeln("Rebuilding native modules for new atom-shell version #{newVersion.cyan}.")
|
grunt.verbose.writeln("Rebuilding native modules for new atom-shell version #{newVersion.cyan}.")
|
||||||
cmd = path.join('node_modules', '.bin', 'apm')
|
cmd = path.join('node_modules', '.bin', 'apm')
|
||||||
cmd += ".cmd" if process.platform is 'win32'
|
cmd += ".cmd" if process.platform is 'win32'
|
||||||
spawn {cmd, args: ['rebuild']}, (error) -> callback(error)
|
spawn {cmd, args: ['rebuild']}, (error) -> callback(error)
|
||||||
@ -186,7 +186,7 @@ module.exports = (grunt) ->
|
|||||||
currentAtomShellVersion = getAtomShellVersion()
|
currentAtomShellVersion = getAtomShellVersion()
|
||||||
if atomShellVersion isnt currentAtomShellVersion
|
if atomShellVersion isnt currentAtomShellVersion
|
||||||
if isAtomShellVersionCached(atomShellVersion)
|
if isAtomShellVersionCached(atomShellVersion)
|
||||||
grunt.log.writeln("Installing cached atom-shell #{atomShellVersion.cyan}")
|
grunt.verbose.writeln("Installing cached atom-shell #{atomShellVersion.cyan}")
|
||||||
installAtomShell(atomShellVersion)
|
installAtomShell(atomShellVersion)
|
||||||
rebuildNativeModules(currentAtomShellVersion, done)
|
rebuildNativeModules(currentAtomShellVersion, done)
|
||||||
else
|
else
|
||||||
@ -198,7 +198,7 @@ module.exports = (grunt) ->
|
|||||||
if error?
|
if error?
|
||||||
done(error)
|
done(error)
|
||||||
else
|
else
|
||||||
grunt.log.writeln("Installing atom-shell #{atomShellVersion.cyan}")
|
grunt.verbose.writeln("Installing atom-shell #{atomShellVersion.cyan}")
|
||||||
installAtomShell(atomShellVersion)
|
installAtomShell(atomShellVersion)
|
||||||
rebuildNativeModules(currentAtomShellVersion, done)
|
rebuildNativeModules(currentAtomShellVersion, done)
|
||||||
else
|
else
|
||||||
|
Loading…
Reference in New Issue
Block a user