mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-12-29 09:34:58 +03:00
Merge remote-tracking branch 'origin/master' into remove-docs
This commit is contained in:
commit
c4500af55a
@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.141.0"
|
||||
"atom-package-manager": "0.142.0"
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
"fs-plus": "2.x",
|
||||
"github-releases": "~0.2.0",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-atom-shell-installer": "^0.23.0",
|
||||
"grunt-atom-shell-installer": "^0.25.0",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt-coffeelint": "git+https://github.com/atom/grunt-coffeelint.git#cfb99aa99811d52687969532bd5a98011ed95bfe",
|
||||
"grunt-contrib-coffee": "~0.12.0",
|
||||
|
@ -78,6 +78,7 @@ If none of this works, do install Github for Windows and use its Git shell. Make
|
||||
```
|
||||
$env:GYP_MSVS_VERSION=2013
|
||||
```
|
||||
* If you are using Visual Studio 2013 and the build fails with some other error message this environment variable might still be required.
|
||||
|
||||
* Other `node-gyp` errors on first build attempt, even though the right node and python versions are installed.
|
||||
* Do try the build command one more time, as experience shows it often works on second try in many of these cases.
|
||||
|
@ -1,4 +1,5 @@
|
||||
{Point, Range} = require 'text-buffer'
|
||||
TextBuffer = require 'text-buffer'
|
||||
{Point, Range} = TextBuffer
|
||||
{Emitter, Disposable, CompositeDisposable} = require 'event-kit'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
@ -7,6 +8,7 @@ module.exports =
|
||||
BufferedProcess: require '../src/buffered-process'
|
||||
GitRepository: require '../src/git-repository'
|
||||
Notification: require '../src/notification'
|
||||
TextBuffer: TextBuffer
|
||||
Point: Point
|
||||
Range: Range
|
||||
Emitter: Emitter
|
||||
|
51
package.json
51
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.185.0",
|
||||
"version": "0.188.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@ -17,7 +17,7 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.21.0",
|
||||
"atomShellVersion": "0.21.3",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^3.1.3",
|
||||
@ -25,7 +25,7 @@
|
||||
"babel-core": "^4.0.2",
|
||||
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"clear-cut": "0.4.0",
|
||||
"coffee-cash": "0.7.0",
|
||||
"coffee-cash": "0.8.0",
|
||||
"coffee-script": "1.8.0",
|
||||
"coffeestack": "^1.1.1",
|
||||
"color": "^0.7.3",
|
||||
@ -47,7 +47,7 @@
|
||||
"nslog": "^2.0.0",
|
||||
"oniguruma": "^4.0.0",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "^3.3.1",
|
||||
"pathwatcher": "^3.3.3",
|
||||
"property-accessors": "^1.1.3",
|
||||
"q": "^1.1.2",
|
||||
"random-words": "0.0.1",
|
||||
@ -90,34 +90,35 @@
|
||||
"bracket-matcher": "0.71.0",
|
||||
"command-palette": "0.34.0",
|
||||
"deprecation-cop": "0.37.0",
|
||||
"dev-live-reload": "0.41.0",
|
||||
"encoding-selector": "0.18.0",
|
||||
"dev-live-reload": "0.42.0",
|
||||
"encoding-selector": "0.19.0",
|
||||
"exception-reporting": "0.24.0",
|
||||
"feedback": "0.34.0",
|
||||
"find-and-replace": "0.159.0",
|
||||
"fuzzy-finder": "0.69.0",
|
||||
"fuzzy-finder": "0.72.0",
|
||||
"git-diff": "0.54.0",
|
||||
"go-to-line": "0.30.0",
|
||||
"grammar-selector": "0.45.0",
|
||||
"grammar-selector": "0.46.0",
|
||||
"image-view": "0.49.0",
|
||||
"incompatible-packages": "0.22.0",
|
||||
"incompatible-packages": "0.24.0",
|
||||
"keybinding-resolver": "0.29.0",
|
||||
"link": "0.30.0",
|
||||
"markdown-preview": "0.137.0",
|
||||
"markdown-preview": "0.139.0",
|
||||
"metrics": "0.45.0",
|
||||
"notifications": "0.31.0",
|
||||
"open-on-github": "0.33.0",
|
||||
"notifications": "0.32.0",
|
||||
"open-on-github": "0.34.0",
|
||||
"package-generator": "0.38.0",
|
||||
"release-notes": "0.51.0",
|
||||
"settings-view": "0.183.0",
|
||||
"snippets": "0.78.0",
|
||||
"release-notes": "0.52.0",
|
||||
"settings-view": "0.184.0",
|
||||
"snippets": "0.80.0",
|
||||
"spell-check": "0.55.0",
|
||||
"status-bar": "0.60.0",
|
||||
"status-bar": "0.63.0",
|
||||
"styleguide": "0.44.0",
|
||||
"symbols-view": "0.86.0",
|
||||
"symbols-view": "0.88.0",
|
||||
"tabs": "0.67.0",
|
||||
"timecop": "0.31.0",
|
||||
"tree-view": "0.164.0",
|
||||
"update-package-dependencies": "0.8.0",
|
||||
"update-package-dependencies": "0.9.0",
|
||||
"welcome": "0.25.0",
|
||||
"whitespace": "0.29.0",
|
||||
"wrap-guide": "0.31.0",
|
||||
@ -126,30 +127,30 @@
|
||||
"language-coffee-script": "0.39.0",
|
||||
"language-csharp": "0.5.0",
|
||||
"language-css": "0.28.0",
|
||||
"language-gfm": "0.64.0",
|
||||
"language-gfm": "0.67.0",
|
||||
"language-git": "0.10.0",
|
||||
"language-go": "0.21.0",
|
||||
"language-html": "0.29.0",
|
||||
"language-hyperlink": "0.12.2",
|
||||
"language-java": "0.14.0",
|
||||
"language-javascript": "0.60.0",
|
||||
"language-javascript": "0.62.0",
|
||||
"language-json": "0.12.0",
|
||||
"language-less": "0.25.0",
|
||||
"language-make": "0.13.0",
|
||||
"language-make": "0.14.0",
|
||||
"language-mustache": "0.11.0",
|
||||
"language-objective-c": "0.15.0",
|
||||
"language-perl": "0.11.0",
|
||||
"language-perl": "0.15.0",
|
||||
"language-php": "0.21.0",
|
||||
"language-property-list": "0.8.0",
|
||||
"language-python": "0.32.0",
|
||||
"language-ruby": "0.49.0",
|
||||
"language-ruby-on-rails": "0.20.0",
|
||||
"language-sass": "0.35.0",
|
||||
"language-shellscript": "0.12.0",
|
||||
"language-sass": "0.36.0",
|
||||
"language-shellscript": "0.13.0",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.14.0",
|
||||
"language-text": "0.6.0",
|
||||
"language-todo": "0.16.0",
|
||||
"language-todo": "0.17.0",
|
||||
"language-toml": "0.15.0",
|
||||
"language-xml": "0.28.0",
|
||||
"language-yaml": "0.22.0"
|
||||
|
@ -31,8 +31,15 @@ function readEnvironmentVariables() {
|
||||
}
|
||||
|
||||
function removeNodeModules() {
|
||||
var fsPlus;
|
||||
try {
|
||||
require('fs-plus').removeSync(path.resolve(__dirname, '..', 'node_modules'));
|
||||
fsPlus = require('fs-plus');
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
fsPlus.removeSync(path.resolve(__dirname, '..', 'node_modules'));
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
|
@ -3,4 +3,4 @@
|
||||
set -e
|
||||
|
||||
script/build
|
||||
script/grunt mkrpm publish-build --stack --install-dir /usr
|
||||
script/grunt mkrpm publish-build --stack --no-color --install-dir /usr
|
||||
|
@ -152,3 +152,31 @@ describe "the `atom` global", ->
|
||||
loadSettings.initialPaths = [dir2, dir1]
|
||||
atom2 = Atom.loadOrCreate("editor")
|
||||
expect(atom2.state.stuff).toBe("cool")
|
||||
|
||||
describe "openInitialEmptyEditorIfNecessary", ->
|
||||
describe "when there are no paths set", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'getLoadSettings').andReturn(initialPaths: [])
|
||||
|
||||
it "opens an empty buffer", ->
|
||||
spyOn(atom.workspace, 'open')
|
||||
atom.openInitialEmptyEditorIfNecessary()
|
||||
expect(atom.workspace.open).toHaveBeenCalledWith(null)
|
||||
|
||||
describe "when there is already a buffer open", ->
|
||||
beforeEach ->
|
||||
waitsForPromise -> atom.workspace.open()
|
||||
|
||||
it "does not open an empty buffer", ->
|
||||
spyOn(atom.workspace, 'open')
|
||||
atom.openInitialEmptyEditorIfNecessary()
|
||||
expect(atom.workspace.open).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the project has a path", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'getLoadSettings').andReturn(initialPaths: ['something'])
|
||||
spyOn(atom.workspace, 'open')
|
||||
|
||||
it "does not open an empty buffer", ->
|
||||
atom.openInitialEmptyEditorIfNecessary()
|
||||
expect(atom.workspace.open).not.toHaveBeenCalled()
|
||||
|
@ -148,6 +148,16 @@ describe "CommandRegistry", ->
|
||||
grandchild.dispatchEvent(new CustomEvent('command-2', bubbles: true))
|
||||
expect(calls).toEqual []
|
||||
|
||||
describe "::add(selector, commandName, callback)", ->
|
||||
it "throws an error when called with an invalid selector", ->
|
||||
badSelector = '<>'
|
||||
addError = null
|
||||
try
|
||||
registry.add badSelector, 'foo:bar', ->
|
||||
catch error
|
||||
addError = error
|
||||
expect(addError.message).toContain(badSelector)
|
||||
|
||||
describe "::findCommands({target})", ->
|
||||
it "returns commands that can be invoked on the target or its ancestors", ->
|
||||
registry.add '.parent', 'namespace:command-1', ->
|
||||
|
@ -151,6 +151,14 @@ describe "ContextMenuManager", ->
|
||||
shouldDisplay = false
|
||||
expect(contextMenu.templateForEvent(dispatchedEvent)).toEqual []
|
||||
|
||||
it "throws an error when the selector is invalid", ->
|
||||
addError = null
|
||||
try
|
||||
contextMenu.add '<>': [{label: 'A', command: 'a'}]
|
||||
catch error
|
||||
addError = error
|
||||
expect(addError.message).toContain('<>')
|
||||
|
||||
describe "when the menus are specified in a legacy format", ->
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
9
spec/fixtures/packages/package-with-invalid-activation-commands/package.json
vendored
Normal file
9
spec/fixtures/packages/package-with-invalid-activation-commands/package.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "package-with-invalid-selectors",
|
||||
"version": "1.0.0",
|
||||
"activationCommands": {
|
||||
"<>": [
|
||||
"foo:bar"
|
||||
]
|
||||
}
|
||||
}
|
10
spec/fixtures/packages/package-with-invalid-context-menu/menus/menu.json
vendored
Normal file
10
spec/fixtures/packages/package-with-invalid-context-menu/menus/menu.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"context-menu": {
|
||||
"<>": [
|
||||
{
|
||||
"label": "Hello",
|
||||
"command:": "world"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
4
spec/fixtures/packages/package-with-invalid-context-menu/package.json
vendored
Normal file
4
spec/fixtures/packages/package-with-invalid-context-menu/package.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "package-with-invalid-context-menu",
|
||||
"version": "1.0.0"
|
||||
}
|
1
spec/fixtures/packages/package-with-invalid-grammar/grammars/grammar.json
vendored
Normal file
1
spec/fixtures/packages/package-with-invalid-grammar/grammars/grammar.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
><
|
4
spec/fixtures/packages/package-with-invalid-grammar/package.json
vendored
Normal file
4
spec/fixtures/packages/package-with-invalid-grammar/package.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "package-with-invalid-grammar",
|
||||
"version": "1.0.0"
|
||||
}
|
4
spec/fixtures/packages/package-with-invalid-settings/package.json
vendored
Normal file
4
spec/fixtures/packages/package-with-invalid-settings/package.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "package-with-invalid-settings",
|
||||
"version": "1.0.0"
|
||||
}
|
1
spec/fixtures/packages/package-with-invalid-settings/settings/settings.json
vendored
Normal file
1
spec/fixtures/packages/package-with-invalid-settings/settings/settings.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
><
|
@ -87,7 +87,7 @@ describe "Starting Atom", ->
|
||||
nestedDir = path.join(otherTempDirPath, "nested-dir")
|
||||
fs.mkdirSync(nestedDir)
|
||||
|
||||
runAtom [tempDirPath, otherTempDirPath, "--multi-folder"], {ATOM_HOME: AtomHome}, (client) ->
|
||||
runAtom [tempDirPath, otherTempDirPath], {ATOM_HOME: AtomHome}, (client) ->
|
||||
client
|
||||
.waitForExist("atom-workspace", 5000)
|
||||
.treeViewRootDirectories()
|
||||
@ -100,31 +100,6 @@ describe "Starting Atom", ->
|
||||
.treeViewRootDirectories()
|
||||
.then ({value}) -> expect(value).toEqual([tempDirPath, otherTempDirPath])
|
||||
|
||||
it "opens each path in its own window unless the --multi-folder flag is passed", ->
|
||||
runAtom [tempDirPath, otherTempDirPath], {ATOM_HOME: AtomHome}, (client) ->
|
||||
treeViewDirs = []
|
||||
|
||||
client
|
||||
.waitForExist("atom-workspace", 5000)
|
||||
.waitForWindowCount(2, 10000)
|
||||
.then ({value: windowHandles}) ->
|
||||
|
||||
@window(windowHandles[0])
|
||||
.waitForExist("atom-workspace")
|
||||
.treeViewRootDirectories()
|
||||
.then ({value}) ->
|
||||
expect(value).toHaveLength(1)
|
||||
treeViewDirs.push(value[0])
|
||||
|
||||
.window(windowHandles[1])
|
||||
.waitForExist("atom-workspace")
|
||||
.treeViewRootDirectories()
|
||||
.then ({value}) ->
|
||||
expect(value).toHaveLength(1)
|
||||
treeViewDirs.push(value[0])
|
||||
.then ->
|
||||
expect(treeViewDirs.sort()).toEqual([tempDirPath, otherTempDirPath].sort())
|
||||
|
||||
describe "when there is an existing window with no project path", ->
|
||||
describe "opening a directory", ->
|
||||
it "opens the directory in the existing window", ->
|
||||
|
@ -18,7 +18,6 @@ describe "PackageManager", ->
|
||||
expect(pack.metadata.name).toBe "package-with-index"
|
||||
|
||||
it "returns the package if it has an invalid keymap", ->
|
||||
spyOn(console, 'warn')
|
||||
pack = atom.packages.loadPackage("package-with-broken-keymap")
|
||||
expect(pack instanceof Package).toBe true
|
||||
expect(pack.metadata.name).toBe "package-with-broken-keymap"
|
||||
@ -30,10 +29,11 @@ describe "PackageManager", ->
|
||||
expect(pack.stylesheets.length).toBe 0
|
||||
|
||||
it "returns null if the package has an invalid package.json", ->
|
||||
spyOn(console, 'warn')
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
expect(atom.packages.loadPackage("package-with-broken-package-json")).toBeNull()
|
||||
expect(console.warn.callCount).toBe(1)
|
||||
expect(console.warn.argsForCall[0][0]).toContain("Failed to load package.json")
|
||||
expect(addErrorHandler.callCount).toBe 1
|
||||
expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to load the package-with-broken-package-json package")
|
||||
|
||||
it "returns null if the package is not found in any package directory", ->
|
||||
spyOn(console, 'warn')
|
||||
@ -212,6 +212,46 @@ describe "PackageManager", ->
|
||||
runs ->
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
|
||||
it "adds a notification when the activation commands are invalid", ->
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
expect(-> atom.packages.activatePackage('package-with-invalid-activation-commands')).not.toThrow()
|
||||
expect(addErrorHandler.callCount).toBe 1
|
||||
expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to activate the package-with-invalid-activation-commands package")
|
||||
|
||||
it "adds a notification when the context menu is invalid", ->
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
expect(-> atom.packages.activatePackage('package-with-invalid-context-menu')).not.toThrow()
|
||||
expect(addErrorHandler.callCount).toBe 1
|
||||
expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to activate the package-with-invalid-context-menu package")
|
||||
|
||||
it "adds a notification when the grammar is invalid", ->
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
|
||||
expect(-> atom.packages.activatePackage('package-with-invalid-grammar')).not.toThrow()
|
||||
|
||||
waitsFor ->
|
||||
addErrorHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(addErrorHandler.callCount).toBe 1
|
||||
expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to load a package-with-invalid-grammar package grammar")
|
||||
|
||||
it "adds a notification when the settings are invalid", ->
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
|
||||
expect(-> atom.packages.activatePackage('package-with-invalid-settings')).not.toThrow()
|
||||
|
||||
waitsFor ->
|
||||
addErrorHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(addErrorHandler.callCount).toBe 1
|
||||
expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to load the package-with-invalid-settings package settings")
|
||||
|
||||
describe "when the package has no main module", ->
|
||||
it "does not throw an exception", ->
|
||||
spyOn(console, "error")
|
||||
@ -257,11 +297,13 @@ describe "PackageManager", ->
|
||||
runs -> expect(activatedPackage.name).toBe 'package-with-main'
|
||||
|
||||
describe "when the package throws an error while loading", ->
|
||||
it "logs a warning instead of throwing an exception", ->
|
||||
it "adds a notification instead of throwing an exception", ->
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
spyOn(console, "warn")
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
expect(-> atom.packages.activatePackage("package-that-throws-an-exception")).not.toThrow()
|
||||
expect(console.warn).toHaveBeenCalled()
|
||||
expect(addErrorHandler.callCount).toBe 1
|
||||
expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to load the package-that-throws-an-exception package")
|
||||
|
||||
describe "when the package is not found", ->
|
||||
it "rejects the promise", ->
|
||||
|
@ -78,6 +78,20 @@ describe "Project", ->
|
||||
expect(directories.length).toBe 1
|
||||
expect(directories[0].getPath()).toBe tmp
|
||||
|
||||
it "gets the parent directory from the default directory provider if it's a local directory", ->
|
||||
tmp = temp.mkdirSync()
|
||||
atom.project.setPaths([path.join(tmp, "not-existing")])
|
||||
directories = atom.project.getDirectories()
|
||||
expect(directories.length).toBe 1
|
||||
expect(directories[0].getPath()).toBe tmp
|
||||
|
||||
it "only normalizes the directory path if it isn't on the local filesystem", ->
|
||||
nonLocalFsDirectory = "custom_proto://abc/def"
|
||||
atom.project.setPaths([nonLocalFsDirectory])
|
||||
directories = atom.project.getDirectories()
|
||||
expect(directories.length).toBe 1
|
||||
expect(directories[0].getPath()).toBe path.normalize(nonLocalFsDirectory)
|
||||
|
||||
it "tries to update repositories when a new RepositoryProvider is registered", ->
|
||||
tmp = temp.mkdirSync('atom-project')
|
||||
atom.project.setPaths([tmp])
|
||||
@ -422,6 +436,23 @@ describe "Project", ->
|
||||
expect(atom.project.getPaths()).toEqual([path.join(__dirname, "..", "src")])
|
||||
expect(atom.project.getRepositories()[0].isSubmodule("src")).toBe false
|
||||
|
||||
it "removes a path that is represented as a URI", ->
|
||||
ftpURI = "ftp://example.com/some/folder"
|
||||
directoryProvider =
|
||||
directoryForURISync: (uri) ->
|
||||
# Dummy implementation of Directory for which GitRepositoryProvider
|
||||
# will not try to create a GitRepository.
|
||||
getPath: -> ftpURI
|
||||
getSubdirectory: -> {}
|
||||
isRoot: -> true
|
||||
off: ->
|
||||
atom.packages.serviceHub.provide(
|
||||
"atom.directory-provider", "0.1.0", directoryProvider)
|
||||
atom.project.setPaths([ftpURI])
|
||||
expect(atom.project.getPaths()).toEqual [ftpURI]
|
||||
atom.project.removePath(ftpURI)
|
||||
expect(atom.project.getPaths()).toEqual []
|
||||
|
||||
describe ".relativize(path)", ->
|
||||
it "returns the path, relative to whichever root directory it is inside of", ->
|
||||
atom.project.addPath(temp.mkdirSync("another-path"))
|
||||
|
@ -56,6 +56,17 @@ describe "TextEditorComponent", ->
|
||||
afterEach ->
|
||||
contentNode.style.width = ''
|
||||
|
||||
describe "async updates", ->
|
||||
it "handles corrupted state gracefully", ->
|
||||
# trigger state updates, e.g. presenter.updateLinesState
|
||||
editor.insertNewline()
|
||||
|
||||
# simulate state corruption
|
||||
component.presenter.startRow = -1
|
||||
component.presenter.endRow = 9999
|
||||
|
||||
expect(nextAnimationFrame).not.toThrow()
|
||||
|
||||
describe "line rendering", ->
|
||||
it "renders the currently-visible lines plus the overdraw margin", ->
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
@ -1845,6 +1856,135 @@ describe "TextEditorComponent", ->
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[3, 4], [9, 0]]
|
||||
|
||||
describe "when soft wrap is enabled", ->
|
||||
beforeEach ->
|
||||
gutterNode = componentNode.querySelector('.gutter')
|
||||
|
||||
editor.setSoftWrapped(true)
|
||||
nextAnimationFrame()
|
||||
componentNode.style.width = 21 * charWidth + editor.getVerticalScrollbarWidth() + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
nextAnimationFrame()
|
||||
|
||||
describe "when the gutter is clicked", ->
|
||||
it "selects the clicked buffer row", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1)))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [2, 0]]
|
||||
|
||||
describe "when the gutter is meta-clicked", ->
|
||||
it "creates a new selection for the clicked buffer row", ->
|
||||
editor.setSelectedScreenRange([[1, 0], [1, 2]])
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(2), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[1, 0], [1, 2]], [[2, 0], [5, 0]]]
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(7), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[1, 0], [1, 2]], [[2, 0], [5, 0]], [[5, 0], [10, 0]]]
|
||||
|
||||
describe "when the gutter is shift-clicked", ->
|
||||
beforeEach ->
|
||||
editor.setSelectedScreenRange([[7, 4], [7, 6]])
|
||||
|
||||
describe "when the clicked row is before the current selection's tail", ->
|
||||
it "selects to the beginning of the clicked buffer row", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1), shiftKey: true))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [7, 4]]
|
||||
|
||||
describe "when the clicked row is after the current selection's tail", ->
|
||||
it "selects to the beginning of the buffer row following the clicked buffer row", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(11), shiftKey: true))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[7, 4], [16, 0]]
|
||||
|
||||
describe "when the gutter is clicked and dragged", ->
|
||||
describe "when dragging downward", ->
|
||||
it "selects the buffer rows between the start and end of the drag", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1)))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6)))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6)))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [10, 0]]
|
||||
|
||||
describe "when dragging upward", ->
|
||||
it "selects the buffer rows between the start and end of the drag", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6)))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(1)))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(1)))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [10, 0]]
|
||||
|
||||
describe "when the gutter is meta-clicked and dragged", ->
|
||||
beforeEach ->
|
||||
editor.setSelectedScreenRange([[7, 4], [7, 6]])
|
||||
|
||||
describe "when dragging downward", ->
|
||||
it "selects the buffer rows between the start and end of the drag", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(3), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(3), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[7, 4], [7, 6]], [[0, 0], [5, 0]]]
|
||||
|
||||
it "merges overlapping selections", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(7), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(7), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[0, 0], [10, 0]]]
|
||||
|
||||
describe "when dragging upward", ->
|
||||
it "selects the buffer rows between the start and end of the drag", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(17), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(11), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(11), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[7, 4], [7, 6]], [[10, 0], [20, 0]]]
|
||||
|
||||
it "merges overlapping selections", ->
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(17), metaKey: true))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(9), metaKey: true))
|
||||
nextAnimationFrame()
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(9), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[5, 0], [20, 0]]]
|
||||
|
||||
describe "when the gutter is shift-clicked and dragged", ->
|
||||
describe "when the shift-click is below the existing selection's tail", ->
|
||||
describe "when dragging downward", ->
|
||||
it "selects the buffer rows between the existing selection's tail and the end of the drag", ->
|
||||
editor.setSelectedScreenRange([[1, 4], [1, 7]])
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(7), shiftKey: true))
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(11)))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[1, 4], [16, 0]]
|
||||
|
||||
describe "when dragging upward", ->
|
||||
it "selects the buffer rows between the end of the drag and the tail of the existing selection", ->
|
||||
editor.setSelectedScreenRange([[1, 4], [1, 7]])
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(11), shiftKey: true))
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(7)))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[1, 4], [10, 0]]
|
||||
|
||||
describe "when the shift-click is above the existing selection's tail", ->
|
||||
describe "when dragging upward", ->
|
||||
it "selects the buffer rows between the end of the drag and the tail of the existing selection", ->
|
||||
editor.setSelectedScreenRange([[7, 4], [7, 6]])
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(3), shiftKey: true))
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(1)))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [7, 4]]
|
||||
|
||||
describe "when dragging downward", ->
|
||||
it "selects the buffer rows between the existing selection's tail and the end of the drag", ->
|
||||
editor.setSelectedScreenRange([[7, 4], [7, 6]])
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1), shiftKey: true))
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(3)))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [7, 4]]
|
||||
|
||||
describe "focus handling", ->
|
||||
inputNode = null
|
||||
|
||||
|
@ -118,7 +118,7 @@ class Atom extends Model
|
||||
|
||||
# Returns the load settings hash associated with the current window.
|
||||
@getLoadSettings: ->
|
||||
@loadSettings ?= JSON.parse(decodeURIComponent(location.search.substr(14)))
|
||||
@loadSettings ?= JSON.parse(decodeURIComponent(location.hash.substr(1)))
|
||||
cloned = _.deepClone(@loadSettings)
|
||||
# The loadSettings.windowState could be large, request it only when needed.
|
||||
cloned.__defineGetter__ 'windowState', =>
|
||||
@ -127,6 +127,11 @@ class Atom extends Model
|
||||
@getCurrentWindow().loadSettings.windowState = value
|
||||
cloned
|
||||
|
||||
@updateLoadSetting: (key, value) ->
|
||||
@getLoadSettings()
|
||||
@loadSettings[key] = value
|
||||
location.hash = encodeURIComponent(JSON.stringify(@loadSettings))
|
||||
|
||||
@getCurrentWindow: ->
|
||||
remote.getCurrentWindow()
|
||||
|
||||
@ -605,6 +610,8 @@ class Atom extends Model
|
||||
@setAutoHideMenuBar(newValue)
|
||||
@setAutoHideMenuBar(true) if @config.get('core.autoHideMenuBar')
|
||||
|
||||
@openInitialEmptyEditorIfNecessary()
|
||||
|
||||
maximize = dimensions?.maximized and process.platform isnt 'darwin'
|
||||
@displayWindow({maximize})
|
||||
|
||||
@ -629,6 +636,10 @@ class Atom extends Model
|
||||
|
||||
@windowEventHandler?.unsubscribe()
|
||||
|
||||
openInitialEmptyEditorIfNecessary: ->
|
||||
if @getLoadSettings().initialPaths?.length is 0 and @workspace.getPaneItems().length is 0
|
||||
@workspace.open(null)
|
||||
|
||||
###
|
||||
Section: Messaging the User
|
||||
###
|
||||
@ -745,10 +756,8 @@ class Atom extends Model
|
||||
|
||||
# Notify the browser project of the window's current project path
|
||||
watchProjectPath: ->
|
||||
onProjectPathChanged = =>
|
||||
ipc.send('window-command', 'project-path-changed', @project.getPaths())
|
||||
@subscribe @project.onDidChangePaths(onProjectPathChanged)
|
||||
onProjectPathChanged()
|
||||
@subscribe @project.onDidChangePaths =>
|
||||
@constructor.updateLoadSetting('initialPaths', @project.getPaths())
|
||||
|
||||
exit: (status) ->
|
||||
app = remote.require('app')
|
||||
|
@ -60,7 +60,7 @@ class AtomApplication
|
||||
exit: (status) -> app.exit(status)
|
||||
|
||||
constructor: (options) ->
|
||||
{@resourcePath, @version, @devMode, @safeMode, @socketPath, @enableMultiFolderProject} = options
|
||||
{@resourcePath, @version, @devMode, @safeMode, @socketPath} = options
|
||||
|
||||
# Normalize to make sure drive letter case is consistent on Windows
|
||||
@resourcePath = path.normalize(@resourcePath) if @resourcePath
|
||||
@ -348,11 +348,6 @@ class AtomApplication
|
||||
# :windowDimensions - Object with height and width keys.
|
||||
# :window - {AtomWindow} to open file paths in.
|
||||
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, window}={}) ->
|
||||
if pathsToOpen?.length > 1 and not @enableMultiFolderProject
|
||||
for pathToOpen in pathsToOpen
|
||||
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, window})
|
||||
return
|
||||
|
||||
pathsToOpen = (fs.normalize(pathToOpen) for pathToOpen in pathsToOpen)
|
||||
locationsToOpen = (@locationForPathToOpen(pathToOpen) for pathToOpen in pathsToOpen)
|
||||
|
||||
|
@ -61,33 +61,35 @@ class AtomWindow
|
||||
pathToOpen
|
||||
|
||||
loadSettings.initialPaths.sort()
|
||||
@projectPaths = loadSettings.initialPaths
|
||||
|
||||
@browserWindow.loadSettings = loadSettings
|
||||
@browserWindow.once 'window:loaded', =>
|
||||
@emit 'window:loaded'
|
||||
@loaded = true
|
||||
|
||||
@browserWindow.on 'project-path-changed', (@projectPaths) =>
|
||||
|
||||
@browserWindow.loadUrl @getUrl(loadSettings)
|
||||
@setLoadSettings(loadSettings)
|
||||
@browserWindow.focusOnWebView() if @isSpec
|
||||
|
||||
@openLocations(locationsToOpen) unless @isSpecWindow()
|
||||
hasPathToOpen = not (locationsToOpen.length is 1 and not locationsToOpen[0].pathToOpen?)
|
||||
@openLocations(locationsToOpen) if hasPathToOpen and not @isSpecWindow()
|
||||
|
||||
getUrl: (loadSettingsObj) ->
|
||||
setLoadSettings: (loadSettingsObj) ->
|
||||
# Ignore the windowState when passing loadSettings via URL, since it could
|
||||
# be quite large.
|
||||
loadSettings = _.clone(loadSettingsObj)
|
||||
delete loadSettings['windowState']
|
||||
|
||||
url.format
|
||||
@browserWindow.loadUrl url.format
|
||||
protocol: 'file'
|
||||
pathname: "#{@resourcePath}/static/index.html"
|
||||
slashes: true
|
||||
query: {loadSettings: JSON.stringify(loadSettings)}
|
||||
hash: encodeURIComponent(JSON.stringify(loadSettings))
|
||||
|
||||
hasProjectPath: -> @projectPaths?.length > 0
|
||||
getLoadSettings: ->
|
||||
hash = url.parse(@browserWindow.webContents.getUrl()).hash.substr(1)
|
||||
JSON.parse(decodeURIComponent(hash))
|
||||
|
||||
hasProjectPath: -> @getLoadSettings().initialPaths?.length > 0
|
||||
|
||||
setupContextMenu: ->
|
||||
ContextMenu = null
|
||||
@ -102,7 +104,7 @@ class AtomWindow
|
||||
true
|
||||
|
||||
containsPath: (pathToCheck) ->
|
||||
@projectPaths.some (projectPath) ->
|
||||
@getLoadSettings().initialPaths?.some (projectPath) ->
|
||||
if not projectPath
|
||||
false
|
||||
else if not pathToCheck
|
||||
|
@ -118,7 +118,6 @@ parseCommandLine = ->
|
||||
options.alias('v', 'version').boolean('v').describe('v', 'Print the version.')
|
||||
options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.')
|
||||
options.string('socket-path')
|
||||
options.boolean('multi-folder')
|
||||
args = options.argv
|
||||
|
||||
if args.help
|
||||
@ -140,7 +139,6 @@ parseCommandLine = ->
|
||||
pidToKillWhenClosed = args['pid'] if args['wait']
|
||||
logFile = args['log-file']
|
||||
socketPath = args['socket-path']
|
||||
enableMultiFolderProject = args['multi-folder']
|
||||
|
||||
if args['resource-path']
|
||||
devMode = true
|
||||
@ -166,6 +164,6 @@ parseCommandLine = ->
|
||||
process.env.PATH = args['path-environment'] if args['path-environment']
|
||||
|
||||
{resourcePath, pathsToOpen, executedFrom, test, version, pidToKillWhenClosed,
|
||||
devMode, safeMode, newWindow, specDirectory, logFile, socketPath, enableMultiFolderProject}
|
||||
devMode, safeMode, newWindow, specDirectory, logFile, socketPath}
|
||||
|
||||
start()
|
||||
|
@ -76,11 +76,23 @@ installContextMenu = (callback) ->
|
||||
installMenu directoryKeyPath, '%1', ->
|
||||
installMenu(backgroundKeyPath, '%V', callback)
|
||||
|
||||
isAscii = (text) ->
|
||||
index = 0
|
||||
while index < text.length
|
||||
return false if text.charCodeAt(index) > 127
|
||||
index++
|
||||
true
|
||||
|
||||
# Get the user's PATH environment variable registry value.
|
||||
getPath = (callback) ->
|
||||
spawnReg ['query', environmentKeyPath, '/v', 'Path'], (error, stdout) ->
|
||||
if error?
|
||||
if error.code is 1
|
||||
# FIXME Don't overwrite path when reading value is disabled
|
||||
# https://github.com/atom/atom/issues/5092
|
||||
if stdout.indexOf('ERROR: Registry editing has been disabled by your administrator.') isnt -1
|
||||
return callback(error)
|
||||
|
||||
# The query failed so the Path does not exist yet in the registry
|
||||
return callback(null, '')
|
||||
else
|
||||
@ -96,7 +108,12 @@ getPath = (callback) ->
|
||||
segments = lines[lines.length - 1]?.split(' ')
|
||||
if segments[1] is 'Path' and segments.length >= 3
|
||||
pathEnv = segments?[3..].join(' ')
|
||||
callback(null, pathEnv)
|
||||
if isAscii(pathEnv)
|
||||
callback(null, pathEnv)
|
||||
else
|
||||
# FIXME Don't corrupt non-ASCII PATH values
|
||||
# https://github.com/atom/atom/issues/5063
|
||||
callback(new Error('PATH contains non-ASCII values'))
|
||||
else
|
||||
callback(new Error('Registry query for PATH failed'))
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
{specificity} = require 'clear-cut'
|
||||
_ = require 'underscore-plus'
|
||||
{$} = require './space-pen-extensions'
|
||||
{validateSelector} = require './selector-validator'
|
||||
|
||||
SequenceCount = 0
|
||||
SpecificityCache = {}
|
||||
@ -87,6 +88,7 @@ class CommandRegistry
|
||||
return disposable
|
||||
|
||||
if typeof target is 'string'
|
||||
validateSelector(target)
|
||||
@addSelectorBasedListener(target, commandName, callback)
|
||||
else
|
||||
@addInlineListener(target, commandName, callback)
|
||||
|
@ -8,6 +8,7 @@ fs = require 'fs-plus'
|
||||
{Disposable} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
MenuHelpers = require './menu-helpers'
|
||||
{validateSelector} = require './selector-validator'
|
||||
|
||||
SpecificityCache = {}
|
||||
|
||||
@ -123,6 +124,7 @@ class ContextMenuManager
|
||||
addedItemSets = []
|
||||
|
||||
for selector, items of itemsBySelector
|
||||
validateSelector(selector)
|
||||
itemSet = new ContextMenuItemSet(selector, items)
|
||||
addedItemSets.push(itemSet)
|
||||
@itemSets.push(itemSet)
|
||||
|
@ -16,10 +16,10 @@ class DefaultDirectoryProvider
|
||||
directoryForURISync: (uri) ->
|
||||
projectPath = path.normalize(uri)
|
||||
|
||||
directoryPath = if fs.isDirectorySync(projectPath)
|
||||
projectPath
|
||||
else
|
||||
directoryPath = if !fs.isDirectorySync(projectPath) and fs.isDirectorySync(path.dirname(projectPath))
|
||||
path.dirname(projectPath)
|
||||
else
|
||||
projectPath
|
||||
|
||||
new Directory(directoryPath)
|
||||
|
||||
|
@ -200,7 +200,7 @@ registerBuiltins = (devMode) ->
|
||||
cache.builtins.atom = atomCoffeePath if fs.isFileSync(atomCoffeePath)
|
||||
cache.builtins.atom ?= path.join(cache.resourcePath, 'exports', 'atom.js')
|
||||
|
||||
atomShellRoot = path.join(process.resourcesPath, 'atom')
|
||||
atomShellRoot = path.join(process.resourcesPath, 'atom.asar')
|
||||
|
||||
commonRoot = path.join(atomShellRoot, 'common', 'api', 'lib')
|
||||
commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'screen', 'shell']
|
||||
|
@ -343,16 +343,18 @@ class PackageManager
|
||||
|
||||
try
|
||||
metadata = Package.loadMetadata(packagePath) ? {}
|
||||
if metadata.theme
|
||||
pack = new ThemePackage(packagePath, metadata)
|
||||
else
|
||||
pack = new Package(packagePath, metadata)
|
||||
pack.load()
|
||||
@loadedPackages[pack.name] = pack
|
||||
@emitter.emit 'did-load-package', pack
|
||||
return pack
|
||||
catch error
|
||||
console.warn "Failed to load package.json '#{path.basename(packagePath)}'", error.stack ? error
|
||||
@handleMetadataError(error, packagePath)
|
||||
return null
|
||||
|
||||
if metadata.theme
|
||||
pack = new ThemePackage(packagePath, metadata)
|
||||
else
|
||||
pack = new Package(packagePath, metadata)
|
||||
pack.load()
|
||||
@loadedPackages[pack.name] = pack
|
||||
@emitter.emit 'did-load-package', pack
|
||||
return pack
|
||||
else
|
||||
console.warn "Could not resolve '#{nameOrPath}' to a package path"
|
||||
null
|
||||
@ -421,3 +423,10 @@ class PackageManager
|
||||
pack.deactivate()
|
||||
delete @activePackages[pack.name]
|
||||
@emitter.emit 'did-deactivate-package', pack
|
||||
|
||||
handleMetadataError: (error, packagePath) ->
|
||||
metadataPath = path.join(packagePath, 'package.json')
|
||||
detail = "#{error.message} in #{metadataPath}"
|
||||
stack = "#{error.stack}\n at #{metadataPath}:1:1"
|
||||
message = "Failed to load the #{path.basename(packagePath)} package"
|
||||
atom.notifications.addError(message, {stack, detail, dismissable: true})
|
||||
|
@ -126,9 +126,8 @@ class Package
|
||||
@loadStylesheets()
|
||||
@settingsPromise = @loadSettings()
|
||||
@requireMainModule() unless @hasActivationCommands()
|
||||
|
||||
catch error
|
||||
console.warn "Failed to load package named '#{@name}'", error.stack ? error
|
||||
@handleError("Failed to load the #{@name} package", error)
|
||||
this
|
||||
|
||||
reset: ->
|
||||
@ -144,11 +143,14 @@ class Package
|
||||
unless @activationDeferred?
|
||||
@activationDeferred = Q.defer()
|
||||
@measure 'activateTime', =>
|
||||
@activateResources()
|
||||
if @hasActivationCommands()
|
||||
@subscribeToActivationCommands()
|
||||
else
|
||||
@activateNow()
|
||||
try
|
||||
@activateResources()
|
||||
if @hasActivationCommands()
|
||||
@subscribeToActivationCommands()
|
||||
else
|
||||
@activateNow()
|
||||
catch error
|
||||
@handleError("Failed to activate the #{@name} package", error)
|
||||
|
||||
Q.all([@grammarsPromise, @settingsPromise, @activationDeferred.promise])
|
||||
|
||||
@ -160,8 +162,8 @@ class Package
|
||||
@mainModule.activate?(atom.packages.getPackageState(@name) ? {})
|
||||
@mainActivated = true
|
||||
@activateServices()
|
||||
catch e
|
||||
console.warn "Failed to activate package named '#{@name}'", e.stack
|
||||
catch error
|
||||
@handleError("Failed to activate the #{@name} package", error)
|
||||
|
||||
@activationDeferred?.resolve()
|
||||
|
||||
@ -200,7 +202,16 @@ class Package
|
||||
activateResources: ->
|
||||
@activationDisposables = new CompositeDisposable
|
||||
@activationDisposables.add(atom.keymaps.add(keymapPath, map)) for [keymapPath, map] in @keymaps
|
||||
@activationDisposables.add(atom.contextMenu.add(map['context-menu'])) for [menuPath, map] in @menus when map['context-menu']?
|
||||
|
||||
for [menuPath, map] in @menus when map['context-menu']?
|
||||
try
|
||||
@activationDisposables.add(atom.contextMenu.add(map['context-menu']))
|
||||
catch error
|
||||
if error.code is 'EBADSELECTOR'
|
||||
error.message += " in #{menuPath}"
|
||||
error.stack += "\n at #{menuPath}:1:1"
|
||||
throw error
|
||||
|
||||
@activationDisposables.add(atom.menu.add(map['menu'])) for [menuPath, map] in @menus when map['menu']?
|
||||
|
||||
unless @grammarsActivated
|
||||
@ -218,6 +229,7 @@ class Package
|
||||
for name, {versions} of @metadata.consumedServices
|
||||
for version, methodName of versions
|
||||
@activationDisposables.add atom.packages.serviceHub.consume(name, version, @mainModule[methodName].bind(@mainModule))
|
||||
return
|
||||
|
||||
loadKeymaps: ->
|
||||
if @bundledPackage and packagesCache[@name]?
|
||||
@ -290,7 +302,9 @@ class Package
|
||||
loadGrammar = (grammarPath, callback) =>
|
||||
atom.grammars.readGrammar grammarPath, (error, grammar) =>
|
||||
if error?
|
||||
console.warn("Failed to load grammar: #{grammarPath}", error.stack ? error)
|
||||
detail = "#{error.message} in #{grammarPath}"
|
||||
stack = "#{error.stack}\n at #{grammarPath}:1:1"
|
||||
atom.notifications.addFatalError("Failed to load a #{@name} package grammar", {stack, detail, dismissable: true})
|
||||
else
|
||||
grammar.packageName = @name
|
||||
@grammars.push(grammar)
|
||||
@ -309,7 +323,9 @@ class Package
|
||||
loadSettingsFile = (settingsPath, callback) =>
|
||||
ScopedProperties.load settingsPath, (error, settings) =>
|
||||
if error?
|
||||
console.warn("Failed to load package settings: #{settingsPath}", error.stack ? error)
|
||||
detail = "#{error.message} in #{settingsPath}"
|
||||
stack = "#{error.stack}\n at #{settingsPath}:1:1"
|
||||
atom.notifications.addFatalError("Failed to load the #{@name} package settings", {stack, detail, dismissable: true})
|
||||
else
|
||||
@settings.push(settings)
|
||||
settings.activate() if @settingsActivated
|
||||
@ -370,7 +386,7 @@ class Package
|
||||
@activateStylesheets()
|
||||
|
||||
requireMainModule: ->
|
||||
return @mainModule if @mainModule?
|
||||
return @mainModule if @mainModuleRequired
|
||||
unless @isCompatible()
|
||||
console.warn """
|
||||
Failed to require the main module of '#{@name}' because it requires an incompatible native module.
|
||||
@ -378,7 +394,9 @@ class Package
|
||||
"""
|
||||
return
|
||||
mainModulePath = @getMainModulePath()
|
||||
@mainModule = require(mainModulePath) if fs.isFileSync(mainModulePath)
|
||||
if fs.isFileSync(mainModulePath)
|
||||
@mainModuleRequired = true
|
||||
@mainModule = require(mainModulePath)
|
||||
|
||||
getMainModulePath: ->
|
||||
return @mainModulePath if @resolvedMainModulePath
|
||||
@ -409,7 +427,15 @@ class Package
|
||||
do (selector, command) =>
|
||||
# Add dummy command so it appears in menu.
|
||||
# The real command will be registered on package activation
|
||||
@activationCommandSubscriptions.add atom.commands.add selector, command, ->
|
||||
try
|
||||
@activationCommandSubscriptions.add atom.commands.add selector, command, ->
|
||||
catch error
|
||||
if error.code is 'EBADSELECTOR'
|
||||
metadataPath = path.join(@path, 'package.json')
|
||||
error.message += " in #{metadataPath}"
|
||||
error.stack += "\n at #{metadataPath}:1:1"
|
||||
throw error
|
||||
|
||||
@activationCommandSubscriptions.add atom.commands.onWillDispatch (event) =>
|
||||
return unless event.type is command
|
||||
currentTarget = event.target
|
||||
@ -528,3 +554,17 @@ class Package
|
||||
@compatible = @incompatibleModules.length is 0
|
||||
else
|
||||
@compatible = true
|
||||
|
||||
handleError: (message, error) ->
|
||||
if error.filename and error.location and (error instanceof SyntaxError)
|
||||
location = "#{error.filename}:#{error.location.first_line + 1}:#{error.location.first_column + 1}"
|
||||
detail = "#{error.message} in #{location}"
|
||||
stack = """
|
||||
SyntaxError: #{error.message}
|
||||
at #{location}
|
||||
"""
|
||||
else
|
||||
detail = error.message
|
||||
stack = error.stack ? error
|
||||
|
||||
atom.notifications.addFatalError(message, {stack, detail, dismissable: true})
|
||||
|
@ -683,6 +683,8 @@ class Pane extends Model
|
||||
atom.notifications.addWarning("Unable to save file '#{error.path}'", detail: error.message)
|
||||
else if error.code is 'EROFS' and error.path?
|
||||
atom.notifications.addWarning("Unable to save file: Read-only file system '#{error.path}'")
|
||||
else if error.code is 'ENOSPC' and error.path?
|
||||
atom.notifications.addWarning("Unable to save file: No space left on device '#{error.path}'")
|
||||
else if errorMatch = /ENOTDIR, not a directory '([^']+)'/.exec(error.message)
|
||||
fileName = errorMatch[1]
|
||||
atom.notifications.addWarning("Unable to save file: A directory in the path '#{fileName}' could not be written to")
|
||||
|
@ -53,7 +53,7 @@ class Project extends Model
|
||||
# to either a {Repository} or null. Ideally, the {Directory} would be used
|
||||
# as the key; however, there can be multiple {Directory} objects created for
|
||||
# the same real path, so it is not a good key.
|
||||
@repositoryPromisesByPath = new Map();
|
||||
@repositoryPromisesByPath = new Map()
|
||||
|
||||
# Note that the GitRepositoryProvider is registered synchronously so that
|
||||
# it is available immediately on startup.
|
||||
@ -109,6 +109,12 @@ class Project extends Model
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Public: Invoke the given callback when the project paths change.
|
||||
#
|
||||
# * `callback` {Function} to be called after the project paths change.
|
||||
# * `projectPaths` An {Array} of {String} project paths.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangePaths: (callback) ->
|
||||
@emitter.on 'did-change-paths', callback
|
||||
|
||||
@ -221,7 +227,9 @@ class Project extends Model
|
||||
#
|
||||
# * `projectPath` {String} The path to remove.
|
||||
removePath: (projectPath) ->
|
||||
projectPath = path.normalize(projectPath)
|
||||
# The projectPath may be a URI, in which case it should not be normalized.
|
||||
unless projectPath in @getPaths()
|
||||
projectPath = path.normalize(projectPath)
|
||||
|
||||
indexToRemove = null
|
||||
for directory, i in @rootDirectories
|
||||
|
@ -1,6 +1,6 @@
|
||||
module.exports =
|
||||
class ScrollbarCornerComponent
|
||||
constructor: () ->
|
||||
constructor: ->
|
||||
@domNode = document.createElement('div')
|
||||
@domNode.classList.add('scrollbar-corner')
|
||||
|
||||
|
@ -251,7 +251,8 @@ class Selection extends Model
|
||||
|
||||
# Public: Selects all the text in the buffer.
|
||||
selectAll: ->
|
||||
@setBufferRange(@editor.buffer.getRange(), autoscroll: false)
|
||||
@editor.unfoldAll()
|
||||
@setBufferRange(@editor.buffer.getRange(), autoscroll: false, preserveFolds: true)
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the
|
||||
# beginning of the line.
|
||||
|
28
src/selector-validator.coffee
Normal file
28
src/selector-validator.coffee
Normal file
@ -0,0 +1,28 @@
|
||||
selectorCache = null
|
||||
testElement = null
|
||||
|
||||
# Parses CSS selectors and memoizes their validity so each selector will only
|
||||
# be parsed once.
|
||||
exports.isSelectorValid = (selector) ->
|
||||
selectorCache ?= {}
|
||||
cachedValue = selectorCache[selector]
|
||||
return cachedValue if cachedValue?
|
||||
|
||||
testElement ?= document.createElement('div')
|
||||
try
|
||||
# querySelector appears to be faster than webkitMatchesSelector
|
||||
# http://jsperf.com/query-vs-matches
|
||||
testElement.querySelector(selector)
|
||||
selectorCache[selector] = true
|
||||
true
|
||||
catch selectorError
|
||||
selectorCache[selector] = false
|
||||
false
|
||||
|
||||
# Parse the given CSS selector and throw an error if it is invalid.
|
||||
exports.validateSelector = (selector) ->
|
||||
return if exports.isSelectorValid(selector)
|
||||
|
||||
error = new Error("'#{selector}' is not a valid selector")
|
||||
error.code = 'EBADSELECTOR'
|
||||
throw error
|
@ -404,29 +404,33 @@ class TextEditorComponent
|
||||
|
||||
onGutterClick: (event) =>
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
clickedBufferRow = @editor.bufferRowForScreenRow(clickedRow)
|
||||
|
||||
@editor.setSelectedScreenRange([[clickedRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
|
||||
@editor.setSelectedBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) =>
|
||||
dragRow = screenPosition.row
|
||||
if dragRow < clickedRow # dragging up
|
||||
@editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
|
||||
dragBufferRow = @editor.bufferRowForScreenRow(dragRow)
|
||||
if dragBufferRow < clickedBufferRow # dragging up
|
||||
@editor.setSelectedBufferRange([[dragBufferRow, 0], [clickedBufferRow + 1, 0]], preserveFolds: true)
|
||||
else
|
||||
@editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true)
|
||||
@editor.setSelectedBufferRange([[clickedBufferRow, 0], [dragBufferRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
onGutterMetaClick: (event) =>
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
clickedBufferRow = @editor.bufferRowForScreenRow(clickedRow)
|
||||
|
||||
bufferRange = @editor.bufferRangeForScreenRange([[clickedRow, 0], [clickedRow + 1, 0]])
|
||||
bufferRange = new Range([clickedBufferRow, 0], [clickedBufferRow + 1, 0])
|
||||
rowSelection = @editor.addSelectionForBufferRange(bufferRange, preserveFolds: true)
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) =>
|
||||
dragRow = screenPosition.row
|
||||
dragBufferRow = @editor.bufferRowForScreenRow(dragRow)
|
||||
|
||||
if dragRow < clickedRow # dragging up
|
||||
rowSelection.setScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true)
|
||||
if dragBufferRow < clickedBufferRow # dragging up
|
||||
rowSelection.setBufferRange([[dragBufferRow, 0], [clickedBufferRow + 1, 0]], preserveFolds: true)
|
||||
else
|
||||
rowSelection.setScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true)
|
||||
rowSelection.setBufferRange([[clickedBufferRow, 0], [dragBufferRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
# After updating the selected screen range, merge overlapping selections
|
||||
@editor.mergeIntersectingSelections(preserveFolds: true)
|
||||
@ -439,19 +443,23 @@ class TextEditorComponent
|
||||
|
||||
onGutterShiftClick: (event) =>
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
clickedBufferRow = @editor.bufferRowForScreenRow(clickedRow)
|
||||
tailPosition = @editor.getLastSelection().getTailScreenPosition()
|
||||
tailBufferPosition = @editor.bufferPositionForScreenPosition(tailPosition)
|
||||
|
||||
if clickedRow < tailPosition.row
|
||||
@editor.selectToScreenPosition([clickedRow, 0])
|
||||
@editor.selectToBufferPosition([clickedBufferRow, 0])
|
||||
else
|
||||
@editor.selectToScreenPosition([clickedRow + 1, 0])
|
||||
@editor.selectToBufferPosition([clickedBufferRow + 1, 0])
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) =>
|
||||
dragRow = screenPosition.row
|
||||
dragBufferRow = @editor.bufferRowForScreenRow(dragRow)
|
||||
if dragRow < tailPosition.row # dragging up
|
||||
@editor.setSelectedScreenRange([[dragRow, 0], tailPosition], preserveFolds: true)
|
||||
@editor.setSelectedBufferRange([[dragBufferRow, 0], tailBufferPosition], preserveFolds: true)
|
||||
else
|
||||
@editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true)
|
||||
@editor.setSelectedBufferRange([tailBufferPosition, [dragBufferRow + 1, 0]], preserveFolds: true)
|
||||
|
||||
|
||||
onStylesheetsChanged: (styleElement) =>
|
||||
return unless @performedInitialMeasurement
|
||||
|
@ -61,6 +61,7 @@ class TextEditorPresenter
|
||||
@[flagName] = true
|
||||
else
|
||||
fn.apply(this)
|
||||
@[flagName] = false
|
||||
|
||||
@emitDidUpdateState()
|
||||
|
||||
@ -69,6 +70,11 @@ class TextEditorPresenter
|
||||
getState: ->
|
||||
@updating = true
|
||||
|
||||
@updateContentDimensions()
|
||||
@updateScrollbarDimensions()
|
||||
@updateStartRow()
|
||||
@updateEndRow()
|
||||
|
||||
@updateFocusedState() if @shouldUpdateFocusedState
|
||||
@updateHeightState() if @shouldUpdateHeightState
|
||||
@updateVerticalScrollState() if @shouldUpdateVerticalScrollState
|
||||
@ -83,20 +89,6 @@ class TextEditorPresenter
|
||||
@updateGutterState() if @shouldUpdateGutterState
|
||||
@updateLineNumbersState() if @shouldUpdateLineNumbersState
|
||||
|
||||
@shouldUpdateFocusedState = false
|
||||
@shouldUpdateHeightState = false
|
||||
@shouldUpdateVerticalScrollState = false
|
||||
@shouldUpdateHorizontalScrollState = false
|
||||
@shouldUpdateScrollbarsState = false
|
||||
@shouldUpdateHiddenInputState = false
|
||||
@shouldUpdateContentState = false
|
||||
@shouldUpdateDecorations = false
|
||||
@shouldUpdateLinesState = false
|
||||
@shouldUpdateCursorsState = false
|
||||
@shouldUpdateOverlaysState = false
|
||||
@shouldUpdateGutterState = false
|
||||
@shouldUpdateLineNumbersState = false
|
||||
|
||||
@updating = false
|
||||
|
||||
@state
|
||||
|
@ -2776,8 +2776,11 @@ class TextEditor extends Model
|
||||
|
||||
# Remove any {Fold}s found that intersect the given buffer row.
|
||||
destroyFoldsIntersectingBufferRange: (bufferRange) ->
|
||||
for row in [bufferRange.start.row..bufferRange.end.row]
|
||||
@unfoldBufferRow(row)
|
||||
@unfoldBufferRow(bufferRange.start.row)
|
||||
@unfoldBufferRow(bufferRange.end.row)
|
||||
|
||||
for row in [bufferRange.end.row..bufferRange.start.row]
|
||||
fold.destroy() for fold in @displayBuffer.foldsStartingAtBufferRow(row)
|
||||
|
||||
# {Delegates to: DisplayBuffer.largestFoldContainingBufferRow}
|
||||
largestFoldContainingBufferRow: (bufferRow) ->
|
||||
|
@ -2,7 +2,7 @@ path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
EmitterMixin = require('emissary').Emitter
|
||||
{Emitter, Disposable} = require 'event-kit'
|
||||
{Emitter, Disposable, CompositeDisposable} = require 'event-kit'
|
||||
{File} = require 'pathwatcher'
|
||||
fs = require 'fs-plus'
|
||||
Q = require 'q'
|
||||
@ -241,7 +241,8 @@ class ThemeManager
|
||||
throw new Error("Could not find a file at path '#{stylesheetPath}'")
|
||||
|
||||
unwatchUserStylesheet: ->
|
||||
@userStylesheetFile?.off()
|
||||
@userStylsheetSubscriptions?.dispose()
|
||||
@userStylsheetSubscriptions = null
|
||||
@userStylesheetFile = null
|
||||
@userStyleSheetDisposable?.dispose()
|
||||
@userStyleSheetDisposable = null
|
||||
@ -254,7 +255,11 @@ class ThemeManager
|
||||
|
||||
try
|
||||
@userStylesheetFile = new File(userStylesheetPath)
|
||||
@userStylesheetFile.on 'contents-changed moved removed', => @loadUserStylesheet()
|
||||
@userStylsheetSubscriptions = new CompositeDisposable()
|
||||
reloadStylesheet = => @loadUserStylesheet()
|
||||
@userStylsheetSubscriptions.add(@userStylesheetFile.onDidChange(reloadStylesheet))
|
||||
@userStylsheetSubscriptions.add(@userStylesheetFile.onDidRename(reloadStylesheet))
|
||||
@userStylsheetSubscriptions.add(@userStylesheetFile.onDidDelete(reloadStylesheet))
|
||||
catch error
|
||||
message = """
|
||||
Unable to watch path: `#{path.basename(userStylesheetPath)}`. Make sure
|
||||
|
5
static/bootstrap-overrides.less
vendored
5
static/bootstrap-overrides.less
vendored
@ -21,3 +21,8 @@ h5,
|
||||
h6 {
|
||||
font-family: inherit; // inherit from themes
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: inherit; // inherit from html
|
||||
font-size: inherit; // inherit from html
|
||||
}
|
||||
|
@ -14,8 +14,7 @@ window.onload = function() {
|
||||
cacheDir = path.join(cacheDir, 'root');
|
||||
}
|
||||
|
||||
// Skip "?loadSettings=".
|
||||
var rawLoadSettings = decodeURIComponent(location.search.substr(14));
|
||||
var rawLoadSettings = decodeURIComponent(location.hash.substr(1));
|
||||
var loadSettings;
|
||||
try {
|
||||
loadSettings = JSON.parse(rawLoadSettings);
|
||||
|
@ -3,13 +3,16 @@
|
||||
|
||||
@font-face { .octicon-font(); }
|
||||
|
||||
html {
|
||||
font-family: @font-family;
|
||||
font-size: @font-size;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-family: @font-family;
|
||||
font-size: @font-size;
|
||||
}
|
||||
|
||||
atom-workspace {
|
||||
|
Loading…
Reference in New Issue
Block a user