Merge remote-tracking branch 'origin/master' into remove-docs

This commit is contained in:
Scott Chacon 2015-03-11 15:11:59 -07:00
commit c4500af55a
44 changed files with 561 additions and 155 deletions

View File

@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "0.141.0"
"atom-package-manager": "0.142.0"
}
}

View File

@ -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",

View File

@ -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.

View File

@ -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

View File

@ -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"

View File

@ -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);

View File

@ -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

View File

@ -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()

View File

@ -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', ->

View File

@ -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()

View File

@ -0,0 +1,9 @@
{
"name": "package-with-invalid-selectors",
"version": "1.0.0",
"activationCommands": {
"<>": [
"foo:bar"
]
}
}

View File

@ -0,0 +1,10 @@
{
"context-menu": {
"<>": [
{
"label": "Hello",
"command:": "world"
}
]
}
}

View File

@ -0,0 +1,4 @@
{
"name": "package-with-invalid-context-menu",
"version": "1.0.0"
}

View File

@ -0,0 +1 @@
><

View File

@ -0,0 +1,4 @@
{
"name": "package-with-invalid-grammar",
"version": "1.0.0"
}

View File

@ -0,0 +1,4 @@
{
"name": "package-with-invalid-settings",
"version": "1.0.0"
}

View File

@ -0,0 +1 @@
><

View File

@ -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", ->

View File

@ -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", ->

View File

@ -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"))

View File

@ -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

View File

@ -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')

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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'))

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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']

View File

@ -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})

View File

@ -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})

View File

@ -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")

View File

@ -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

View File

@ -1,6 +1,6 @@
module.exports =
class ScrollbarCornerComponent
constructor: () ->
constructor: ->
@domNode = document.createElement('div')
@domNode.classList.add('scrollbar-corner')

View File

@ -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.

View 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

View File

@ -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

View File

@ -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

View File

@ -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) ->

View File

@ -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

View File

@ -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
}

View File

@ -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);

View File

@ -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 {