merge latest upstream master

This commit is contained in:
Michael Herring
2014-08-28 17:35:09 +09:00
53 changed files with 2481 additions and 7035 deletions

View File

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

View File

@@ -60,7 +60,7 @@ module.exports = (grunt) ->
contentsDir = shellAppDir
appDir = path.join(shellAppDir, 'resources', 'app')
installDir ?= process.env.INSTALL_PREFIX ? '/usr/local'
killCommand ='pkill -9 Atom'
killCommand ='pkill -9 atom'
coffeeConfig =
glob_to_multiple:
@@ -130,7 +130,7 @@ module.exports = (grunt) ->
atom: {appDir, appName, symbolsDir, buildDir, contentsDir, installDir, shellAppDir}
docsOutputDir: 'docs/output/api'
docsOutputDir: 'docs/output'
coffee: coffeeConfig

View File

@@ -7,7 +7,8 @@
},
"dependencies": {
"async": "~0.2.9",
"biscotto": ">=2.1.1 <3.0",
"donna": "~1.0",
"tello": "~0.2",
"formidable": "~1.0.14",
"fs-plus": "2.x",
"github-releases": "~0.2.0",

View File

@@ -1,162 +1,39 @@
path = require 'path'
async = require 'async'
fs = require 'fs-plus'
request = require 'request'
_ = require 'underscore-plus'
donna = require 'donna'
tello = require 'tello'
module.exports = (grunt) ->
{rm} = require('./task-helpers')(grunt)
getClassesToInclude = ->
modulesPath = path.resolve(__dirname, '..', '..', 'node_modules')
classes = {}
fs.traverseTreeSync modulesPath, (modulePath) ->
return true unless path.basename(modulePath) is 'package.json'
return true unless fs.isFileSync(modulePath)
cmd = path.join('node_modules', '.bin', 'coffee')
commonArgs = [path.join('build', 'node_modules', '.bin', 'biscotto'), '--']
opts =
stdio: 'inherit'
apiPath = path.join(path.dirname(modulePath), 'api.json')
if fs.isFileSync(apiPath)
_.extend(classes, grunt.file.readJSON(apiPath).classes)
true
classes
sortClasses = (classes) ->
sortedClasses = {}
for className in Object.keys(classes).sort()
sortedClasses[className] = classes[className]
sortedClasses
grunt.registerTask 'build-docs', 'Builds the API docs in src', ->
done = @async()
docsOutputDir = grunt.config.get('docsOutputDir')
downloadIncludes (error, includePaths) ->
if error?
done(error)
else
rm(docsOutputDir)
args = [
commonArgs...
'--title', 'Atom API Documentation'
'-o', docsOutputDir
'-r', 'docs/README.md'
'--stability', '1'
'src/'
includePaths...
]
grunt.util.spawn({cmd, args, opts}, done)
metadata = donna.generateMetadata(['.'])
api = tello.digest(metadata)
_.extend(api.classes, getClassesToInclude())
api.classes = sortClasses(api.classes)
grunt.registerTask 'lint-docs', 'Generate stats about the doc coverage', ->
done = @async()
downloadIncludes (error, includePaths) ->
if error?
done(error)
else
args = [
commonArgs...
'--noOutput'
'src/'
includePaths...
]
grunt.util.spawn({cmd, args, opts}, done)
grunt.registerTask 'missing-docs', 'Generate stats about the doc coverage', ->
done = @async()
downloadIncludes (error, includePaths) ->
if error?
done(error)
else
args = [
commonArgs...
'--noOutput'
'--missing'
'src/'
includePaths...
]
grunt.util.spawn({cmd, args, opts}, done)
grunt.registerTask 'copy-docs', 'Copies over latest API docs to atom-docs', ->
done = @async()
fetchTag = (args..., callback) ->
cmd = 'git'
args = ['describe', '--abbrev=0', '--tags']
grunt.util.spawn {cmd, args}, (error, result) ->
if error?
callback(error)
else
callback(null, String(result).trim())
copyDocs = (tag, callback) ->
cmd = 'cp'
args = ['-r', 'docs/output/', "../atom.io/public/docs/api/#{tag}/"]
fs.exists "../atom.io/public/docs/api/", (exists) ->
if exists
grunt.util.spawn {cmd, args}, (error, result) ->
if error?
callback(error)
else
callback(null, tag)
else
grunt.log.error "../atom.io/public/docs/api/ doesn't exist"
return false
grunt.util.async.waterfall [fetchTag, copyDocs], done
grunt.registerTask 'deploy-docs', 'Publishes latest API docs to atom-docs.githubapp.com', ->
done = @async()
docsRepoArgs = ['--work-tree=../atom-docs/', '--git-dir=../atom-docs/.git/']
fetchTag = (args..., callback) ->
cmd = 'git'
args = ['describe', '--abbrev=0', '--tags']
grunt.util.spawn {cmd, args}, (error, result) ->
if error?
callback(error)
else
callback(null, String(result).trim().split('.')[0..1].join('.'))
stageDocs = (tag, callback) ->
cmd = 'git'
args = [docsRepoArgs..., 'add', "public/#{tag}"]
grunt.util.spawn({cmd, args, opts}, callback)
fetchSha = (args..., callback) ->
cmd = 'git'
args = ['rev-parse', 'HEAD']
grunt.util.spawn {cmd, args}, (error, result) ->
if error?
callback(error)
else
callback(null, String(result).trim())
commitChanges = (sha, callback) ->
cmd = 'git'
args = [docsRepoArgs..., 'commit', "-m Update API docs to #{sha}"]
grunt.util.spawn({cmd, args, opts}, callback)
pushOrigin = (args..., callback) ->
cmd = 'git'
args = [docsRepoArgs..., 'push', 'origin', 'master']
grunt.util.spawn({cmd, args, opts}, callback)
pushHeroku = (args..., callback) ->
cmd = 'git'
args = [docsRepoArgs..., 'push', 'heroku', 'master']
grunt.util.spawn({cmd, args, opts}, callback)
grunt.util.async.waterfall [fetchTag, stageDocs, fetchSha, commitChanges, pushOrigin, pushHeroku], done
downloadFileFromRepo = ({repo, file}, callback) ->
uri = "https://raw.github.com/atom/#{repo}/master/#{file}"
request uri, (error, response, contents) ->
return callback(error) if error?
downloadPath = path.join('docs', 'includes', repo, file)
fs.writeFile downloadPath, contents, (error) ->
callback(error, downloadPath)
downloadIncludes = (callback) ->
includes = [
{repo: 'atom-keymap', file: 'src/keymap-manager.coffee'}
{repo: 'atom-keymap', file: 'src/key-binding.coffee'}
{repo: 'first-mate', file: 'src/grammar.coffee'}
{repo: 'first-mate', file: 'src/grammar-registry.coffee'}
{repo: 'node-pathwatcher', file: 'src/directory.coffee'}
{repo: 'node-pathwatcher', file: 'src/file.coffee'}
{repo: 'space-pen', file: 'src/space-pen.coffee'}
{repo: 'text-buffer', file: 'src/marker.coffee'}
{repo: 'text-buffer', file: 'src/point.coffee'}
{repo: 'text-buffer', file: 'src/range.coffee'}
{repo: 'text-buffer', file: 'src/text-buffer.coffee'}
{repo: 'theorist', file: 'src/model.coffee'}
]
async.map(includes, downloadFileFromRepo, callback)
apiJson = JSON.stringify(api, null, 2)
apiJsonPath = path.join(docsOutputDir, 'api.json')
grunt.file.write(apiJsonPath, apiJson)

View File

@@ -13,8 +13,16 @@ module.exports = (grunt) ->
grunt.file.write(outputPath, filled)
outputPath
getInstalledSize = (buildDir, callback) ->
cmd = 'du'
args = ['-sk', path.join(buildDir, 'Atom')]
spawn {cmd, args}, (error, {stdout}) ->
installedSize = stdout.split(/\s+/)?[0] or '200000' # default to 200MB
callback(null, installedSize)
grunt.registerTask 'mkdeb', 'Create debian package', ->
done = @async()
buildDir = grunt.config.get('atom.buildDir')
if process.arch is 'ia32'
arch = 'i386'
@@ -28,13 +36,17 @@ module.exports = (grunt) ->
maintainer = 'GitHub <atom@github.com>'
installDir = '/usr'
iconName = 'atom'
data = {name, version, description, section, arch, maintainer, installDir, iconName}
getInstalledSize buildDir, (error, installedSize) ->
data = {name, version, description, section, arch, maintainer, installDir, iconName, installedSize}
controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data)
desktopFilePath = fillTemplate(path.join('resources', 'linux', 'Atom.desktop'), data)
icon = path.join('resources', 'atom.png')
controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data)
desktopFilePath = fillTemplate(path.join('resources', 'linux', 'Atom.desktop'), data)
icon = path.join('resources', 'atom.png')
buildDir = grunt.config.get('atom.buildDir')
cmd = path.join('script', 'mkdeb')
args = [version, arch, controlFilePath, desktopFilePath, icon, buildDir]
spawn({cmd, args}, done)
cmd = path.join('script', 'mkdeb')
args = [version, arch, controlFilePath, desktopFilePath, icon, buildDir]
spawn {cmd, args}, (error) ->
if error?
done(error)
else
grunt.log.ok "Created #{buildDir}/atom-#{version}-#{arch}.deb"
done()

View File

@@ -17,6 +17,7 @@ defaultHeaders =
module.exports = (gruntObject) ->
grunt = gruntObject
{cp} = require('./task-helpers')(grunt)
grunt.registerTask 'publish-build', 'Publish the built app', ->
return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
@@ -24,8 +25,10 @@ module.exports = (gruntObject) ->
tasks.unshift('build-docs', 'prepare-docs') if process.platform is 'darwin'
grunt.task.run(tasks)
grunt.registerTask 'prepare-docs', 'Move the build docs to the build dir', ->
fs.copySync(grunt.config.get('docsOutputDir'), path.join(grunt.config.get('atom.buildDir'), 'atom-docs'))
grunt.registerTask 'prepare-docs', 'Move api.json to atom-api.json', ->
docsOutputDir = grunt.config.get('docsOutputDir')
buildDir = grunt.config.get('atom.buildDir')
cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json')
grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', ->
done = @async()
@@ -46,7 +49,7 @@ getAssets = ->
[
{assetName: 'atom-mac.zip', sourcePath: 'Atom.app'}
{assetName: 'atom-mac-symbols.zip', sourcePath: 'Atom.breakpad.syms'}
{assetName: 'atom-docs.zip', sourcePath: 'atom-docs'}
{assetName: 'atom-api.json', sourcePath: 'atom-api.json'}
]
else
[
@@ -70,7 +73,7 @@ zipAssets = (buildDir, assets, callback) ->
callback(error)
tasks = []
for {assetName, sourcePath} in assets
for {assetName, sourcePath} in assets when path.extname(assetName) is '.zip'
fs.removeSync(path.join(buildDir, assetName))
tasks.push(zip.bind(this, buildDir, sourcePath, assetName))
async.parallel(tasks, callback)

View File

@@ -2,7 +2,6 @@ fs = require 'fs'
path = require 'path'
_ = require 'underscore-plus'
async = require 'async'
module.exports = (grunt) ->
@@ -10,18 +9,27 @@ module.exports = (grunt) ->
packageSpecQueue = null
getAppPath = ->
contentsDir = grunt.config.get('atom.contentsDir')
switch process.platform
when 'darwin'
path.join(contentsDir, 'MacOS', 'Atom')
when 'linux'
path.join(contentsDir, 'atom')
when 'win32'
path.join(contentsDir, 'atom.exe')
runPackageSpecs = (callback) ->
failedPackages = []
rootDir = grunt.config.get('atom.shellAppDir')
contentsDir = grunt.config.get('atom.contentsDir')
resourcePath = process.cwd()
if process.platform is 'darwin'
appPath = path.join(contentsDir, 'MacOS', 'Atom')
else if process.platform is 'win32'
appPath = path.join(contentsDir, 'atom.exe')
appPath = getAppPath()
# Ensure application is executable on Linux
fs.chmodSync(appPath, '755') if process.platform is 'linux'
packageSpecQueue = async.queue (packagePath, callback) ->
if process.platform is 'darwin'
if process.platform in ['darwin', 'linux']
options =
cmd: appPath
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{path.join(packagePath, 'spec')}"]
@@ -57,15 +65,11 @@ module.exports = (grunt) ->
packageSpecQueue.drain = -> callback(null, failedPackages)
runCoreSpecs = (callback) ->
contentsDir = grunt.config.get('atom.contentsDir')
if process.platform is 'darwin'
appPath = path.join(contentsDir, 'MacOS', 'Atom')
else if process.platform is 'win32'
appPath = path.join(contentsDir, 'atom.exe')
appPath = getAppPath()
resourcePath = process.cwd()
coreSpecsPath = path.resolve('spec')
if process.platform is 'darwin'
if process.platform in ['darwin', 'linux']
options =
cmd: appPath
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}"]
@@ -90,7 +94,7 @@ module.exports = (grunt) ->
# TODO: This should really be parallel on both platforms, however our
# fixtures step on each others toes currently.
if process.platform is 'darwin'
if process.platform in ['darwin', 'linux']
method = async.parallel
else if process.platform is 'win32'
method = async.series

View File

@@ -15,10 +15,7 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
module.exports.$ = $
module.exports.$$ = $$
module.exports.$$$ = $$$
if atom.config.get('core.useReactMiniEditors')
module.exports.EditorView = require '../src/react-editor-view'
else
module.exports.EditorView = require '../src/editor-view'
module.exports.EditorView = require '../src/editor-view'
module.exports.ScrollView = require '../src/scroll-view'
module.exports.SelectListView = require '../src/select-list-view'
module.exports.Task = require '../src/task'

View File

@@ -14,6 +14,7 @@
'F11': 'window:toggle-full-screen'
# Sublime Parity
'ctrl-,': 'application:show-settings'
'ctrl-N': 'application:new-window'
'ctrl-W': 'window:close'
'ctrl-o': 'application:open-file'

View File

@@ -8,8 +8,6 @@
{ label: 'Open Folder...', command: 'application:open-folder' }
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
{ type: 'separator' }
{ label: '&Preferences...', command: 'application:show-settings' }
{ type: 'separator' }
{ label: '&Save', command: 'core:save' }
{ label: 'Save &As...', command: 'core:save-as' }
{ label: 'Save A&ll', command: 'window:save-all' }
@@ -80,6 +78,13 @@
{ label: 'Fold Level 9', command: 'editor:fold-at-indent-level-9' }
]
}
{ type: 'separator' }
{ label: '&Preferences', command: 'application:show-settings' }
{ label: 'Open Your Config', command: 'application:open-your-config' }
{ label: 'Open Your Init Script', command: 'application:open-your-init-script' }
{ label: 'Open Your Keymap', command: 'application:open-your-keymap' }
{ label: 'Open Your Snippets', command: 'application:open-your-snippets' }
{ label: 'Open Your Stylesheet', command: 'application:open-your-stylesheet' }
]
}

View File

@@ -1,7 +1,7 @@
{
"name": "atom",
"productName": "Atom",
"version": "0.124.0",
"version": "0.125.0",
"description": "A hackable text editor for the 21st Century.",
"main": "./src/browser/main.js",
"repository": {
@@ -20,14 +20,14 @@
"atomShellVersion": "0.15.9",
"dependencies": {
"async": "0.2.6",
"atom-keymap": "^2.0.2",
"atom-keymap": "^2.0.5",
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
"clear-cut": "0.4.0",
"coffee-script": "1.7.0",
"coffeestack": "0.7.0",
"delegato": "^1",
"emissary": "^1.2.2",
"first-mate": "^2.0.1",
"first-mate": "^2.0.5",
"fs-plus": "^2.2.6",
"fstream": "0.1.24",
"fuzzaldrin": "^1.1",
@@ -39,9 +39,9 @@
"mixto": "^1",
"mkdirp": "0.3.5",
"nslog": "^1.0.1",
"oniguruma": "^3.0.3",
"oniguruma": "^3.0.4",
"optimist": "0.4.0",
"pathwatcher": "^2.0.7",
"pathwatcher": "^2.0.10",
"property-accessors": "^1",
"q": "^1.0.1",
"random-words": "0.0.1",
@@ -54,10 +54,10 @@
"season": "^1.0.2",
"semver": "1.1.4",
"serializable": "^1",
"space-pen": "3.4.1",
"space-pen": "3.4.6",
"temp": "0.7.0",
"text-buffer": "^3.0.1",
"theorist": "^1",
"text-buffer": "^3.0.4",
"theorist": "^1.0.2",
"underscore-plus": "^1.5.1",
"vm-compatibility-layer": "0.1.0"
},
@@ -74,39 +74,39 @@
"autocomplete": "0.31.0",
"autoflow": "0.18.0",
"autosave": "0.15.0",
"background-tips": "0.15.0",
"bookmarks": "0.27.0",
"background-tips": "0.16.0",
"bookmarks": "0.28.0",
"bracket-matcher": "0.54.0",
"command-palette": "0.24.0",
"deprecation-cop": "0.9.0",
"dev-live-reload": "0.34.0",
"exception-reporting": "0.20.0",
"feedback": "0.33.0",
"find-and-replace": "0.129.0",
"find-and-replace": "0.131.0",
"fuzzy-finder": "0.57.0",
"git-diff": "0.38.0",
"go-to-line": "0.24.0",
"git-diff": "0.39.0",
"go-to-line": "0.25.0",
"grammar-selector": "0.29.0",
"image-view": "0.36.0",
"incompatible-packages": "0.9.0",
"keybinding-resolver": "0.19.0",
"link": "0.25.0",
"markdown-preview": "0.99.0",
"markdown-preview": "0.100.0",
"metrics": "0.33.0",
"open-on-github": "0.30.0",
"package-generator": "0.31.0",
"release-notes": "0.36.0",
"settings-view": "0.140.0",
"settings-view": "0.141.0",
"snippets": "0.51.0",
"spell-check": "0.42.0",
"status-bar": "0.43.0",
"status-bar": "0.44.0",
"styleguide": "0.30.0",
"symbols-view": "0.63.0",
"tabs": "0.49.0",
"tabs": "0.50.0",
"timecop": "0.22.0",
"tree-view": "0.112.0",
"update-package-dependencies": "0.6.0",
"welcome": "0.17.0",
"welcome": "0.18.0",
"whitespace": "0.25.0",
"wrap-guide": "0.21.0",
@@ -123,7 +123,7 @@
"language-json": "0.8.0",
"language-less": "0.14.0",
"language-make": "0.12.0",
"language-mustache": "0.9.0",
"language-mustache": "0.10.0",
"language-objective-c": "0.11.0",
"language-perl": "0.9.0",
"language-php": "0.15.0",
@@ -131,7 +131,7 @@
"language-python": "0.18.0",
"language-ruby": "0.35.0",
"language-ruby-on-rails": "0.18.0",
"language-sass": "0.19.0",
"language-sass": "0.20.0",
"language-shellscript": "0.8.0",
"language-source": "0.8.0",
"language-sql": "0.10.0",
@@ -139,7 +139,7 @@
"language-todo": "0.10.0",
"language-toml": "0.12.0",
"language-xml": "0.18.0",
"language-yaml": "0.16.0"
"language-yaml": "0.17.0"
},
"private": true,
"scripts": {

View File

@@ -3,6 +3,6 @@ Version: <%= version %>
Section: <%= section %>
Priority: optional
Architecture: <%= arch %>
Installed-Size: `du -ks usr|cut -f 1`
Installed-Size: <%= installedSize %>
Maintainer: <%= maintainer %>
Description: <%= description %>

View File

@@ -493,13 +493,14 @@ describe "the `atom` global", ->
expect(atom.config.get('core.disabledPackages')).toContain packageName
describe "with themes", ->
reloadedHandler = null
beforeEach ->
waitsForPromise ->
atom.themes.activateThemes()
afterEach ->
atom.themes.deactivateThemes()
atom.config.unobserve('core.themes')
it ".enablePackage() and .disablePackage() enables and disables a theme", ->
packageName = 'theme-with-package-file'
@@ -517,13 +518,17 @@ describe "the `atom` global", ->
expect(atom.config.get('core.themes')).toContain packageName
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
# disabling of theme
reloadedHandler = jasmine.createSpy('reloadedHandler')
reloadedHandler.reset()
atom.themes.on('reloaded', reloadedHandler)
pack = atom.packages.disablePackage(packageName)
waitsFor ->
not (pack in atom.packages.getActivePackages())
reloadedHandler.callCount is 1
runs ->
expect(atom.packages.getActivePackages()).not.toContain pack
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.disabledPackages')).not.toContain packageName

View File

@@ -1037,6 +1037,19 @@ describe "DisplayBuffer", ->
expect(start.top).toBe 5 * 20
expect(start.left).toBe (4 * 10) + (6 * 11)
describe 'when there are multiple DisplayBuffers for a buffer', ->
describe 'when a marker is created', ->
it 'the second display buffer will not emit a marker-created event when the marker has been deleted in the first marker-created event', ->
displayBuffer2 = new DisplayBuffer({buffer, tabLength})
displayBuffer.on 'marker-created', markerCreated1 = jasmine.createSpy().andCallFake (marker) ->
marker.destroy()
displayBuffer2.on 'marker-created', markerCreated2 = jasmine.createSpy()
displayBuffer.markBufferRange([[0, 0], [1, 5]], {})
expect(markerCreated1).toHaveBeenCalled()
expect(markerCreated2).not.toHaveBeenCalled()
describe "decorations", ->
[marker, decoration, decorationParams] = []
beforeEach ->

View File

@@ -1,7 +1,7 @@
_ = require 'underscore-plus'
{extend, flatten, toArray, last} = _
ReactEditorView = require '../src/react-editor-view'
EditorView = require '../src/editor-view'
EditorComponent = require '../src/editor-component'
nbsp = String.fromCharCode(160)
@@ -34,7 +34,7 @@ describe "EditorComponent", ->
contentNode = document.querySelector('#jasmine-content')
contentNode.style.width = '1000px'
wrapperView = new ReactEditorView(editor, {lineOverdrawMargin})
wrapperView = new EditorView(editor, {lineOverdrawMargin})
wrapperView.attachToDom()
wrapperNode = wrapperView.element
@@ -1911,7 +1911,7 @@ describe "EditorComponent", ->
hiddenParent.style.display = 'none'
contentNode.appendChild(hiddenParent)
wrapperView = new ReactEditorView(editor, {lineOverdrawMargin})
wrapperView = new EditorView(editor, {lineOverdrawMargin})
wrapperNode = wrapperView.element
wrapperView.appendTo(hiddenParent)
@@ -1953,6 +1953,7 @@ describe "EditorComponent", ->
wrapperView.hide()
component.setFontSize(22)
editor.getBuffer().insert([0, 0], 'a') # regression test against atom/atom#3318
wrapperView.show()
editor.setCursorBufferPosition([0, Infinity])
@@ -2164,6 +2165,12 @@ describe "EditorComponent", ->
setEditorWidthInChars(wrapperView, 10)
expect(componentNode.querySelector('.scroll-view').offsetWidth).toBe charWidth * 10
describe "grammar data attributes", ->
it "adds and updates the grammar data attribute based on the current grammar", ->
expect(wrapperNode.dataset.grammar).toBe 'source js'
editor.setGrammar(atom.syntax.nullGrammar)
expect(wrapperNode.dataset.grammar).toBe 'text plain null-grammar'
buildMouseEvent = (type, properties...) ->
properties = extend({bubbles: true, cancelable: true}, properties...)
properties.detail ?= 1

File diff suppressed because it is too large Load Diff

View File

@@ -95,8 +95,6 @@ beforeEach ->
config.set "editor.autoIndent", false
config.set "core.disabledPackages", ["package-that-throws-an-exception",
"package-with-broken-package-json", "package-with-broken-keymap"]
config.set "core.useReactEditor", true
config.set "core.useReactMiniEditors", true
config.save.reset()
atom.config = config

View File

@@ -17,29 +17,14 @@ WindowEventHandler = require './window-event-handler'
# Public: Atom global for dealing with packages, themes, menus, and the window.
#
# An instance of this class is always available as the `atom` global.
#
# ## Useful properties available:
#
# * `atom.clipboard` - A {Clipboard} instance
# * `atom.config` - A {Config} instance
# * `atom.contextMenu` - A {ContextMenuManager} instance
# * `atom.deserializers` - A {DeserializerManager} instance
# * `atom.keymaps` - A {KeymapManager} instance
# * `atom.menu` - A {MenuManager} instance
# * `atom.packages` - A {PackageManager} instance
# * `atom.project` - A {Project} instance
# * `atom.syntax` - A {Syntax} instance
# * `atom.themes` - A {ThemeManager} instance
# * `atom.workspace` - A {Workspace} instance
# * `atom.workspaceView` - A {WorkspaceView} instance
module.exports =
class Atom extends Model
@version: 1 # Increment this when the serialization format changes
# Public: Load or create the Atom environment in the given mode.
#
# mode - Pass 'editor' or 'spec' depending on the kind of environment you
# want to build.
# * `mode` A {String} mode that is either 'editor' or 'spec' depending on the
# kind of environment you want to build.
#
# Returns an Atom instance, fully initialized
@loadOrCreate: (mode) ->
@@ -111,6 +96,43 @@ class Atom extends Model
remote.getCurrentWindow()
workspaceViewParentSelector: 'body'
lastUncaughtError: null
# Public: A {Clipboard} instance
clipboard: null
# Public: A {Config} instance
config: null
# Public: A {ContextMenuManager} instance
contextMenu: null
# Public: A {DeserializerManager} instance
deserializers: null
# Public: A {KeymapManager} instance
keymaps: null
# Public: A {MenuManager} instance
menu: null
# Public: A {PackageManager} instance
packages: null
# Public: A {Project} instance
project: null
# Public: A {Syntax} instance
syntax: null
# Public: A {ThemeManager} instance
themes: null
# Public: A {Workspace} instance
workspace: null
# Public: A {WorkspaceView} instance
workspaceView: null
# Call .loadOrCreate instead
constructor: (@state) ->
@@ -119,12 +141,14 @@ class Atom extends Model
@deserializers = new DeserializerManager()
# Public: Sets up the basic services that should be available in all modes
# (both spec and application). Call after this instance has been assigned to
# the `atom` global.
# (both spec and application).
#
# Call after this instance has been assigned to the `atom` global.
initialize: ->
window.onerror = =>
@openDevTools()
@executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()')
@lastUncaughtError = Array::slice.call(arguments)
@emit 'uncaught-error', arguments...
@unsubscribe()
@@ -191,7 +215,11 @@ class Atom extends Model
# Public: Get the dimensions of this window.
#
# Returns an object with x, y, width, and height keys.
# Returns an {Object} with the following keys:
# * `x` The window's x-position {Number}.
# * `y` The window's y-position {Number}.
# * `width` The window's width {Number}.
# * `height` The window's height {Number}.
getWindowDimensions: ->
browserWindow = @getCurrentWindow()
[x, y] = browserWindow.getPosition()
@@ -205,11 +233,11 @@ class Atom extends Model
# in the dimensions parameter. If x or y are omitted the window will be
# centered. If height or width are omitted only the position will be changed.
#
# dimensions - An {Object} with the following keys:
# :x - The new x coordinate.
# :y - The new y coordinate.
# :width - The new width.
# :height - The new height.
# * `dimensions` An {Object} with the following keys:
# * `x` The new x coordinate.
# * `y` The new y coordinate.
# * `width` The new width.
# * `height` The new height.
setWindowDimensions: ({x, y, width, height}) ->
if width? and height?
@setSize(width, height)
@@ -258,7 +286,7 @@ class Atom extends Model
# Public: Get the load settings for the current window.
#
# Returns an object containing all the load setting key/value pairs.
# Returns an {Object} containing all the load setting key/value pairs.
getLoadSettings: ->
@constructor.getLoadSettings()
@@ -308,6 +336,9 @@ class Atom extends Model
@themes.loadBaseStylesheets()
@packages.loadPackages()
@deserializeEditorWindow()
@watchProjectPath()
@packages.activate()
@keymaps.loadUserKeymap()
@requireUserInitScript()
@@ -347,19 +378,33 @@ class Atom extends Model
pack.reloadStylesheets?()
null
# Notify the browser project of the window's current project path
watchProjectPath: ->
onProjectPathChanged = =>
ipc.send('window-command', 'project-path-changed', @project.getPath())
@subscribe @project, 'path-changed', onProjectPathChanged
onProjectPathChanged()
# Public: Open a new Atom window using the given options.
#
# Calling this method without an options parameter will open a prompt to pick
# a file/folder to open in the new window.
#
# options - An {Object} with the following keys:
# :pathsToOpen - An {Array} of {String} paths to open.
# * `options` An {Object} with the following keys:
# * `pathsToOpen` An {Array} of {String} paths to open.
# * `newWindow` A {Boolean}, true to always open a new window instead of
# reusing existing windows depending on the paths to open.
# * `devMode` A {Boolean}, true to open the window in development mode.
# Development mode loads the Atom source from the locally cloned
# repository and also loads all the packages in ~/.atom/dev/packages
# * `safeMode` A {Boolean}, true to open the window in safe mode. Safe
# mode prevents all packages installed to ~/.atom/packages from loading.
open: (options) ->
ipc.send('open', options)
# Public: Open a confirm dialog.
#
# ## Example
# ## Examples
#
# ```coffee
# atom.confirm
@@ -370,12 +415,12 @@ class Atom extends Model
# Bad: -> window.alert('bummer')
# ```
#
# options - An {Object} with the following keys:
# :message - The {String} message to display.
# :detailedMessage - The {String} detailed message to display.
# :buttons - Either an array of strings or an object where keys are
# button names and the values are callbacks to invoke when
# clicked.
# * `options` An {Object} with the following keys:
# * `message` The {String} message to display.
# * `detailedMessage` The {String} detailed message to display.
# * `buttons` Either an array of strings or an object where keys are
# button names and the values are callbacks to invoke when
# clicked.
#
# Returns the chosen button index {Number} if the buttons option was an array.
confirm: ({message, detailedMessage, buttons}={}) ->
@@ -438,15 +483,15 @@ class Atom extends Model
# Public: Set the size of current window.
#
# width - The {Number} of pixels.
# height - The {Number} of pixels.
# * `width` The {Number} of pixels.
# * `height` The {Number} of pixels.
setSize: (width, height) ->
@getCurrentWindow().setSize(width, height)
# Public: Set the position of current window.
#
# x - The {Number} of pixels.
# y - The {Number} of pixels.
# * `x` The {Number} of pixels.
# * `y` The {Number} of pixels.
setPosition: (x, y) ->
ipc.send('call-window-method', 'setPosition', x, y)
@@ -533,7 +578,7 @@ class Atom extends Model
# This time include things like loading and activating packages, creating
# DOM elements for the editor, and reading the config.
#
# Returns the number of milliseconds taken to load the window or null
# Returns the {Number} of milliseconds taken to load the window or null
# if the window hasn't finished loading yet.
getWindowLoadTime: ->
@loadTime
@@ -565,8 +610,8 @@ class Atom extends Model
# The globals will be set on the `window` object and removed after the
# require completes.
#
# id - The {String} module name or path.
# globals - An {Object} to set as globals during require (default: {})
# * `id` The {String} module name or path.
# * `globals` An optinal {Object} to set as globals during require.
requireWithGlobals: (id, globals={}) ->
existingGlobals = {}
for key, value of globals

View File

@@ -103,6 +103,12 @@ class AtomApplication
window.once 'window:loaded', =>
@autoUpdateManager.emitUpdateAvailableEvent(window)
focusHandler = => @lastFocusedWindow = window
window.browserWindow.on 'focus', focusHandler
window.browserWindow.once 'closed', =>
@lastFocusedWindow = null if window is @lastFocusedWindow
window.browserWindow.removeListener 'focus', focusHandler
# Creates server to listen for additional atom application launches.
#
# You can run the atom command multiple times, but after the first launch
@@ -199,13 +205,15 @@ class AtomApplication
# A request from the associated render process to open a new render process.
ipc.on 'open', (event, options) =>
window = @windowForEvent(event)
if options?
if options.pathsToOpen?.length > 0
options.window = window
@openPaths(options)
else
new AtomWindow(options)
else
@promptForPath()
@promptForPath({window})
ipc.on 'update-application-menu', (event, template, keystrokesByCommand) =>
@applicationMenu.update(template, keystrokesByCommand)
@@ -281,8 +289,12 @@ class AtomApplication
# Returns the {AtomWindow} for the given path.
windowForPath: (pathToOpen) ->
for atomWindow in @windows
return atomWindow if atomWindow.containsPath(pathToOpen)
_.find @windows, (atomWindow) -> atomWindow.containsPath(pathToOpen)
# Returns the {AtomWindow} for the given ipc event.
windowForEvent: ({sender}) ->
window = BrowserWindow.fromWebContents(sender)
_.find @windows, ({browserWindow}) -> window is browserWindow
# Public: Returns the currently focused {AtomWindow} or undefined if none.
focusedWindow: ->
@@ -296,9 +308,10 @@ class AtomApplication
# :newWindow - Boolean of whether this should be opened in a new window.
# :devMode - Boolean to control the opened window's dev mode.
# :safeMode - Boolean to control the opened window's safe mode.
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode}) ->
# :window - {AtomWindow} to open file paths in.
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, window}) ->
for pathToOpen in pathsToOpen ? []
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode})
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, window})
# Public: Opens a single path, in an existing window if possible.
#
@@ -309,15 +322,30 @@ class AtomApplication
# :devMode - Boolean to control the opened window's dev mode.
# :safeMode - Boolean to control the opened window's safe mode.
# :windowDimensions - Object with height and width keys.
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions}={}) ->
# :window - {AtomWindow} to open file paths in.
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, window}={}) ->
{pathToOpen, initialLine, initialColumn} = @locationForPathToOpen(pathToOpen)
unless devMode
existingWindow = @windowForPath(pathToOpen) unless pidToKillWhenClosed or newWindow
if existingWindow
unless pidToKillWhenClosed or newWindow
pathToOpenStat = fs.statSyncNoException(pathToOpen)
# Default to using the specified window or the last focused window
currentWindow = window ? @lastFocusedWindow
if pathToOpenStat.isFile?()
# Open the file in the current window
existingWindow = currentWindow
else if pathToOpenStat.isDirectory?()
# Open the folder in the current window if it doesn't have a path
existingWindow = currentWindow unless currentWindow?.hasProjectPath()
# Don't reuse windows in dev mode
existingWindow ?= @windowForPath(pathToOpen) unless devMode
if existingWindow?
openedWindow = existingWindow
openedWindow.openPath(pathToOpen, initialLine)
openedWindow.restore()
openedWindow.restore() if openedWindow.isMinimized()
else
if devMode
try
@@ -331,7 +359,7 @@ class AtomApplication
if pidToKillWhenClosed?
@pidsToOpenWindows[pidToKillWhenClosed] = openedWindow
openedWindow.browserWindow.on 'closed', =>
openedWindow.browserWindow.once 'closed', =>
@killProcessForWindow(openedWindow)
# Kill all processes associated with opened windows.
@@ -444,7 +472,8 @@ class AtomApplication
# should be in dev mode or not.
# :safeMode - A Boolean which controls whether any newly opened windows
# should be in safe mode or not.
promptForPath: ({type, devMode, safeMode}={}) ->
# :window - An {AtomWindow} to use for opening a selected file path.
promptForPath: ({type, devMode, safeMode, window}={}) ->
type ?= 'all'
properties =
switch type
@@ -453,4 +482,4 @@ class AtomApplication
when 'all' then ['openFile', 'openDirectory']
else throw new Error("#{type} is an invalid type for promptForPath")
dialog.showOpenDialog title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) =>
@openPaths({pathsToOpen, devMode, safeMode})
@openPaths({pathsToOpen, devMode, safeMode, window})

View File

@@ -25,9 +25,9 @@ class AtomWindow
# Normalize to make sure drive letter case is consistent on Windows
@resourcePath = path.normalize(@resourcePath) if @resourcePath
@browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath
global.atomApplication.addWindow(this)
@browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath
@handleEvents()
loadSettings = _.extend({}, settings)
@@ -49,6 +49,8 @@ class AtomWindow
@emit 'window:loaded'
@loaded = true
@browserWindow.on 'project-path-changed', (@projectPath) =>
@browserWindow.loadUrl @getUrl(loadSettings)
@browserWindow.focusOnWebView() if @isSpec
@@ -66,6 +68,8 @@ class AtomWindow
slashes: true
query: {loadSettings: JSON.stringify(loadSettings)}
hasProjectPath: -> @projectPath?.length > 0
getInitialPath: ->
@browserWindow.loadSettings.initialPath
@@ -169,6 +173,8 @@ class AtomWindow
isFocused: -> @browserWindow.isFocused()
isMinimized: -> @browserWindow.isMinimized()
isWebViewFocused: -> @browserWindow.isWebViewFocused()
isSpecWindow: -> @isSpec

View File

@@ -63,7 +63,14 @@ parseCommandLine = ->
options.usage """
Atom Editor v#{version}
Usage: atom [options] [file ...]
Usage: atom [options] [path ...]
One or more paths to files or folders to open may be specified.
File paths will open in the current window.
Folder paths will open in an existing window if that folder has already been
opened or a new window if it hasn't.
"""
options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.')
options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the browser process in the foreground.')

View File

@@ -6,34 +6,35 @@ path = require 'path'
#
# This is necessary on Windows since it doesn't support shebang `#!` lines.
#
# ## Requiring in packages
# ## Examples
#
# ```coffee
# {BufferedNodeProcess} = require 'atom'
# {BufferedNodeProcess} = require 'atom'
# ```
module.exports =
class BufferedNodeProcess extends BufferedProcess
# Public: Runs the given Node script by spawning a new child process.
#
# options - An {Object} with the following keys:
# :command - The {String} path to the JavaScript script to execute.
# :args - The {Array} of arguments to pass to the script (optional).
# :options - The options {Object} to pass to Node's `ChildProcess.spawn`
# method (optional).
# :stdout - The callback {Function} that receives a single argument which
# contains the standard output from the command. The callback is
# called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After
# the source stream has closed all remaining data is sent in a
# final call (optional).
# :stderr - The callback {Function} that receives a single argument which
# contains the standard error output from the command. The
# callback is called as data is received but it's buffered to
# ensure only complete lines are passed until the source stream
# closes. After the source stream has closed all remaining data
# is sent in a final call (optional).
# :exit - The callback {Function} which receives a single argument
# containing the exit status (optional).
# * `options` An {Object} with the following keys:
# * `command` The {String} path to the JavaScript script to execute.
# * `args` The {Array} of arguments to pass to the script (optional).
# * `options` The options {Object} to pass to Node's `ChildProcess.spawn`
# method (optional).
# * `stdout` The callback {Function} that receives a single argument which
# contains the standard output from the command. The callback is
# called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After
# the source stream has closed all remaining data is sent in a
# final call (optional).
# * `stderr` The callback {Function} that receives a single argument which
# contains the standard error output from the command. The
# callback is called as data is received but it's buffered to
# ensure only complete lines are passed until the source stream
# closes. After the source stream has closed all remaining data
# is sent in a final call (optional).
# * `exit` The callback {Function} which receives a single argument
# containing the exit status (optional).
constructor: ({command, args, options, stdout, stderr, exit}) ->
node =
if process.platform is 'darwin'

View File

@@ -4,7 +4,7 @@ ChildProcess = require 'child_process'
# Public: A wrapper which provides standard error/output line buffering for
# Node's ChildProcess.
#
# ## Requiring in packages
# ## Examples
#
# ```coffee
# {BufferedProcess} = require 'atom'
@@ -19,25 +19,25 @@ module.exports =
class BufferedProcess
# Public: Runs the given command by spawning a new child process.
#
# options - An {Object} with the following keys:
# :command - The {String} command to execute.
# :args - The {Array} of arguments to pass to the command (optional).
# :options - The options {Object} to pass to Node's `ChildProcess.spawn`
# method (optional).
# :stdout - The callback {Function} that receives a single argument which
# contains the standard output from the command. The callback is
# called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After
# the source stream has closed all remaining data is sent in a
# final call (optional).
# :stderr - The callback {Function} that receives a single argument which
# contains the standard error output from the command. The
# callback is called as data is received but it's buffered to
# ensure only complete lines are passed until the source stream
# closes. After the source stream has closed all remaining data
# is sent in a final call (optional).
# :exit - The callback {Function} which receives a single argument
# containing the exit status (optional).
# * `options` An {Object} with the following keys:
# * `command` The {String} command to execute.
# * `args` The {Array} of arguments to pass to the command (optional).
# * `options` The options {Object} to pass to Node's `ChildProcess.spawn`
# method (optional).
# * `stdout` The callback {Function} that receives a single argument which
# contains the standard output from the command. The callback is
# called as data is received but it's buffered to ensure only
# complete lines are passed until the source stream closes. After
# the source stream has closed all remaining data is sent in a
# final call (optional).
# * `stderr` The callback {Function} that receives a single argument which
# contains the standard error output from the command. The
# callback is called as data is received but it's buffered to
# ensure only complete lines are passed until the source stream
# closes. After the source stream has closed all remaining data
# is sent in a final call (optional).
# * `exit` The callback {Function} which receives a single argument
# containing the exit status (optional).
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
options ?= {}
# Related to joyent/node#2318
@@ -95,9 +95,9 @@ class BufferedProcess
# Helper method to pass data line by line.
#
# stream - The Stream to read from.
# onLines - The callback to call with each line of data.
# onDone - The callback to call when the stream has closed.
# * `stream` The Stream to read from.
# * `onLines` The callback to call with each line of data.
# * `onDone` The callback to call when the stream has closed.
bufferStream: (stream, onLines, onDone) ->
stream.setEncoding('utf8')
buffered = ''

View File

@@ -4,6 +4,14 @@ crypto = require 'crypto'
# Public: Represents the clipboard used for copying and pasting in Atom.
#
# An instance of this class is always available as the `atom.clipboard` global.
#
# ## Examples
#
# ```coffee
# atom.clipboard.write('hello')
#
# console.log(atom.clipboard.read()) # 'hello'
# ```
module.exports =
class Clipboard
metadata: null
@@ -11,7 +19,7 @@ class Clipboard
# Creates an `md5` hash of some text.
#
# text - A {String} to hash.
# * `text` A {String} to hash.
#
# Returns a hashed {String}.
md5: (text) ->
@@ -22,8 +30,8 @@ class Clipboard
# The metadata associated with the text is available by calling
# {::readWithMetadata}.
#
# text - The {String} to store.
# metadata - The additional info to associate with the text.
# * `text` The {String} to store.
# * `metadata` The additional info to associate with the text.
write: (text, metadata) ->
@signatureForMetadata = @md5(text)
@metadata = metadata
@@ -39,8 +47,8 @@ class Clipboard
# associated metadata.
#
# Returns an {Object} with the following keys:
# :text - The {String} clipboard text.
# :metadata - The metadata stored by an earlier call to {::write}.
# * `text` The {String} clipboard text.
# * `metadata` The metadata stored by an earlier call to {::write}.
readWithMetadata: ->
text = @read()
if @signatureForMetadata is @md5(text)

View File

@@ -15,9 +15,9 @@ pathWatcher = require 'pathwatcher'
# * Create your own root keypath using your package's name.
# * Don't depend on (or write to) configuration keys outside of your keypath.
#
# ## Example
# ## Examples
#
# ```coffeescript
# ```coffee
# atom.config.set('my-package.key', 'value')
# atom.config.observe 'my-package.key', ->
# console.log 'My configuration changed:', atom.config.get('my-package.key')
@@ -88,17 +88,17 @@ class Config
_.extend hash, defaults
@emit 'updated'
# Public: Get the {String} path to the config file being used.
# Extended: Get the {String} path to the config file being used.
getUserConfigPath: ->
@configFilePath
# Public: Returns a new {Object} containing all of settings and defaults.
# Extended: Returns a new {Object} containing all of settings and defaults.
getSettings: ->
_.deepExtend(@settings, @defaultSettings)
# Public: Retrieves the setting for the given key.
# Essential: Retrieves the setting for the given key.
#
# keyPath - The {String} name of the key to retrieve.
# * `keyPath` The {String} name of the key to retrieve.
#
# Returns the value from Atom's default settings, the user's configuration
# file, or `null` if the key doesn't exist in either.
@@ -117,19 +117,19 @@ class Config
value
# Public: Retrieves the setting for the given key as an integer.
# Extended: Retrieves the setting for the given key as an integer.
#
# keyPath - The {String} name of the key to retrieve
# * `keyPath` The {String} name of the key to retrieve
#
# Returns the value from Atom's default settings, the user's configuration
# file, or `NaN` if the key doesn't exist in either.
getInt: (keyPath) ->
parseInt(@get(keyPath))
# Public: Retrieves the setting for the given key as a positive integer.
# Extended: Retrieves the setting for the given key as a positive integer.
#
# keyPath - The {String} name of the key to retrieve
# defaultValue - The integer {Number} to fall back to if the value isn't
# * `keyPath` The {String} name of the key to retrieve
# * `defaultValue` The integer {Number} to fall back to if the value isn't
# positive, defaults to 0.
#
# Returns the value from Atom's default settings, the user's configuration
@@ -137,12 +137,12 @@ class Config
getPositiveInt: (keyPath, defaultValue=0) ->
Math.max(@getInt(keyPath), 0) or defaultValue
# Public: Sets the value for a configuration setting.
# Essential: Sets the value for a configuration setting.
#
# This value is stored in Atom's internal configuration file.
#
# keyPath - The {String} name of the key.
# value - The value of the setting.
# * `keyPath` The {String} name of the key.
# * `value` The value of the setting.
#
# Returns the `value`.
set: (keyPath, value) ->
@@ -153,47 +153,47 @@ class Config
@update()
value
# Public: Toggle the value at the key path.
# Extended: Toggle the value at the key path.
#
# The new value will be `true` if the value is currently falsy and will be
# `false` if the value is currently truthy.
#
# keyPath - The {String} name of the key.
# * `keyPath` The {String} name of the key.
#
# Returns the new value.
toggle: (keyPath) ->
@set(keyPath, !@get(keyPath))
# Public: Restore the key path to its default value.
# Extended: Restore the key path to its default value.
#
# keyPath - The {String} name of the key.
# * `keyPath` The {String} name of the key.
#
# Returns the new value.
restoreDefault: (keyPath) ->
@set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath))
# Public: Get the default value of the key path.
# Extended: Get the default value of the key path.
#
# keyPath - The {String} name of the key.
# * `keyPath` The {String} name of the key.
#
# Returns the default value.
getDefault: (keyPath) ->
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
_.deepClone(defaultValue)
# Public: Is the key path value its default value?
# Extended: Is the key path value its default value?
#
# keyPath - The {String} name of the key.
# * `keyPath` The {String} name of the key.
#
# Returns a {Boolean}, `true` if the current value is the default, `false`
# otherwise.
isDefault: (keyPath) ->
not _.valueForKeyPath(@settings, keyPath)?
# Public: Push the value to the array at the key path.
# Extended: Push the value to the array at the key path.
#
# keyPath - The {String} key path.
# value - The value to push to the array.
# * `keyPath` The {String} key path.
# * `value` The value to push to the array.
#
# Returns the new array length {Number} of the setting.
pushAtKeyPath: (keyPath, value) ->
@@ -202,10 +202,10 @@ class Config
@set(keyPath, arrayValue)
result
# Public: Add the value to the beginning of the array at the key path.
# Extended: Add the value to the beginning of the array at the key path.
#
# keyPath - The {String} key path.
# value - The value to shift onto the array.
# * `keyPath` The {String} key path.
# * `value` The value to shift onto the array.
#
# Returns the new array length {Number} of the setting.
unshiftAtKeyPath: (keyPath, value) ->
@@ -216,8 +216,8 @@ class Config
# Public: Remove the value from the array at the key path.
#
# keyPath - The {String} key path.
# value - The value to remove from the array.
# * `keyPath` The {String} key path.
# * `value` The value to remove from the array.
#
# Returns the new array value of the setting.
removeAtKeyPath: (keyPath, value) ->
@@ -226,20 +226,17 @@ class Config
@set(keyPath, arrayValue)
result
# Public: Establishes an event listener for a given key.
# Essential: Add a listener for changes to a given key path.
#
# `callback` is fired whenever the value of the key is changed and will
# be fired immediately unless the `callNow` option is `false`.
#
# keyPath - The {String} name of the key to observe
# options - An optional {Object} containing the `callNow` key.
# callback - The {Function} to call when the value of the key changes.
# The first argument will be the new value of the key and the
#   second argument will be an {Object} with a `previous` property
# that is the prior value of the key.
# * `keyPath` The {String} name of the key to observe
# * `options` An optional {Object} containing the `callNow` key.
# * `callback` The {Function} to call when the value of the key changes.
# The first argument will be the new value of the key and the
#   second argument will be an {Object} with a `previous` property
# that is the prior value of the key.
#
# Returns an {Object} with the following keys:
# :off - A {Function} that unobserves the `keyPath` when called.
# * `off` A {Function} that unobserves the `keyPath` when called.
observe: (keyPath, options={}, callback) ->
if _.isFunction(options)
callback = options
@@ -259,9 +256,9 @@ class Config
callback(value) if options.callNow ? true
subscription
# Public: Unobserve all callbacks on a given key.
# Unobserve all callbacks on a given key.
#
# keyPath - The {String} name of the key to unobserve.
# * `keyPath` The {String} name of the key to unobserve.
unobserve: (keyPath) ->
@off("updated.#{keyPath.replace(/\./, '-')}")

View File

@@ -22,15 +22,13 @@ class ContextMenuManager
]
# Public: Creates menu definitions from the object specified by the menu
# cson API.
# JSON API.
#
# name - The path of the file that contains the menu definitions.
# object - The 'context-menu' object specified in the menu cson API.
# options - An {Object} with the following keys:
# :devMode - Determines whether the entries should only be shown when
# the window is in dev mode.
#
# Returns nothing.
# * `name` The path of the file that contains the menu definitions.
# * `object` The 'context-menu' object specified in the menu JSON API.
# * `options` An optional {Object} with the following keys:
# * `devMode` Determines whether the entries should only be shown when
# the window is in dev mode.
add: (name, object, {devMode}={}) ->
for selector, items of object
for label, commandOrSubmenu of items
@@ -43,6 +41,8 @@ class ContextMenuManager
menuItem = @buildMenuItem(label, commandOrSubmenu)
@addBySelector(selector, menuItem, {devMode})
undefined
buildMenuItem: (label, command) ->
if command is '-'
{type: 'separator'}
@@ -52,12 +52,12 @@ class ContextMenuManager
# Registers a command to be displayed when the relevant item is right
# clicked.
#
# selector - The css selector for the active element which should include
# the given command in its context menu.
# definition - The object containing keys which match the menu template API.
# options - An {Object} with the following keys:
# :devMode - Indicates whether this command should only appear while the
# editor is in dev mode.
# * `selector` The css selector for the active element which should include
# the given command in its context menu.
# * `definition` The object containing keys which match the menu template API.
# * `options` An optional {Object} with the following keys:
# * `devMode` Indicates whether this command should only appear while the
# editor is in dev mode.
addBySelector: (selector, definition, {devMode}={}) ->
definitions = if devMode then @devModeDefinitions else @definitions
if not _.findWhere(definitions[selector], definition) or _.isEqual(definition, {type: 'separator'})
@@ -79,7 +79,7 @@ class ContextMenuManager
# active element are listed first. The further down the list you go, the higher
# up the ancestor hierarchy they match.
#
# element - The DOM element to generate the menu template for.
# * `element` The DOM element to generate the menu template for.
menuTemplateForMostSpecificElement: (element, {devMode}={}) ->
menuTemplate = @definitionsForElement(element, {devMode})
if element.parentElement
@@ -109,9 +109,12 @@ class ContextMenuManager
delete template.executeAtBuild
# Public: Request a context menu to be displayed.
#
# * `event` A DOM event.
showForEvent: (event) ->
@activeElement = event.target
menuTemplate = @combinedMenuTemplateForElement(event.target)
return unless menuTemplate?.length > 0
@executeBuildHandlers(event, menuTemplate)
remote.getCurrentWindow().emit('context-menu', menuTemplate)
undefined

View File

@@ -1,113 +0,0 @@
{View} = require './space-pen-extensions'
_ = require 'underscore-plus'
module.exports =
class CursorView extends View
@content: ->
@div class: 'cursor idle', => @raw '&nbsp;'
@blinkPeriod: 800
@blinkCursors: ->
element.classList.toggle('blink-off') for [element] in @cursorViews
@startBlinking: (cursorView) ->
@cursorViews ?= []
@cursorViews.push(cursorView)
if @cursorViews.length is 1
@blinkInterval = setInterval(@blinkCursors.bind(this), @blinkPeriod / 2)
@stopBlinking: (cursorView) ->
cursorView[0].classList.remove('blink-off')
_.remove(@cursorViews, cursorView)
clearInterval(@blinkInterval) if @cursorViews.length is 0
blinking: false
visible: true
needsUpdate: true
needsRemoval: false
shouldPauseBlinking: false
initialize: (@cursor, @editorView) ->
@subscribe @cursor, 'moved', =>
@needsUpdate = true
@shouldPauseBlinking = true
@subscribe @cursor, 'visibility-changed', =>
@needsUpdate = true
@subscribe @cursor, 'autoscrolled', =>
@editorView.requestDisplayUpdate()
@subscribe @cursor, 'destroyed', =>
@needsRemoval = true
beforeRemove: ->
@editorView.removeCursorView(this)
@stopBlinking()
updateDisplay: ->
screenPosition = @getScreenPosition()
pixelPosition = @getPixelPosition()
unless _.isEqual(@lastPixelPosition, pixelPosition)
@lastPixelPosition = pixelPosition
@css(pixelPosition)
@trigger 'cursor:moved'
if @shouldPauseBlinking
@resetBlinking()
else if !@startBlinkingTimeout
@startBlinking()
@setVisible(@cursor.isVisible() and not @editorView.getEditor().isFoldedAtScreenRow(screenPosition.row))
# Override for speed. The base function checks the computedStyle
isHidden: ->
this[0].style.display is 'none' or not @isOnDom()
needsAutoscroll: ->
@cursor.needsAutoscroll
clearAutoscroll: ->
@cursor.clearAutoscroll()
getPixelPosition: ->
@editorView.pixelPositionForScreenPosition(@getScreenPosition())
setVisible: (visible) ->
unless @visible is visible
@visible = visible
hiddenCursor = 'hidden-cursor'
if visible
@removeClass hiddenCursor
else
@addClass hiddenCursor
stopBlinking: ->
@constructor.stopBlinking(this) if @blinking
@blinking = false
startBlinking: ->
@constructor.startBlinking(this) unless @blinking
@blinking = true
resetBlinking: ->
@stopBlinking()
@startBlinking()
getBufferPosition: ->
@cursor.getBufferPosition()
getScreenPosition: ->
@cursor.getScreenPosition()
removeIdleClassTemporarily: ->
@removeClass 'idle'
window.clearTimeout(@idleTimeout) if @idleTimeout
@idleTimeout = window.setTimeout (=> @addClass 'idle'), 200
resetCursorAnimation: ->
window.clearTimeout(@idleTimeout) if @idleTimeout
@removeClass 'idle'
_.defer => @addClass 'idle'

View File

@@ -2,11 +2,36 @@
{Model} = require 'theorist'
_ = require 'underscore-plus'
# Public: The `Cursor` class represents the little blinking line identifying
# Extended: The `Cursor` class represents the little blinking line identifying
# where text can be inserted.
#
# Cursors belong to {Editor}s and have some metadata attached in the form
# of a {Marker}.
#
# ## Events
#
# ### moved
#
# Extended: Emit when a cursor has been moved. If there are multiple cursors,
# it will be emit for each cursor.
#
# * `event` {Object}
# * `oldBufferPosition` {Point}
# * `oldScreenPosition` {Point}
# * `newBufferPosition` {Point}
# * `newScreenPosition` {Point}
# * `textChanged` {Boolean}
#
# ### destroyed
#
# Extended: Emit when the cursor is destroyed
#
# ### visibility-changed
#
# Extended: Emit when the Cursor is hidden or shown
#
# * `visible` {Boolean} true when cursor is visible
#
module.exports =
class Cursor extends Model
screenPosition: null
@@ -63,11 +88,10 @@ class Cursor extends Model
# Public: Moves a cursor to a given screen position.
#
# screenPosition - An {Array} of two numbers: the screen row, and the screen
# column.
# options - An {Object} with the following keys:
# :autoscroll - A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
# * `screenPosition` {Array} of two numbers: the screen row, and the screen column.
# * `options` (optional) {Object} with the following keys:
# * `autoscroll` A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
setScreenPosition: (screenPosition, options={}) ->
@changePosition options, =>
@marker.setHeadScreenPosition(screenPosition, options)
@@ -82,11 +106,10 @@ class Cursor extends Model
# Public: Moves a cursor to a given buffer position.
#
# bufferPosition - An {Array} of two numbers: the buffer row, and the buffer
# column.
# options - An {Object} with the following keys:
# :autoscroll - A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
# * `bufferPosition` {Array} of two numbers: the buffer row, and the buffer column.
# * `options` (optional) {Object} with the following keys:
# * `autoscroll` A Boolean which, if `true`, scrolls the {Editor} to wherever
# the cursor moves to.
setBufferPosition: (bufferPosition, options={}) ->
@changePosition options, =>
@marker.setHeadBufferPosition(bufferPosition, options)
@@ -114,10 +137,9 @@ class Cursor extends Model
# Public: Get the RegExp used by the cursor to determine what a "word" is.
#
# options: An optional {Object} with the following keys:
# :includeNonWordCharacters - A {Boolean} indicating whether to include
# non-word characters in the regex.
# (default: true)
# * `options` (optional) {Object} with the following keys:
# * `includeNonWordCharacters` A {Boolean} indicating whether to include
# non-word characters in the regex. (default: true)
#
# Returns a {RegExp}.
wordRegExp: ({includeNonWordCharacters}={}) ->
@@ -228,9 +250,9 @@ class Cursor extends Model
# Public: Moves the cursor left one screen column.
#
# options - An {Object} with the following keys:
# :moveToEndOfSelection - if true, move to the left of the selection if a
# selection exists.
# * `options` (optional) {Object} with the following keys:
# * `moveToEndOfSelection` if true, move to the left of the selection if a
# selection exists.
moveLeft: ({moveToEndOfSelection}={}) ->
range = @marker.getScreenRange()
if moveToEndOfSelection and not range.isEmpty()
@@ -242,9 +264,9 @@ class Cursor extends Model
# Public: Moves the cursor right one screen column.
#
# options - An {Object} with the following keys:
# :moveToEndOfSelection - if true, move to the right of the selection if a
# selection exists.
# * `options` (optional) {Object} with the following keys:
# * `moveToEndOfSelection` if true, move to the right of the selection if a
# selection exists.
moveRight: ({moveToEndOfSelection}={}) ->
range = @marker.getScreenRange()
if moveToEndOfSelection and not range.isEmpty()
@@ -332,14 +354,14 @@ class Cursor extends Model
# Public: Retrieves the buffer position of where the current word starts.
#
# options - An {Object} with the following keys:
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
# :includeNonWordCharacters - A {Boolean} indicating whether to include
# non-word characters in the default word regex.
# Has no effect if wordRegex is set.
# :allowPrevious - A {Boolean} indicating whether the beginning of the
# previous word can be returned.
# * `options` (optional) An {Object} with the following keys:
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
# * `includeNonWordCharacters` A {Boolean} indicating whether to include
# non-word characters in the default word regex.
# Has no effect if wordRegex is set.
# * `allowPrevious` A {Boolean} indicating whether the beginning of the
# previous word can be returned.
#
# Returns a {Range}.
getBeginningOfCurrentWordBufferPosition: (options = {}) ->
@@ -407,12 +429,12 @@ class Cursor extends Model
# Public: Retrieves the buffer position of where the current word ends.
#
# options - An {Object} with the following keys:
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp})
# :includeNonWordCharacters - A Boolean indicating whether to include
# non-word characters in the default word regex.
# Has no effect if wordRegex is set.
# * `options` (optional) {Object} with the following keys:
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp})
# * `includeNonWordCharacters` A Boolean indicating whether to include
# non-word characters in the default word regex. Has no effect if
# wordRegex is set.
#
# Returns a {Range}.
getEndOfCurrentWordBufferPosition: (options = {}) ->
@@ -431,11 +453,11 @@ class Cursor extends Model
# Public: Retrieves the buffer position of where the next word starts.
#
# options -
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
# * `options` (optional) {Object}
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
#
# Returns a {Range}.
# Returns a {Range}
getBeginningOfNextWordBufferPosition: (options = {}) ->
currentBufferPosition = @getBufferPosition()
start = if @isInsideWord() then @getEndOfCurrentWordBufferPosition() else currentBufferPosition
@@ -450,9 +472,9 @@ class Cursor extends Model
# Public: Returns the buffer Range occupied by the word located under the cursor.
#
# options -
# :wordRegex - A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
# * `options` (optional) {Object}
# * `wordRegex` A {RegExp} indicating what constitutes a "word"
# (default: {::wordRegExp}).
getCurrentWordBufferRange: (options={}) ->
startOptions = _.extend(_.clone(options), allowPrevious: false)
endOptions = _.extend(_.clone(options), allowNext: false)
@@ -460,9 +482,9 @@ class Cursor extends Model
# Public: Returns the buffer Range for the current line.
#
# options -
# :includeNewline: - A {Boolean} which controls whether the Range should
# include the newline.
# * `options` (optional) {Object}
# * `includeNewline` A {Boolean} which controls whether the Range should
# include the newline.
getCurrentLineBufferRange: (options) ->
@editor.bufferRangeForBufferRow(@getBufferRow(), options)

View File

@@ -4,7 +4,7 @@ _ = require 'underscore-plus'
idCounter = 0
nextId = -> idCounter++
# Public: Represents a decoration that follows a {Marker}. A decoration is
# Essential: Represents a decoration that follows a {Marker}. A decoration is
# basically a visual representation of a marker. It allows you to add CSS
# classes to line numbers in the gutter, lines, and add selection-line regions
# around marked ranges of text.
@@ -20,27 +20,39 @@ nextId = -> idCounter++
#
# Best practice for destorying the decoration is by destroying the {Marker}.
#
# ```
# ```coffee
# marker.destroy()
# ```
#
# You should only use {Decoration::destroy} when you still need or do not own
# the marker.
#
# ### IDs
# Each {Decoration} has a unique ID available via `decoration.id`.
# ## Events
#
# ### Events
# A couple of events are emitted:
# ### updated
#
# * `destroyed`: When the {Decoration} is destroyed
# * `updated`: When the {Decoration} is updated via {Decoration::update}.
# Event object has properties `oldParams` and `newParams`
# Extended: When the {Decoration} is updated via {Decoration::update}.
#
# * `event` {Object}
# * `oldParams` {Object} the old parameters the decoration used to have
# * `newParams` {Object} the new parameters the decoration now has
#
# ### destroyed
#
# Extended: When the {Decoration} is destroyed
#
module.exports =
class Decoration
Emitter.includeInto(this)
# Extended: Check if the `decorationParams.type` matches `type`
#
# * `decorationParams` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
# be an {Array} of {String}s, where it will return true if the decoration's
# type matches any in the array.
#
# Returns {Boolean}
@isType: (decorationParams, type) ->
if _.isArray(decorationParams.type)
type in decorationParams.type
@@ -53,21 +65,34 @@ class Decoration
@flashQueue = null
@isDestroyed = false
# Public: Destroy this marker.
#
# If you own the marker, you should use {Marker::destroy} which will destroy
# this decoration.
destroy: ->
return if @isDestroyed
@isDestroyed = true
@displayBuffer.removeDecoration(this)
@emit 'destroyed'
# Essential: An id unique across all {Decoration} objects
getId: -> @id
# Public: Update the marker with new params. Allows you to change the decoration's class.
# Essential: Returns the marker associated with this {Decoration}
getMarker: -> @marker
# Essential: Returns the {Decoration}'s params.
getParams: -> @params
# Public: Check if this decoration is of type `type`
#
# ```
# * `type` {String} type like `'gutter'`, `'line'`, etc. `type` can also
# be an {Array} of {String}s, where it will return true if the decoration's
# type matches any in the array.
#
# Returns {Boolean}
isType: (type) ->
Decoration.isType(@params, type)
# Essential: Update the marker with new params. Allows you to change the decoration's class.
#
# ## Examples
#
# ```coffee
# decoration.update({type: 'gutter', class: 'my-new-class'})
# ```
#
# * `newParams` {Object} eg. `{type: 'gutter', class: 'my-new-class'}`
update: (newParams) ->
return if @isDestroyed
oldParams = @params
@@ -76,19 +101,15 @@ class Decoration
@displayBuffer.decorationUpdated(this)
@emit 'updated', {oldParams, newParams}
# Public: Returns the marker associated with this {Decoration}
getMarker: -> @marker
# Public: Returns the {Decoration}'s params.
getParams: -> @params
# Public: Check if this decoration is of type `type`
# Essential: Destroy this marker.
#
# type - A {String} type like `'gutter'`
#
# Returns a {Boolean}
isType: (type) ->
Decoration.isType(@params, type)
# If you own the marker, you should use {Marker::destroy} which will destroy
# this decoration.
destroy: ->
return if @isDestroyed
@isDestroyed = true
@displayBuffer.removeDecoration(this)
@emit 'destroyed'
matchesPattern: (decorationPattern) ->
return false unless decorationPattern?

View File

@@ -3,7 +3,7 @@
# An instance of this class is always available as the `atom.deserializers`
# global.
#
# ### Registering a deserializer
# ## Examples
#
# ```coffee
# class MyPackageView extends View
@@ -24,21 +24,21 @@ class DeserializerManager
# Public: Register the given class(es) as deserializers.
#
# classes - One or more classes to register.
# * `classes` One or more classes to register.
add: (classes...) ->
@deserializers[klass.name] = klass for klass in classes
# Public: Remove the given class(es) as deserializers.
#
# classes - One or more classes to remove.
# * `classes` One or more classes to remove.
remove: (classes...) ->
delete @deserializers[name] for {name} in classes
# Public: Deserialize the state and params.
#
# state - The state {Object} to deserialize.
# params - The params {Object} to pass as the second arguments to the
# deserialize method of the deserializer.
# * `state` The state {Object} to deserialize.
# * `params` The params {Object} to pass as the second arguments to the
# deserialize method of the deserializer.
deserialize: (state, params) ->
return unless state?
@@ -51,7 +51,7 @@ class DeserializerManager
# Get the deserializer for the state.
#
# state - The state {Object} being deserialized.
# * `state` The state {Object} being deserialized.
get: (state) ->
return unless state?

View File

@@ -1097,7 +1097,10 @@ class DisplayBuffer extends Model
handleBufferMarkerCreated: (marker) =>
@createFoldForMarker(marker) if marker.matchesAttributes(@getFoldMarkerAttributes())
@emit 'marker-created', @getMarker(marker.id)
if displayBufferMarker = @getMarker(marker.id)
# The marker might have been removed in some other handler called before
# this one. Only emit when the marker still exists.
@emit 'marker-created', displayBufferMarker
createFoldForMarker: (marker) ->
@decorateMarker(marker, type: 'gutter', class: 'folded')

View File

@@ -59,6 +59,8 @@ EditorComponent = React.createClass
[renderedStartRow, renderedEndRow] = renderedRowRange
cursorPixelRects = @getCursorPixelRects(renderedRowRange)
tokenizedLines = editor.linesForScreenRows(renderedStartRow, renderedEndRow - 1)
decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow)
highlightDecorations = @getHighlightDecorations(decorations)
lineDecorations = @getLineDecorations(decorations)
@@ -107,7 +109,7 @@ EditorComponent = React.createClass
LinesComponent {
ref: 'lines',
editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations,
editor, lineHeightInPixels, defaultCharWidth, tokenizedLines, lineDecorations, highlightDecorations,
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow,
visible, scrollViewHeight, @scopedCharacterWidthsChangeCount, lineWidth, @useHardwareAcceleration,
@@ -191,11 +193,6 @@ EditorComponent = React.createClass
componentWillReceiveProps: (newProps) ->
@props.editor.setMini(newProps.mini)
componentWillUpdate: ->
@updatesPaused = true
@checkForVisibilityChange()
@updatesPaused = false
componentDidUpdate: (prevProps, prevState) ->
cursorsMoved = @cursorsMoved
selectionChanged = @selectionChanged
@@ -211,6 +208,7 @@ EditorComponent = React.createClass
@props.parentView.trigger 'editor:display-updated'
becameVisible: ->
@updatesPaused = true
@sampleFontStyling()
@sampleBackgroundColors()
@measureHeightAndWidth()
@@ -219,6 +217,8 @@ EditorComponent = React.createClass
@remeasureCharacterWidths() if @remeasureCharacterWidthsWhenShown
@props.editor.setVisible(true)
@performedInitialMeasurement = true
@updatesPaused = false
@forceUpdate() if @updateRequestedWhilePaused
requestUpdate: ->
return unless @isMounted()
@@ -610,6 +610,10 @@ EditorComponent = React.createClass
{editor} = @props
{detail, shiftKey, metaKey, ctrlKey} = event
# CTRL+click brings up the context menu on OSX, so don't handle those either
return if ctrlKey and process.platform is 'darwin'
screenPosition = @screenPositionForMouseEvent(event)
if event.target?.classList.contains('fold-marker')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -20,16 +20,16 @@ Task = require './task'
# For a repository with submodules this would have the following outcome:
#
# ```coffee
# repo = atom.project.getRepo()
# repo.getShortHead() # 'master'
# repo.getShortHead('vendor/path/to/a/submodule') # 'dead1234'
# repo = atom.project.getRepo()
# repo.getShortHead() # 'master'
# repo.getShortHead('vendor/path/to/a/submodule') # 'dead1234'
# ```
#
# ## Example
# ## Examples
#
# ```coffeescript
# git = atom.project.getRepo()
# console.log git.getOriginUrl()
# ```coffee
# git = atom.project.getRepo()
# console.log git.getOriginUrl()
# ```
#
# ## Requiring in packages
@@ -44,12 +44,12 @@ class Git
# Public: Creates a new Git instance.
#
# path - The path to the Git repository to open.
# options - An object with the following keys (default: {}):
# :refreshOnWindowFocus - `true` to refresh the index and statuses when the
# window is focused.
# * `path` The {String} path to the Git repository to open.
# * `options` An optinal {Object} with the following keys:
# * `refreshOnWindowFocus` A {Boolean}, `true` to refresh the index and
# statuses when the window is focused.
#
# Returns a Git instance or null if the repository could not be opened.
# Returns a {Git} instance or `null` if the repository could not be opened.
@open: (path, options) ->
return null unless path
try
@@ -114,8 +114,10 @@ class Git
else
checkoutHead()
# Public: Destroy this `Git` object. This destroys any tasks and subscriptions
# and releases the underlying libgit2 repository handle.
# Public: Destroy this {Git} object.
#
# This destroys any tasks and subscriptions and releases the underlying
# libgit2 repository handle.
destroy: ->
if @statusTask?
@statusTask.terminate()
@@ -147,7 +149,7 @@ class Git
# Public: Get the status of a single path in the repository.
#
# path - A {String} repository-relative path.
# `path` A {String} repository-relative path.
#
# Returns a {Number} representing the status. This value can be passed to
# {::isStatusModified} or {::isStatusNew} to get more information.
@@ -196,8 +198,8 @@ class Git
# `refs/remotes`. It also shortens the SHA-1 of a detached `HEAD` to 7
# characters.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository contains submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository contains submodules.
#
# Returns a {String}.
getShortHead: (path) -> @getRepo(path).getShortHead()
@@ -207,12 +209,12 @@ class Git
#
# This is essentially the same as running:
#
# ```
# git reset HEAD -- <path>
# git checkout HEAD -- <path>
# ```sh
# git reset HEAD -- <path>
# git checkout HEAD -- <path>
# ```
#
# path - The {String} path to checkout.
# * `path` The {String} path to checkout.
#
# Returns a {Boolean} that's true if the method was successful.
checkoutHead: (path) ->
@@ -223,9 +225,9 @@ class Git
# Public: Checks out a branch in your repository.
#
# reference - The String reference to checkout
# create - A Boolean value which, if true creates the new reference if it
# doesn't exist.
# * `reference` The {String} reference to checkout.
# * `create` A {Boolean} value which, if true creates the new reference if
# it doesn't exist.
#
# Returns a Boolean that's true if the method was successful.
checkoutReference: (reference, create) ->
@@ -236,18 +238,18 @@ class Git
# This compares the working directory contents of the path to the `HEAD`
# version.
#
# path - The {String} path to check.
# * `path` The {String} path to check.
#
# Returns an {Object} with the following keys:
# :added - The {Number} of added lines.
# :deleted - The {Number} of deleted lines.
# * `added` The {Number} of added lines.
# * `deleted` The {Number} of deleted lines.
getDiffStats: (path) ->
repo = @getRepo(path)
repo.getDiffStats(repo.relativize(path))
# Public: Is the given path a submodule in the repository?
#
# path - The {String} path to check.
# * `path` The {String} path to check.
#
# Returns a {Boolean}.
isSubmodule: (path) ->
@@ -262,7 +264,7 @@ class Git
# Public: Get the status of a directory in the repository's working directory.
#
# path - The {String} path to check.
# * `path` The {String} path to check.
#
# Returns a {Number} representing the status. This value can be passed to
# {::isStatusModified} or {::isStatusNew} to get more information.
@@ -276,14 +278,14 @@ class Git
# Public: Retrieves the line diffs comparing the `HEAD` version of the given
# path and the given text.
#
# path - The {String} path relative to the repository.
# text - The {String} to compare against the `HEAD` contents
# * `path` The {String} path relative to the repository.
# * `text` The {String} to compare against the `HEAD` contents
#
# Returns an {Array} of hunk {Object}s with the following keys:
# :oldStart - The line {Number} of the old hunk.
# :newStart - The line {Number} of the new hunk.
# :oldLines - The {Number} of lines in the old hunk.
# :newLines - The {Number} of lines in the new hunk
# * `oldStart` The line {Number} of the old hunk.
# * `newStart` The line {Number} of the new hunk.
# * `oldLines` The {Number} of lines in the old hunk.
# * `newLines` The {Number} of lines in the new hunk
getLineDiffs: (path, text) ->
# Ignore eol of line differences on windows so that files checked in as
# LF don't report every line modified when the text contains CRLF endings.
@@ -293,68 +295,68 @@ class Git
# Public: Returns the git configuration value specified by the key.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
getConfigValue: (key, path) -> @getRepo(path).getConfigValue(key)
# Public: Returns the origin url of the repository.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
getOriginUrl: (path) -> @getConfigValue('remote.origin.url', path)
# Public: Returns the upstream branch for the current HEAD, or null if there
# is no upstream branch for the current HEAD.
#
# path - An optional {String} path in the repo to get this information for,
# only needed if the repository contains submodules.
# * `path` An optional {String} path in the repo to get this information for,
# only needed if the repository contains submodules.
#
# Returns a {String} branch name such as `refs/remotes/origin/master`.
getUpstreamBranch: (path) -> @getRepo(path).getUpstreamBranch()
# Public: Returns the current SHA for the given reference.
# Public: Returns the current {String} SHA for the given reference.
#
# reference - The {String} reference to get the target of.
# path - An optional {String} path in the repo to get the reference target
# for. Only needed if the repository contains submodules.
# * `reference` The {String} reference to get the target of.
# * `path` An optional {String} path in the repo to get the reference target
# for. Only needed if the repository contains submodules.
getReferenceTarget: (reference, path) ->
@getRepo(path).getReferenceTarget(reference)
# Public: Gets all the local and remote references.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
#
# Returns an {Object} with the following keys:
# :heads - An {Array} of head reference names.
# :remotes - An {Array} of remote reference names.
# :tags - An {Array} of tag reference names.
# * `heads` An {Array} of head reference names.
# * `remotes` An {Array} of remote reference names.
# * `tags` An {Array} of tag reference names.
getReferences: (path) -> @getRepo(path).getReferences()
# Public: Returns the number of commits behind the current branch is from the
# its upstream remote branch.
#
# reference - The {String} branch reference name.
# path - The {String} path in the repository to get this information for,
# only needed if the repository contains submodules.
# * `reference` The {String} branch reference name.
# * `path` The {String} path in the repository to get this information for,
# only needed if the repository contains submodules.
getAheadBehindCount: (reference, path) ->
@getRepo(path).getAheadBehindCount(reference)
# Public: Get the cached ahead/behind commit counts for the current branch's
# upstream branch.
#
# path - An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
# * `path` An optional {String} path in the repository to get this information
# for, only needed if the repository has submodules.
#
# Returns an {Object} with the following keys:
# :ahead - The {Number} of commits ahead.
# :behind - The {Number} of commits behind.
# * `ahead` The {Number} of commits ahead.
# * `behind` The {Number} of commits behind.
getCachedUpstreamAheadBehindCount: (path) ->
@getRepo(path).upstream ? @upstream
# Public: Get the cached status for the given path.
#
# path - A {String} path in the repository, relative or absolute.
# * `path` A {String} path in the repository, relative or absolute.
#
# Returns a status {Number} or null if the path is not in the cache.
getCachedPathStatus: (path) ->

View File

@@ -1,275 +0,0 @@
{View, $, $$, $$$} = require './space-pen-extensions'
{Range} = require 'text-buffer'
_ = require 'underscore-plus'
# Represents the portion of the {EditorView} containing row numbers.
#
# The gutter also indicates if rows are folded.
module.exports =
class GutterView extends View
@content: ->
@div class: 'gutter', =>
@div outlet: 'lineNumbers', class: 'line-numbers'
firstScreenRow: null
lastScreenRow: null
initialize: ->
@elementBuilder = document.createElement('div')
afterAttach: (onDom) ->
return if @attached or not onDom
@attached = true
highlightLines = => @highlightLines()
@getEditorView().on 'cursor:moved', highlightLines
@getEditorView().on 'selection:changed', highlightLines
@on 'mousedown', (e) => @handleMouseEvents(e)
beforeRemove: ->
$(document).off(".gutter-#{@getEditorView().id}")
handleMouseEvents: (e) ->
editorView = @getEditorView()
editor = @getEditor()
startRow = editorView.screenPositionFromMouseEvent(e).row
if e.shiftKey
editor.selectToScreenPosition([startRow + 1, 0])
return
else
editor.getSelection().setScreenRange([[startRow, 0], [startRow, 0]])
moveHandler = (e) ->
start = startRow
end = editorView.screenPositionFromMouseEvent(e).row
if end > start then end++ else start++
editor.getSelection().setScreenRange([[start, 0], [end, 0]])
$(document).on "mousemove.gutter-#{editorView.id}", moveHandler
$(document).one "mouseup.gutter-#{editorView.id}", -> $(document).off 'mousemove', moveHandler
# Retrieves the containing {EditorView}.
#
# Returns an {EditorView}.
getEditorView: ->
@parentView
getEditor: ->
@getEditorView().getEditor()
# Defines whether to show the gutter or not.
#
# showLineNumbers - A {Boolean} which, if `false`, hides the gutter
setShowLineNumbers: (showLineNumbers) ->
if showLineNumbers then @lineNumbers.show() else @lineNumbers.hide()
# Get all the line-number divs.
#
# Returns a list of {HTMLElement}s.
getLineNumberElements: ->
@lineNumbers[0].children
# Get all the line-number divs.
#
# Returns a list of {HTMLElement}s.
getLineNumberElementsForClass: (klass) ->
@lineNumbers[0].getElementsByClassName(klass)
# Get a single line-number div.
#
# * bufferRow: 0 based line number
#
# Returns a list of {HTMLElement}s that correspond to the bufferRow. More than
# one in the list indicates a wrapped line.
getLineNumberElement: (bufferRow) ->
@getLineNumberElementsForClass("line-number-#{bufferRow}")
# Add a class to all line-number divs.
#
# * klass: string class name
#
# Returns true if the class was added to any lines
addClassToAllLines: (klass) ->
elements = @getLineNumberElements()
el.classList.add(klass) for el in elements
!!elements.length
# Remove a class from all line-number divs.
#
# * klass: string class name. Can only be one class name. i.e. 'my-class'
#
# Returns true if the class was removed from any lines
removeClassFromAllLines: (klass) ->
# This is faster than calling $.removeClass on all lines, and faster than
# making a new array and iterating through it.
elements = @getLineNumberElementsForClass(klass)
willRemoveClasses = !!elements.length
elements[0].classList.remove(klass) while elements.length > 0
willRemoveClasses
# Add a class to a single line-number div
#
# * bufferRow: 0 based line number
# * klass: string class name
#
# Returns true if there were lines the class was added to
addClassToLine: (bufferRow, klass) ->
elements = @getLineNumberElement(bufferRow)
el.classList.add(klass) for el in elements
!!elements.length
# Remove a class from a single line-number div
#
# * bufferRow: 0 based line number
# * klass: string class name
#
# Returns true if there were lines the class was removed from
removeClassFromLine: (bufferRow, klass) ->
classesRemoved = false
elements = @getLineNumberElement(bufferRow)
for el in elements
hasClass = el.classList.contains(klass)
classesRemoved |= hasClass
el.classList.remove(klass) if hasClass
classesRemoved
updateLineNumbers: (changes, startScreenRow, endScreenRow) ->
# Check if we have something already rendered that overlaps the requested range
updateAllLines = not (startScreenRow? and endScreenRow?)
updateAllLines |= endScreenRow <= @firstScreenRow or startScreenRow >= @lastScreenRow
unless updateAllLines
for change in changes
if change.screenDelta or change.bufferDelta
updateAllLines = true
break
# Rebuild the entire gutter if a change added or removed lines
if updateAllLines
@lineNumbers[0].innerHTML = @buildLineElementsHtml(startScreenRow, endScreenRow)
# Handle changes that didn't add/remove lines more optimally
else
if startScreenRow < @firstScreenRow
@prependLineElements(@buildLineElements(startScreenRow, @firstScreenRow-1))
else if startScreenRow != @firstScreenRow
@removeLineElements(startScreenRow - @firstScreenRow)
if endScreenRow > @lastScreenRow
@appendLineElements(@buildLineElements(@lastScreenRow+1, endScreenRow))
else if endScreenRow != @lastScreenRow
@removeLineElements(endScreenRow - @lastScreenRow)
@updateFoldableClasses(changes)
@firstScreenRow = startScreenRow
@lastScreenRow = endScreenRow
@highlightedRows = null
@highlightLines()
prependLineElements: (lineElements) ->
anchor = @lineNumbers[0].children[0]
return appendLineElements(lineElements) unless anchor?
@lineNumbers[0].insertBefore(lineElements[0], anchor) while lineElements.length > 0
null # defeat coffeescript array return
appendLineElements: (lineElements) ->
@lineNumbers[0].appendChild(lineElements[0]) while lineElements.length > 0
null # defeat coffeescript array return
removeLineElements: (numberOfElements) ->
children = @getLineNumberElements()
# children is a live NodeList, so remove from the desired end {numberOfElements} times
if numberOfElements < 0
@lineNumbers[0].removeChild(children[children.length-1]) while numberOfElements++
else if numberOfElements > 0
@lineNumbers[0].removeChild(children[0]) while numberOfElements--
null # defeat coffeescript array return
buildLineElements: (startScreenRow, endScreenRow) ->
@elementBuilder.innerHTML = @buildLineElementsHtml(startScreenRow, endScreenRow)
@elementBuilder.children
buildLineElementsHtml: (startScreenRow, endScreenRow) =>
editor = @getEditor()
maxDigits = editor.getLineCount().toString().length
rows = editor.bufferRowsForScreenRows(startScreenRow, endScreenRow)
html = ''
for row in rows
if row is lastRow
rowValue = ''
else
rowValue = (row + 1).toString()
classes = "line-number line-number-#{row}"
classes += ' foldable' if row isnt lastRow and editor.isFoldableAtBufferRow(row)
classes += ' folded' if editor.isFoldedAtBufferRow(row)
rowValuePadding = _.multiplyString('&nbsp;', maxDigits - rowValue.length)
html += """<div class="#{classes}" data-buffer-row=#{row}>#{rowValuePadding}#{rowValue}<div class="icon-right"></div></div>"""
lastRow = row
html
# Called to update the 'foldable' class of line numbers when there's
# a change to the display buffer that doesn't regenerate all the line numbers
# anyway.
updateFoldableClasses: (changes) ->
editor = @getEditor()
for {start, end} in changes when start <= @lastScreenRow and end >= @firstScreenRow
startScreenRow = Math.max(start - 1, @firstScreenRow)
endScreenRow = Math.min(end + 1, @lastScreenRow)
lastBufferRow = null
for bufferRow in editor.bufferRowsForScreenRows(startScreenRow, endScreenRow) when bufferRow isnt lastBufferRow
lastBufferRow = bufferRow
if lineNumberElement = @getLineNumberElement(bufferRow)[0]
if editor.isFoldableAtBufferRow(bufferRow)
lineNumberElement.classList.add('foldable')
else
lineNumberElement.classList.remove('foldable')
removeLineHighlights: ->
return unless @highlightedLineNumbers
for line in @highlightedLineNumbers
line.classList.remove('cursor-line')
line.classList.remove('cursor-line-no-selection')
@highlightedLineNumbers = null
addLineHighlight: (row, emptySelection) ->
return if row < @firstScreenRow or row > @lastScreenRow
@highlightedLineNumbers ?= []
if highlightedLineNumber = @lineNumbers[0].children[row - @firstScreenRow]
highlightedLineNumber.classList.add('cursor-line')
highlightedLineNumber.classList.add('cursor-line-no-selection') if emptySelection
@highlightedLineNumbers.push(highlightedLineNumber)
highlightLines: ->
editor = @getEditor()
return unless editor?.isAlive()
if editor.getSelection().isEmpty()
row = editor.getCursorScreenPosition().row
rowRange = new Range([row, 0], [row, 0])
return if @selectionEmpty and @highlightedRows?.isEqual(rowRange)
@removeLineHighlights()
@addLineHighlight(row, true)
@highlightedRows = rowRange
@selectionEmpty = true
else
selectedRows = editor.getSelection().getScreenRange()
endRow = selectedRows.end.row
endRow-- if selectedRows.end.column is 0
selectedRows = new Range([selectedRows.start.row, 0], [endRow, 0])
return if not @selectionEmpty and @highlightedRows?.isEqual(selectedRows)
@removeLineHighlights()
for row in [selectedRows.start.row..selectedRows.end.row]
@addLineHighlight(row, false)
@highlightedRows = selectedRows
@selectionEmpty = false

View File

@@ -91,12 +91,11 @@ LinesComponent = React.createClass
@lineIdsByScreenRow = {}
updateLines: (updateWidth) ->
{editor, renderedRowRange, showIndentGuide, selectionChanged, lineDecorations} = @props
[startRow, endRow] = renderedRowRange
{tokenizedLines, renderedRowRange, showIndentGuide, selectionChanged, lineDecorations} = @props
[startRow] = renderedRowRange
visibleLines = editor.linesForScreenRows(startRow, endRow - 1)
@removeLineNodes(visibleLines)
@appendOrUpdateVisibleLineNodes(visibleLines, startRow, updateWidth)
@removeLineNodes(tokenizedLines)
@appendOrUpdateVisibleLineNodes(tokenizedLines, startRow, updateWidth)
removeLineNodes: (visibleLines=[]) ->
{mouseWheelScreenRow} = @props
@@ -147,7 +146,7 @@ LinesComponent = React.createClass
@lineNodesByLineId.hasOwnProperty(lineId)
buildLineHTML: (line, screenRow) ->
{editor, mini, showIndentGuide, lineHeightInPixels, lineDecorations, lineWidth} = @props
{mini, showIndentGuide, lineHeightInPixels, lineDecorations, lineWidth} = @props
{tokens, text, lineEnding, fold, isSoftWrapped, indentLevel} = line
classes = ''
@@ -244,7 +243,7 @@ LinesComponent = React.createClass
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
updateLineNode: (line, screenRow, updateWidth) ->
{editor, lineHeightInPixels, lineDecorations, lineWidth} = @props
{lineHeightInPixels, lineDecorations, lineWidth} = @props
lineNode = @lineNodesByLineId[line.id]
decorations = lineDecorations[screenRow]
@@ -292,12 +291,12 @@ LinesComponent = React.createClass
@measureCharactersInNewLines()
measureCharactersInNewLines: ->
{editor, renderedRowRange} = @props
[visibleStartRow, visibleEndRow] = renderedRowRange
{editor, tokenizedLines, renderedRowRange} = @props
[visibleStartRow] = renderedRowRange
node = @getDOMNode()
editor.batchCharacterMeasurement =>
for tokenizedLine in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
for tokenizedLine in tokenizedLines
unless @measuredLines.has(tokenizedLine)
lineNode = @lineNodesByLineId[tokenizedLine.id]
@measureCharactersInLine(tokenizedLine, lineNode)

View File

@@ -17,9 +17,9 @@ class MenuManager
atom.keymaps.on 'bundled-keymaps-loaded', => @loadPlatformItems()
atom.packages.on 'activated', => @sortPackagesMenu()
# Public: Adds the given item definition to the existing template.
# Public: Adds the given items to the application menu.
#
# ## Example
# ## Examples
# ```coffee
# atom.menu.add [
# {
@@ -29,23 +29,22 @@ class MenuManager
# ]
# ```
#
# items - An {Array} of menu item {Object}s containing the keys:
# :label - The {String} menu label.
# :submenu - An optional {Array} of sub menu items.
# :command - An optional {String} command to trigger when the item is
# clicked.
#
# Returns nothing.
# * `items` An {Array} of menu item {Object}s containing the keys:
# * `label` The {String} menu label.
# * `submenu` An optional {Array} of sub menu items.
# * `command` An optional {String} command to trigger when the item is
# clicked.
add: (items) ->
@merge(@template, item) for item in items
@update()
undefined
# Should the binding for the given selector be included in the menu
# commands.
#
# selector - A {String} selector to check.
# * `selector` A {String} selector to check.
#
# Returns true to include the selector, false otherwise.
# Returns a {Boolean}, true to include the selector, false otherwise.
includeSelector: (selector) ->
try
return true if document.body.webkitMatchesSelector(selector)

View File

@@ -21,7 +21,7 @@ ThemePackage = require './theme-package'
# `deactivate()` on the package's main module.
# * Unloading a package removes it completely from the package manager.
#
# Packages can also be enabled/disabled via the `core.disabledPackages` config
# Packages can be enabled/disabled via the `core.disabledPackages` config
# settings and also by calling `enablePackage()/disablePackage()`.
module.exports =
class PackageManager
@@ -41,15 +41,17 @@ class PackageManager
@packageActivators = []
@registerPackageActivator(this, ['atom', 'textmate'])
# Public: Get the path to the apm command
# Extended: Get the path to the apm command.
#
# Return a {String} file path to apm.
getApmPath: ->
commandName = 'apm'
commandName += '.cmd' if process.platform is 'win32'
@apmPath ?= path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', commandName)
# Public: Get the paths being used to look for packages.
# Extended: Get the paths being used to look for packages.
#
# Returns an Array of String directory paths.
# Returns an {Array} of {String} directory paths.
getPackageDirPaths: ->
_.clone(@packageDirPaths)
@@ -59,13 +61,17 @@ class PackageManager
setPackageState: (name, state) ->
@packageStates[name] = state
# Public: Enable the package with the given name
# Extended: Enable the package with the given name.
#
# Returns the {Package} that was enabled or null if it isn't loaded.
enablePackage: (name) ->
pack = @loadPackage(name)
pack?.enable()
pack
# Public: Disable the package with the given name
# Extended: Disable the package with the given name.
#
# Returns the {Package} that was disabled or null if it isn't loaded.
disablePackage: (name) ->
pack = @loadPackage(name)
pack?.disable()
@@ -110,15 +116,23 @@ class PackageManager
pack.deactivate()
delete @activePackages[pack.name]
# Public: Get an array of all the active packages
# Essential: Get an {Array} of all the active {Package}s.
getActivePackages: ->
_.values(@activePackages)
# Public: Get the active package with the given name
# Essential: Get the active {Package} with the given name.
#
# * `name` - The {String} package name.
#
# Returns a {Package} or undefined.
getActivePackage: (name) ->
@activePackages[name]
# Public: Is the package with the given name active?
# Public: Is the {Package} with the given name active?
#
# * `name` - The {String} package name.
#
# Returns a {Boolean}.
isPackageActive: (name) ->
@getActivePackage(name)?
@@ -179,25 +193,37 @@ class PackageManager
else
throw new Error("No loaded package for name '#{name}'")
# Public: Get the loaded package with the given name
# Essential: Get the loaded {Package} with the given name.
#
# * `name` - The {String} package name.
#
# Returns a {Package} or undefined.
getLoadedPackage: (name) ->
@loadedPackages[name]
# Public: Is the package with the given name loaded?
# Essential: Is the package with the given name loaded?
#
# * `name` - The {String} package name.
#
# Returns a {Boolean}.
isPackageLoaded: (name) ->
@getLoadedPackage(name)?
# Public: Get an array of all the loaded packages
# Essential: Get an {Array} of all the loaded {Package}s
getLoadedPackages: ->
_.values(@loadedPackages)
# Get packages for a certain package type
#
# types - an {Array} of {String}s like ['atom', 'textmate'].
# * `types` an {Array} of {String}s like ['atom', 'textmate'].
getLoadedPackagesForTypes: (types) ->
pack for pack in @getLoadedPackages() when pack.getType() in types
# Public: Resolve the given package name to a path on disk.
# Extended: Resolve the given package name to a path on disk.
#
# * `name` - The {String} package name.
#
# Return a {String} folder path or undefined if it could not be resolved.
resolvePackagePath: (name) ->
return name if fs.isDirectorySync(name)
@@ -207,7 +233,11 @@ class PackageManager
packagePath = path.join(@resourcePath, 'node_modules', name)
return packagePath if @hasAtomEngine(packagePath)
# Public: Is the package with the given name disabled?
# Essential: Is the package with the given name disabled?
#
# * `name` - The {String} package name.
#
# Returns a {Boolean}.
isPackageDisabled: (name) ->
_.include(atom.config.get('core.disabledPackages') ? [], name)
@@ -215,7 +245,11 @@ class PackageManager
metadata = Package.loadMetadata(packagePath, true)
metadata?.engines?.atom?
# Public: Is the package with the given name bundled with Atom?
# Extended: Is the package with the given name bundled with Atom?
#
# * `name` - The {String} package name.
#
# Returns a {Boolean}.
isBundledPackage: (name) ->
@getPackageDependencies().hasOwnProperty(name)
@@ -228,7 +262,7 @@ class PackageManager
@packageDependencies
# Public: Get an array of all the available package paths.
# Extended: Get an {Array} of {String}s of all the available package paths.
getAvailablePackagePaths: ->
packagePaths = []
@@ -243,11 +277,11 @@ class PackageManager
_.uniq(packagePaths)
# Public: Get an array of all the available package names.
# Extended: Get an {Array} of {String}s of all the available package names.
getAvailablePackageNames: ->
_.uniq _.map @getAvailablePackagePaths(), (packagePath) -> path.basename(packagePath)
# Public: Get an array of all the available package metadata.
# Extended: Get an {Array} of {String}s of all the available package metadata.
getAvailablePackageMetadata: ->
packages = []
for packagePath in @getAvailablePackagePaths()

View File

@@ -5,7 +5,7 @@ PropertyAccessors = require 'property-accessors'
Pane = require './pane'
# Public: A container which can contains multiple items to be switched between.
# Extended: A container which can contains multiple items to be switched between.
#
# Items can be almost anything however most commonly they're {EditorView}s.
#

View File

@@ -5,9 +5,43 @@ PaneAxis = require './pane-axis'
Editor = require './editor'
PaneView = null
# Public: A container for multiple items, one of which is *active* at a given
# Extended: A container for multiple items, one of which is *active* at a given
# time. With the default packages, a tab is displayed for each item and the
# active item's view is displayed.
#
# ## Events
# ### activated
#
# Extended: Emit when this pane as been activated
#
# ### item-added
#
# Extended: Emit when an item was added to the pane
#
# * `item` The pane item that has been added
# * `index` {Number} Index in the pane
#
# ### before-item-destroyed
#
# Extended: Emit before the item is destroyed
#
# * `item` The pane item that will be destoryed
#
# ### item-removed
#
# Extended: Emit when the item was removed from the pane
#
# * `item` The pane item that was removed
# * `index` {Number} Index in the pane
# * `destroying` {Boolean} `true` when the item is being removed because of destruction
#
# ### item-moved
#
# Extended: Emit when an item was moved within the pane
#
# * `item` The pane item that was moved
# * `newIndex` {Number} Index that the item was moved to
#
module.exports =
class Pane extends Model
atom.deserializers.add(this)
@@ -130,9 +164,9 @@ class Pane extends Model
# Public: Adds the item to the pane.
#
# item - The item to add. It can be a model with an associated view or a view.
# index - An optional index at which to add the item. If omitted, the item is
# added after the current active item.
# * `item` The item to add. It can be a model with an associated view or a view.
# * `index` (optional) {Number} at which to add the item. If omitted, the item is
# added after the current active item.
#
# Returns the added item
addItem: (item, index=@getActiveItemIndex() + 1) ->
@@ -145,11 +179,11 @@ class Pane extends Model
# Public: Adds the given items to the pane.
#
# items - An {Array} of items to add. Items can be models with associated
# views or views. Any items that are already present in items will
# not be added.
# index - An optional index at which to add the item. If omitted, the item is
# added after the current active item.
# * `items` An {Array} of items to add. Items can be models with associated
# views or views. Any items that are already present in items will
# not be added.
# * `index` (optional) {Number} index at which to add the item. If omitted, the item is
# added after the current active item.
#
# Returns an {Array} of the added items
addItems: (items, index=@getActiveItemIndex() + 1) ->
@@ -246,9 +280,8 @@ class Pane extends Model
# Public: Saves the specified item.
#
# item - The item to save.
# nextAction - An optional function which will be called after the item is
# saved.
# * `item` The item to save.
# * `nextAction` (optional) {Function} which will be called after the item is saved.
saveItem: (item, nextAction) ->
if item?.getUri?()
item.save?()
@@ -258,9 +291,8 @@ class Pane extends Model
# Public: Saves the given item at a prompted-for location.
#
# item - The item to save.
# nextAction - An optional function which will be called after the item is
# saved.
# * `item` The item to save.
# * `nextAction` (optional) {Function} which will be called after the item is saved.
saveItemAs: (item, nextAction) ->
return unless item?.saveAs?
@@ -294,8 +326,8 @@ class Pane extends Model
# Public: Creates a new pane to the left of the receiver.
#
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
# * `params` {Object} with keys
# * `items` (optional) {Array} of items with which to construct the new pane.
#
# Returns the new {Pane}.
splitLeft: (params) ->
@@ -303,8 +335,8 @@ class Pane extends Model
# Public: Creates a new pane to the right of the receiver.
#
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
# * `params` {Object} with keys:
# * `items` (optional) {Array} of items with which to construct the new pane.
#
# Returns the new {Pane}.
splitRight: (params) ->
@@ -312,8 +344,8 @@ class Pane extends Model
# Public: Creates a new pane above the receiver.
#
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
# * `params` {Object} with keys:
# * `items` (optional) {Array} of items with which to construct the new pane.
#
# Returns the new {Pane}.
splitUp: (params) ->
@@ -321,8 +353,8 @@ class Pane extends Model
# Public: Creates a new pane below the receiver.
#
# params - An object with keys:
# :items - An optional array of items with which to construct the new pane.
# * `params` {Object} with keys:
# * `items` (optional) {Array} of items with which to construct the new pane.
#
# Returns the new {Pane}.
splitDown: (params) ->

View File

@@ -15,15 +15,30 @@ Editor = require './editor'
Task = require './task'
Git = require './git'
# Public: Represents a project that's opened in Atom.
# Extended: Represents a project that's opened in Atom.
#
# An instance of this class is always available as the `atom.project` global.
#
# ## Events
#
# ### path-changed
#
# Extended: Emit when the project's path has changed. Use {::getPath} to get the new path
#
# ### buffer-created
#
# Extended: Emit when a buffer is created. For example, when {::open} is called, this is fired.
#
# * `buffer` {TextBuffer} the new buffer that was created.
#
module.exports =
class Project extends Model
atom.deserializers.add(this)
Serializable.includeInto(this)
# Public: Find the local path for the given repository URL.
#
# * `repoUrl` {String} url to a git repository
@pathForRepositoryUrl: (repoUrl) ->
[repoName] = url.parse(repoUrl).path.split('/')[-1..]
repoName = repoName.replace(/\.git$/, '')
@@ -61,11 +76,13 @@ class Project extends Model
# Public: Returns the {Git} repository if available.
getRepo: -> @repo
# Public: Returns the project's fullpath.
# Public: Returns the project's {String} fullpath.
getPath: ->
@rootDirectory?.path
# Public: Sets the project's fullpath.
#
# * `projectPath` {String} path
setPath: (projectPath) ->
@path = projectPath
@rootDirectory?.off()
@@ -90,7 +107,7 @@ class Project extends Model
# the path is already absolute or if it is prefixed with a scheme, it is
# returned unchanged.
#
# uri - The {String} name of the path to convert.
# * `uri` The {String} name of the path to convert.
#
# Returns a {String} or undefined if the uri is not missing or empty.
resolve: (uri) ->
@@ -100,26 +117,30 @@ class Project extends Model
uri
else
if fs.isAbsolute(uri)
fs.absolute(uri)
path.normalize(fs.absolute(uri))
else if projectPath = @getPath()
fs.absolute(path.join(projectPath, uri))
path.normalize(fs.absolute(path.join(projectPath, uri)))
else
undefined
# Public: Make the given path relative to the project directory.
#
# * `fullPath` {String} full path
relativize: (fullPath) ->
return fullPath if fullPath?.match(/[A-Za-z0-9+-.]+:\/\//) # leave path alone if it has a scheme
@rootDirectory?.relativize(fullPath) ? fullPath
# Public: Returns whether the given path is inside this project.
#
# * `pathToCheck` {String} path
contains: (pathToCheck) ->
@rootDirectory?.contains(pathToCheck) ? false
# Given a path to a file, this constructs and associates a new
# {Editor}, showing the file.
#
# filePath - The {String} path of the file to associate with.
# options - Options that you can pass to the {Editor} constructor.
# * `filePath` The {String} path of the file to associate with.
# * `options` Options that you can pass to the {Editor} constructor.
#
# Returns a promise that resolves to an {Editor}.
open: (filePath, options={}) ->
@@ -158,7 +179,7 @@ class Project extends Model
# If the `filePath` already has a `buffer`, that value is used instead. Otherwise,
# `text` is used as the contents of the new buffer.
#
# filePath - A {String} representing a path. If `null`, an "Untitled" buffer is created.
# * `filePath` A {String} representing a path. If `null`, an "Untitled" buffer is created.
#
# Returns a promise that resolves to the {TextBuffer}.
bufferForPath: (filePath) ->
@@ -178,8 +199,8 @@ class Project extends Model
# Given a file path, this sets its {TextBuffer}.
#
# absoluteFilePath - A {String} representing a path.
# text - The {String} text to use as a buffer.
# * `absoluteFilePath` A {String} representing a path.
# * `text` The {String} text to use as a buffer.
#
# Returns a promise that resolves to the {TextBuffer}.
buildBuffer: (absoluteFilePath) ->
@@ -215,10 +236,10 @@ class Project extends Model
# Public: Performs a search across all the files in the project.
#
# regex - A {RegExp} to search with.
# options - An optional options {Object} (default: {}):
# :paths - An {Array} of glob patterns to search within
# iterator - A {Function} callback on each file found
# * `regex` {RegExp} to search with.
# * `options` (optional) {Object} (default: {})
# * `paths` An {Array} of glob patterns to search within
# * `iterator` {Function} callback on each file found
scan: (regex, options={}, iterator) ->
if _.isFunction(options)
iterator = options
@@ -261,11 +282,11 @@ class Project extends Model
# Public: Performs a replace across all the specified files in the project.
#
# regex - A {RegExp} to search with.
# replacementText - Text to replace all matches of regex with
# filePaths - List of file path strings to run the replace on.
# iterator - A {Function} callback on each file with replacements:
# `({filePath, replacements}) ->`.
# * `regex` A {RegExp} to search with.
# * `replacementText` Text to replace all matches of regex with
# * `filePaths` List of file path strings to run the replace on.
# * `iterator` A {Function} callback on each file with replacements:
# * `options` {Object} with keys `filePath` and `replacements`
replace: (regex, replacementText, filePaths, iterator) ->
deferred = Q.defer()

View File

@@ -1,244 +0,0 @@
{View, $} = require 'space-pen'
React = require 'react-atom-fork'
{defaults} = require 'underscore-plus'
TextBuffer = require 'text-buffer'
Editor = require './editor'
EditorComponent = require './editor-component'
module.exports =
class ReactEditorView extends View
@content: (params) ->
attributes = params.attributes ? {}
attributes.class = 'editor react editor-colors'
attributes.tabIndex = -1
@div attributes
focusOnAttach: false
constructor: (editorOrParams, props) ->
super
if editorOrParams instanceof Editor
@editor = editorOrParams
else
{@editor, mini, placeholderText} = editorOrParams
props ?= {}
props.mini = mini
props.placeholderText = placeholderText
@editor ?= new Editor
buffer: new TextBuffer
softWrap: false
tabLength: 2
softTabs: true
mini: mini
props = defaults({@editor, parentView: this}, props)
@component = React.renderComponent(EditorComponent(props), @element)
node = @component.getDOMNode()
@scrollView = $(node).find('.scroll-view')
@underlayer = $(node).find('.highlights').addClass('underlayer')
@overlayer = $(node).find('.lines').addClass('overlayer')
@hiddenInput = $(node).find('.hidden-input')
# FIXME: there should be a better way to deal with the gutter element
@subscribe atom.config.observe 'editor.showLineNumbers', =>
@gutter = $(node).find('.gutter')
@gutter.removeClassFromAllLines = (klass) =>
@gutter.find('.line-number').removeClass(klass)
@gutter.getLineNumberElement = (bufferRow) =>
@gutter.find("[data-buffer-row='#{bufferRow}']")
@gutter.addClassToLine = (bufferRow, klass) =>
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
lines.addClass(klass)
lines.length > 0
@on 'focus', =>
if @component?
@component.onFocus()
else
@focusOnAttach = true
getEditor: -> @editor
getModel: -> @editor
Object.defineProperty @::, 'lineHeight', get: -> @editor.getLineHeightInPixels()
Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth()
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
Object.defineProperty @::, 'active', get: -> @is(@getPane()?.activeView)
Object.defineProperty @::, 'isFocused', get: -> @component?.state.focused
Object.defineProperty @::, 'mini', get: -> @component?.props.mini
afterAttach: (onDom) ->
return unless onDom
return if @attached
@attached = true
@component.pollDOM()
@focus() if @focusOnAttach
@trigger 'editor:attached', [this]
scrollTop: (scrollTop) ->
if scrollTop?
@editor.setScrollTop(scrollTop)
else
@editor.getScrollTop()
scrollLeft: (scrollLeft) ->
if scrollLeft?
@editor.setScrollLeft(scrollLeft)
else
@editor.getScrollLeft()
scrollToBottom: ->
@editor.setScrollBottom(Infinity)
scrollToScreenPosition: (screenPosition, options) ->
@editor.scrollToScreenPosition(screenPosition, options)
scrollToBufferPosition: (bufferPosition, options) ->
@editor.scrollToBufferPosition(bufferPosition, options)
scrollToCursorPosition: ->
@editor.scrollToCursorPosition()
scrollToPixelPosition: (pixelPosition) ->
screenPosition = screenPositionForPixelPosition(pixelPosition)
@editor.scrollToScreenPosition(screenPosition)
pixelPositionForBufferPosition: (bufferPosition) ->
@editor.pixelPositionForBufferPosition(bufferPosition)
pixelPositionForScreenPosition: (screenPosition) ->
@editor.pixelPositionForScreenPosition(screenPosition)
appendToLinesView: (view) ->
view.css('position', 'absolute')
view.css('z-index', 1)
@find('.lines').prepend(view)
beforeRemove: ->
return unless @attached
@attached = false
React.unmountComponentAtNode(@element) if @component.isMounted()
@trigger 'editor:detached', this
# Public: Split the editor view left.
splitLeft: ->
pane = @getPane()
pane?.splitLeft(pane?.copyActiveItem()).activeView
# Public: Split the editor view right.
splitRight: ->
pane = @getPane()
pane?.splitRight(pane?.copyActiveItem()).activeView
# Public: Split the editor view up.
splitUp: ->
pane = @getPane()
pane?.splitUp(pane?.copyActiveItem()).activeView
# Public: Split the editor view down.
splitDown: ->
pane = @getPane()
pane?.splitDown(pane?.copyActiveItem()).activeView
getPane: ->
@parent('.item-views').parents('.pane').view()
hide: ->
super
@pollComponentDOM()
show: ->
super
@pollComponentDOM()
pollComponentDOM: ->
return unless @component?
valueToRestore = @component.performSyncUpdates
@component.performSyncUpdates = true
@component.pollDOM()
@component.performSyncUpdates = valueToRestore
pageDown: ->
@editor.pageDown()
pageUp: ->
@editor.pageUp()
getFirstVisibleScreenRow: ->
@editor.getVisibleRowRange()[0]
getLastVisibleScreenRow: ->
@editor.getVisibleRowRange()[1]
getFontFamily: ->
@component?.getFontFamily()
setFontFamily: (fontFamily) ->
@component?.setFontFamily(fontFamily)
getFontSize: ->
@component?.getFontSize()
setFontSize: (fontSize) ->
@component?.setFontSize(fontSize)
setWidthInChars: (widthInChars) ->
@component.getDOMNode().style.width = (@editor.getDefaultCharWidth() * widthInChars) + 'px'
setLineHeight: (lineHeight) ->
@component.setLineHeight(lineHeight)
setShowIndentGuide: (showIndentGuide) ->
@component.setShowIndentGuide(showIndentGuide)
setSoftWrap: (softWrap) ->
@editor.setSoftWrap(softWrap)
setShowInvisibles: (showInvisibles) ->
@component.setShowInvisibles(showInvisibles)
toggleSoftWrap: ->
@editor.toggleSoftWrap()
toggleSoftTabs: ->
@editor.toggleSoftTabs()
getText: ->
@editor.getText()
setText: (text) ->
@editor.setText(text)
insertText: (text) ->
@editor.insertText(text)
isInputEnabled: ->
@component.isInputEnabled()
setInputEnabled: (inputEnabled) ->
@component.setInputEnabled(inputEnabled)
requestDisplayUpdate: -> # No-op shim for find-and-replace
updateDisplay: -> # No-op shim for package specs
resetDisplay: -> # No-op shim for package specs
redraw: -> # No-op shim
setPlaceholderText: (placeholderText) ->
if @component?
@component.setProps({placeholderText})
else
@props.placeholderText = placeholderText
lineElementForScreenRow: (screenRow) ->
$(@component.lineNodeForScreenRow(screenRow))

View File

@@ -2,22 +2,31 @@
# Public: Represents a view that scrolls.
#
# Subclasses must call `super` if overriding the `initialize` method or else
# the following events won't be handled by the ScrollView.
# Handles several core events to update scroll position:
#
# ## Events
# * `core:move-up`
# * `core:move-down`
# * `core:page-up`
# * `core:page-down`
# * `core:move-to-top`
# * `core:move-to-bottom`
# * `core:move-up` Scrolls the view up
# * `core:move-down` Scrolls the view down
# * `core:page-up` Scrolls the view up by the height of the page
# * `core:page-down` Scrolls the view down by the height of the page
# * `core:move-to-top` Scrolls the editor to the top
# * `core:move-to-bottom` Scroll the editor to the bottom
#
# ## Requiring in packages
# Subclasses must call `super` if overriding the `initialize` method.
#
# ## Examples
#
# ```coffee
# {ScrollView} = require 'atom'
# {ScrollView} = require 'atom'
#
# class MyView extends ScrollView
# @content: ->
# @div()
#
# initialize: ->
# super
# @text('super long content that will scroll')
# ```
#
module.exports =
class ScrollView extends View
initialize: ->

View File

@@ -1,11 +1,8 @@
{$, View} = require './space-pen-extensions'
if atom.config.get('core.useReactMiniEditors')
EditorView = require './react-editor-view'
else
EditorView = require './editor-view'
EditorView = require './editor-view'
fuzzyFilter = require('fuzzaldrin').filter
# Public: Provides a view that renders a list of items with an editor that
# Essential: Provides a view that renders a list of items with an editor that
# filters the items. Used by many packages such as the fuzzy-finder,
# command-palette, symbols-view and autocomplete.
#
@@ -99,14 +96,14 @@ class SelectListView extends View
# This should be model items not actual views. {::viewForItem} will be
# called to render the item when it is being appended to the list view.
#
# items - The {Array} of model items to display in the list (default: []).
# * `items` The {Array} of model items to display in the list (default: []).
setItems: (@items=[]) ->
@populateList()
@setLoading()
# Public: Set the error message to display.
#
# message - The {String} error message (default: '').
# * `message` The {String} error message (default: '').
setError: (message='') ->
if message.length is 0
@error.text('').hide()
@@ -116,7 +113,7 @@ class SelectListView extends View
# Public: Set the loading message to display.
#
# message - The {String} loading message (default: '').
# * `message` The {String} loading message (default: '').
setLoading: (message='') ->
if message.length is 0
@loading.text("")
@@ -168,15 +165,15 @@ class SelectListView extends View
#
# Subclasses may override this method to customize the message.
#
# itemCount - The {Number} of items in the array specified to {::setItems}
# filteredItemCount - The {Number} of items that pass the fuzzy filter test.
# * `itemCount` The {Number} of items in the array specified to {::setItems}
# * `filteredItemCount` The {Number} of items that pass the fuzzy filter test.
#
# Returns a {String} message (default: 'No matches found').
getEmptyMessage: (itemCount, filteredItemCount) -> 'No matches found'
# Public: Set the maximum numbers of items to display in the list.
#
# maxItems - The maximum {Number} of items to display.
# * `maxItems` The maximum {Number} of items to display.
setMaxItems: (@maxItems) ->
selectPreviousItemView: ->
@@ -227,8 +224,8 @@ class SelectListView extends View
#
# This is called when the item is about to appended to the list view.
#
# item - The model item being rendered. This will always be one of the items
# previously passed to {::setItems}.
# * `item` The model item being rendered. This will always be one of the items
# previously passed to {::setItems}.
#
# Returns a String of HTML, DOM element, jQuery object, or View.
viewForItem: (item) ->
@@ -238,8 +235,8 @@ class SelectListView extends View
#
# This method must be overridden by subclasses.
#
# item - The selected model item. This will always be one of the items
# previously passed to {::setItems}.
# * `item` The selected model item. This will always be one of the items
# previously passed to {::setItems}.
#
# Returns a DOM element, jQuery object, or {View}.
confirmed: (item) ->
@@ -275,7 +272,6 @@ class SelectListView extends View
cancelled: ->
@filterEditorView.getEditor().setText('')
@filterEditorView.updateDisplay()
# Public: Cancel and close this select list view.
#

View File

@@ -1,85 +0,0 @@
{Point, Range} = require 'text-buffer'
{View, $$} = require './space-pen-extensions'
module.exports =
class SelectionView extends View
@content: ->
@div class: 'selection'
regions: null
needsRemoval: false
initialize: ({@editorView, @selection} = {}) ->
@regions = []
@selection.on 'screen-range-changed', => @editorView.requestDisplayUpdate()
@selection.on 'destroyed', =>
@needsRemoval = true
@editorView.requestDisplayUpdate()
updateDisplay: ->
@clearRegions()
range = @getScreenRange()
@trigger 'selection:changed'
@editorView.highlightFoldsContainingBufferRange(@getBufferRange())
return if range.isEmpty()
rowSpan = range.end.row - range.start.row
if rowSpan == 0
@appendRegion(1, range.start, range.end)
else
@appendRegion(1, range.start, null)
if rowSpan > 1
@appendRegion(rowSpan - 1, { row: range.start.row + 1, column: 0}, null)
@appendRegion(1, { row: range.end.row, column: 0 }, range.end)
appendRegion: (rows, start, end) ->
{ lineHeight, charWidth } = @editorView
css = @editorView.pixelPositionForScreenPosition(start)
css.height = lineHeight * rows
if end
css.width = @editorView.pixelPositionForScreenPosition(end).left - css.left
else
css.right = 0
region = ($$ -> @div class: 'region').css(css)
@append(region)
@regions.push(region)
getCenterPixelPosition: ->
{ start, end } = @getScreenRange()
startRow = start.row
endRow = end.row
endRow-- if end.column == 0
@editorView.pixelPositionForScreenPosition([((startRow + endRow + 1) / 2), start.column])
clearRegions: ->
region.remove() for region in @regions
@regions = []
getScreenRange: ->
@selection.getScreenRange()
getBufferRange: ->
@selection.getBufferRange()
needsAutoscroll: ->
@selection.needsAutoscroll
clearAutoscroll: ->
@selection.clearAutoscroll()
highlight: ->
@unhighlight()
@addClass('highlighted')
clearTimeout(@unhighlightTimeout)
@unhighlightTimeout = setTimeout((=> @unhighlight()), 1000)
unhighlight: ->
@removeClass('highlighted')
remove: ->
@editorView.removeSelectionView(this)
super

View File

@@ -2,7 +2,20 @@
{Model} = require 'theorist'
{pick} = require 'underscore-plus'
# Public: Represents a selection in the {Editor}.
# Extended: Represents a selection in the {Editor}.
#
# ## Events
#
# ### screen-range-changed
#
# Extended: Emit when the selection was moved.
#
# * `screenRange` {Range} indicating the new screenrange
#
# ### destroyed
#
# Extended: Emit when the selection was destroyed
#
module.exports =
class Selection extends Model
cursor: null
@@ -56,8 +69,8 @@ class Selection extends Model
# Public: Modifies the screen range for the selection.
#
# screenRange - The new {Range} to use.
# options - A hash of options matching those found in {::setBufferRange}.
# * `screenRange` The new {Range} to use.
# * `options` (optional) {Object} options matching those found in {::setBufferRange}.
setScreenRange: (screenRange, options) ->
@setBufferRange(@editor.bufferRangeForScreenRange(screenRange), options)
@@ -67,11 +80,10 @@ class Selection extends Model
# Public: Modifies the buffer {Range} for the selection.
#
# screenRange - The new {Range} to select.
# options - An {Object} with the keys:
# :preserveFolds - if `true`, the fold settings are preserved after the
# selection moves.
# :autoscroll - if `true`, the {Editor} scrolls to the new selection.
# * `screenRange` The new {Range} to select.
# * `options` (optional) {Object} with the keys:
# * `preserveFolds` if `true`, the fold settings are preserved after the selection moves.
# * `autoscroll` if `true`, the {Editor} scrolls to the new selection.
setBufferRange: (bufferRange, options={}) ->
bufferRange = Range.fromObject(bufferRange)
@needsAutoscroll = options.autoscroll
@@ -141,7 +153,7 @@ class Selection extends Model
# Public: Selects an entire line in the buffer.
#
# row - The line {Number} to select (default: the row of the cursor).
# * `row` The line {Number} to select (default: the row of the cursor).
selectLine: (row=@cursor.getBufferPosition().row) ->
range = @editor.bufferRangeForBufferRow(row, includeNewline: true)
@setBufferRange(@getBufferRange().union(range))
@@ -160,7 +172,7 @@ class Selection extends Model
# Public: Selects the text from the current cursor position to a given screen
# position.
#
# position - An instance of {Point}, with a given `row` and `column`.
# * `position` An instance of {Point}, with a given `row` and `column`.
selectToScreenPosition: (position) ->
position = Point.fromObject(position)
@@ -181,7 +193,7 @@ class Selection extends Model
# Public: Selects the text from the current cursor position to a given buffer
# position.
#
# position - An instance of {Point}, with a given `row` and `column`.
# * `position` An instance of {Point}, with a given `row` and `column`.
selectToBufferPosition: (position) ->
@modifySelection => @cursor.setBufferPosition(position)
@@ -306,14 +318,14 @@ class Selection extends Model
# Public: Replaces text at the current selection.
#
# text - A {String} representing the text to add
# options - An {Object} with keys:
# :select - if `true`, selects the newly added text.
# :autoIndent - if `true`, indents all inserted text appropriately.
# :autoIndentNewline - if `true`, indent newline appropriately.
# :autoDecreaseIndent - if `true`, decreases indent level appropriately
# (for example, when a closing bracket is inserted).
# :undo - if `skip`, skips the undo stack for this operation.
# * `text` A {String} representing the text to add
# * `options` (optional) {Object} with keys:
# * `select` if `true`, selects the newly added text.
# * `autoIndent` if `true`, indents all inserted text appropriately.
# * `autoIndentNewline` if `true`, indent newline appropriately.
# * `autoDecreaseIndent` if `true`, decreases indent level appropriately
# (for example, when a closing bracket is inserted).
# * `undo` if `skip`, skips the undo stack for this operation.
insertText: (text, options={}) ->
oldBufferRange = @getBufferRange()
@editor.unfoldBufferRow(oldBufferRange.end.row)
@@ -345,8 +357,8 @@ class Selection extends Model
# Public: Indents the given text to the suggested level based on the grammar.
#
# text - The {String} to indent within the selection.
# indentBasis - The beginning indent level.
# * `text` The {String} to indent within the selection.
# * `indentBasis` The beginning indent level.
normalizeIndents: (text, indentBasis) ->
textPrecedingCursor = @cursor.getCurrentBufferLine()[0...@cursor.getBufferColumn()]
isCursorInsideExistingLine = /\S/.test(textPrecedingCursor)
@@ -378,9 +390,9 @@ class Selection extends Model
# non-whitespace characters, and otherwise inserts a tab. If the selection is
# non empty, calls {::indentSelectedRows}.
#
# options - A {Object} with the keys:
# :autoIndent - If `true`, the line is indented to an automatically-inferred
# level. Otherwise, {Editor::getTabText} is inserted.
# * `options` (optional) {Object} with the keys:
# * `autoIndent` If `true`, the line is indented to an automatically-inferred
# level. Otherwise, {Editor::getTabText} is inserted.
indent: ({ autoIndent }={}) ->
{ row, column } = @cursor.getBufferPosition()
@@ -549,16 +561,18 @@ class Selection extends Model
@cut(maintainClipboard)
# Public: Copies the selection to the clipboard and then deletes it.
#
# * `maintainClipboard` {Boolean} (default: false) See {::copy}
cut: (maintainClipboard=false) ->
@copy(maintainClipboard)
@delete()
# Public: Copies the current selection to the clipboard.
#
# If the `maintainClipboard` is set to `true`, a specific metadata property
# is created to store each content copied to the clipboard. The clipboard
# `text` still contains the concatenation of the clipboard with the
# current selection.
# * `maintainClipboard` {Boolean} if `true`, a specific metadata property
# is created to store each content copied to the clipboard. The clipboard
# `text` still contains the concatenation of the clipboard with the
# current selection. (default: false)
copy: (maintainClipboard=false) ->
return if @isEmpty()
text = @editor.buffer.getTextInRange(@getBufferRange())
@@ -598,9 +612,9 @@ class Selection extends Model
# Public: Identifies if a selection intersects with a given buffer range.
#
# bufferRange - A {Range} to check against.
# * `bufferRange` A {Range} to check against.
#
# Returns a Boolean.
# Returns a {Boolean}
intersectsBufferRange: (bufferRange) ->
@getBufferRange().intersectsWith(bufferRange)
@@ -612,17 +626,17 @@ class Selection extends Model
# Public: Identifies if a selection intersects with another selection.
#
# otherSelection - A {Selection} to check against.
# * `otherSelection` A {Selection} to check against.
#
# Returns a Boolean.
# Returns a {Boolean}
intersectsWith: (otherSelection) ->
@getBufferRange().intersectsWith(otherSelection.getBufferRange())
# Public: Combines the given selection into this selection and then destroys
# the given selection.
#
# otherSelection - A {Selection} to merge with.
# options - A hash of options matching those found in {::setBufferRange}.
# * `otherSelection` A {Selection} to merge with.
# * `options` (optional) {Object} options matching those found in {::setBufferRange}.
merge: (otherSelection, options) ->
myGoalBufferRange = @getGoalBufferRange()
otherGoalBufferRange = otherSelection.getGoalBufferRange()
@@ -638,7 +652,7 @@ class Selection extends Model
#
# See {Range::compare} for more details.
#
# otherSelection - A {Selection} to compare against.
# * `otherSelection` A {Selection} to compare against
compare: (otherSelection) ->
@getBufferRange().compare(otherSelection.getBufferRange())

View File

@@ -14,7 +14,7 @@ Token = require './token'
# An instance of this class is always available as the `atom.syntax` global.
#
# The Syntax class also contains properties for things such as the
# language-specific comment regexes.
# language-specific comment regexes. See {::getProperty} for more details.
module.exports =
class Syntax extends GrammarRegistry
PropertyAccessors.includeInto(this)
@@ -55,14 +55,15 @@ class Syntax extends GrammarRegistry
# Public: Get a property for the given scope and key path.
#
# ## Example
# ## Examples
#
# ```coffee
# comment = atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')
# console.log(comment) # '# '
# ```
#
# scope - An {Array} of {String} scopes.
# keyPath - A {String} key path.
# * `scope` An {Array} of {String} scopes.
# * `keyPath` A {String} key path.
#
# Returns a {String} property value or undefined.
getProperty: (scope, keyPath) ->

View File

@@ -6,27 +6,41 @@ child_process = require 'child_process'
#
# Used by the fuzzy-finder.
#
# ## Events
#
# * task:log - Emitted when console.log is called within the task.
# * task:warn - Emitted when console.warn is called within the task.
# * task:error - Emitted when console.error is called within the task.
# * task:completed - Emitted when the task has succeeded or failed.
#
# ## Requiring in packages
# ## Examples
#
# ```coffee
# {Task} = require 'atom'
# {Task} = require 'atom'
# ```
#
# ## Events
#
# ### task:log
#
# Emitted when console.log is called within the task.
#
# ### task:warn
#
# Emitted when console.warn is called within the task.
#
# ### task:error
#
# Emitted when console.error is called within the task.
#
# ### task:completed
#
# Emitted when the task has succeeded or failed.
#
module.exports =
class Task
Emitter.includeInto(this)
# Public: A helper method to easily launch and run a task once.
#
# taskPath - The {String} path to the CoffeeScript/JavaScript file which
# exports a single {Function} to execute.
# args - The arguments to pass to the exported function.
# * `taskPath` The {String} path to the CoffeeScript/JavaScript file which
# exports a single {Function} to execute.
# * `args` The arguments to pass to the exported function.
#
# Returns the created {Task}.
@once: (taskPath, args...) ->
task = new Task(taskPath)
task.once 'task:completed', -> task.terminate()
@@ -43,8 +57,8 @@ class Task
# Public: Creates a task.
#
# taskPath - The {String} path to the CoffeeScript/JavaScript file that
# exports a single {Function} to execute.
# * `taskPath` The {String} path to the CoffeeScript/JavaScript file that
# exports a single {Function} to execute.
constructor: (taskPath) ->
coffeeCacheRequire = "require('#{require.resolve('./coffee-cache')}').register();"
coffeeScriptRequire = "require('#{require.resolve('coffee-script')}').register();"
@@ -81,10 +95,10 @@ class Task
# Throws an error if this task has already been terminated or if sending a
# message to the child process fails.
#
# args - The arguments to pass to the function exported by this task's script.
# callback - An optional {Function} to call when the task completes.
# * `args` The arguments to pass to the function exported by this task's script.
# * `callback` (optional) A {Function} to call when the task completes.
start: (args..., callback) ->
throw new Error("Cannot start terminated process") unless @childProcess?
throw new Error('Cannot start terminated process') unless @childProcess?
@handleEvents()
if _.isFunction(callback)
@@ -92,20 +106,24 @@ class Task
else
args.push(callback)
@send({event: 'start', args})
undefined
# Public: Send message to the task.
#
# Throws an error if this task has already been terminated or if sending a
# message to the child process fails.
#
# message - The message to send to the task.
# * `message` The message to send to the task.
send: (message) ->
throw new Error("Cannot send message to terminated process") unless @childProcess?
@childProcess.send(message)
if @childProcess?
@childProcess.send(message)
else
throw new Error('Cannot send message to terminated process')
undefined
# Public: Forcefully stop the running task.
#
# No events are emitted.
# No more events are emitted once this method is called.
terminate: ->
return unless @childProcess?
@@ -114,3 +132,4 @@ class Task
@childProcess = null
@off()
undefined

View File

@@ -9,9 +9,32 @@ Q = require 'q'
Package = require './package'
{File} = require 'pathwatcher'
# Public: Handles loading and activating available themes.
# Extended: Handles loading and activating available themes.
#
# An instance of this class is always available as the `atom.themes` global.
#
# ## Events
#
# ### reloaded
#
# Extended: Emit when all styles have been reloaded.
#
# ### stylesheet-added
#
# Extended: Emit when a stylesheet has been added.
#
# * `stylesheet` {StyleSheet} object that was removed
#
# ### stylesheet-removed
#
# Extended: Emit when a stylesheet has been removed.
#
# * `stylesheet` {StyleSheet} object that was removed
#
# ### stylesheets-changed
#
# Extended: Emit anytime any style sheet is added or removed from the editor
#
module.exports =
class ThemeManager
Emitter.includeInto(this)
@@ -98,7 +121,7 @@ class ThemeManager
@refreshLessCache() # Update cache again now that @getActiveThemes() is populated
@loadUserStylesheet()
@reloadBaseStylesheets()
@emit('reloaded')
@emit 'reloaded'
deferred.resolve()
deferred.promise
@@ -124,7 +147,7 @@ class ThemeManager
# Public: Set the list of enabled themes.
#
# enabledThemeNames - An {Array} of {String} theme names.
# * `enabledThemeNames` An {Array} of {String} theme names.
setEnabledThemes: (enabledThemeNames) ->
atom.config.set('core.themes', enabledThemeNames)
@@ -187,9 +210,8 @@ class ThemeManager
#
# This supports both CSS and LESS stylsheets.
#
# stylesheetPath - A {String} path to the stylesheet that can be an absolute
# path or a relative path that will be resolved against the
# load path.
# * `stylesheetPath` A {String} path to the stylesheet that can be an absolute
# path or a relative path that will be resolved against the load path.
#
# Returns the absolute path to the required stylesheet.
requireStylesheet: (stylesheetPath, type = 'bundled', htmlElement) ->

View File

@@ -28,7 +28,9 @@ class WindowEventHandler
@subscribe $(window), 'blur', -> document.body.classList.add('is-blurred')
@subscribe $(window), 'window:open-path', (event, {pathToOpen, initialLine, initialColumn}) ->
unless fs.isDirectorySync(pathToOpen)
if fs.isDirectorySync(pathToOpen)
atom.project.setPath(pathToOpen) unless atom.project.getPath()
else
atom.workspace?.open(pathToOpen, {initialLine, initialColumn})
@subscribe $(window), 'beforeunload', =>

View File

@@ -9,14 +9,13 @@ scrollbarStyle = require 'scrollbar-style'
fs = require 'fs-plus'
Workspace = require './workspace'
CommandInstaller = require './command-installer'
EditorView = require './editor-view'
PaneView = require './pane-view'
PaneColumnView = require './pane-column-view'
PaneRowView = require './pane-row-view'
PaneContainerView = require './pane-container-view'
Editor = require './editor'
# Public: The top-level view for the entire window. An instance of this class is
# Essential: The top-level view for the entire window. An instance of this class is
# available via the `atom.workspaceView` global.
#
# It is backed by a model object, an instance of {Workspace}, which is available
@@ -45,7 +44,7 @@ Editor = require './editor'
# the built-in `atom` module.
#
# ```coffee
# {WorkspaceView} = require 'atom'
# {WorkspaceView} = require 'atom'
# ```
#
# You can assign it to the `atom.workspaceView` global in the spec or just use
@@ -71,8 +70,6 @@ class WorkspaceView extends View
projectHome: path.join(fs.getHomeDirectory(), 'github')
audioBeep: true
destroyEmptyPanes: true
useReactEditor: true
useReactMiniEditors: true
@content: ->
@div class: 'workspace', tabindex: -1, =>
@@ -247,40 +244,56 @@ class WorkspaceView extends View
# Public: Prepend an element or view to the panels at the top of the
# workspace.
#
# * `element` jQuery object or DOM element
prependToTop: (element) ->
@vertical.prepend(element)
# Public: Append an element or view to the panels at the top of the workspace.
#
# * `element` jQuery object or DOM element
appendToTop: (element) ->
@panes.before(element)
# Public: Prepend an element or view to the panels at the bottom of the
# workspace.
#
# * `element` jQuery object or DOM element
prependToBottom: (element) ->
@panes.after(element)
# Public: Append an element or view to the panels at the bottom of the
# workspace.
#
# * `element` jQuery object or DOM element
appendToBottom: (element) ->
@vertical.append(element)
# Public: Prepend an element or view to the panels at the left of the
# workspace.
#
# * `element` jQuery object or DOM element
prependToLeft: (element) ->
@horizontal.prepend(element)
# Public: Append an element or view to the panels at the left of the
# workspace.
#
# * `element` jQuery object or DOM element
appendToLeft: (element) ->
@vertical.before(element)
# Public: Prepend an element or view to the panels at the right of the
# workspace.
#
# * `element` jQuery object or DOM element
prependToRight: (element) ->
@vertical.after(element)
# Public: Append an element or view to the panels at the right of the
# workspace.
#
# * `element` jQuery object or DOM element
appendToRight: (element) ->
@horizontal.append(element)
@@ -320,7 +333,8 @@ class WorkspaceView extends View
# Public: Register a function to be called for every current and future
# pane view in the workspace.
#
# callback - A {Function} with a {PaneView} as its only argument.
# * `callback` A {Function} with a {PaneView} as its only argument.
# * `paneView` {PaneView}
#
# Returns a subscription object with an `.off` method that you can call to
# unregister the callback.
@@ -341,7 +355,8 @@ class WorkspaceView extends View
# editor view in the workspace (only includes {EditorView}s that are pane
# items).
#
# callback - A {Function} with an {EditorView} as its only argument.
# * `callback` A {Function} with an {EditorView} as its only argument.
# * `editorView` {EditorView}
#
# Returns a subscription object with an `.off` method that you can call to
# unregister the callback.

View File

@@ -15,6 +15,20 @@ Pane = require './pane'
# Interact with this object to open files, be notified of current and future
# editors, and manipulate panes. To add panels, you'll need to use the
# {WorkspaceView} class for now until we establish APIs at the model layer.
#
# ## Events
#
# ### uri-opened
#
# Extended: Emit when something has been opened. This can be anything, from an
# editor to the settings view. You can get the new item via {::getActivePaneItem}
#
# ### editor-created
#
# Extended: Emit when an editor is created (a file opened).
#
# * `editor` {Editor} the new editor
#
module.exports =
class Workspace extends Model
atom.deserializers.add(this)
@@ -78,7 +92,7 @@ class Workspace extends Model
# Public: Register a function to be called for every current and future
# {Editor} in the workspace.
#
# callback - A {Function} with an {Editor} as its only argument.
# * `callback` A {Function} with an {Editor} as its only argument.
#
# Returns a subscription object with an `.off` method that you can call to
# unregister the callback.
@@ -98,22 +112,21 @@ class Workspace extends Model
# Public: Open a given a URI in Atom asynchronously.
#
# uri - A {String} containing a URI.
# options - An optional options {Object}
# :initialLine - A {Number} indicating which row to move the cursor to
# initially. Defaults to `0`.
# :initialColumn - A {Number} indicating which column to move the cursor to
# initially. Defaults to `0`.
# :split - Either 'left' or 'right'. If 'left', the item will be opened in
# leftmost pane of the current active pane's row. If 'right', the
# item will be opened in the rightmost pane of the current active
# pane's row.
# :activatePane - A {Boolean} indicating whether to call {Pane::activate} on
# the containing pane. Defaults to `true`.
# :searchAllPanes - A {Boolean}. If `true`, the workspace will attempt to
# activate an existing item for the given URI on any pane.
# If `false`, only the active pane will be searched for
# an existing item for the same URI. Defaults to `false`.
# * `uri` A {String} containing a URI.
# * `options` (optional) {Object}
# * `initialLine` A {Number} indicating which row to move the cursor to
# initially. Defaults to `0`.
# * `initialColumn` A {Number} indicating which column to move the cursor to
# initially. Defaults to `0`.
# * `split` Either 'left' or 'right'. If 'left', the item will be opened in
# leftmost pane of the current active pane's row. If 'right', the
# item will be opened in the rightmost pane of the current active pane's row.
# * `activatePane` A {Boolean} indicating whether to call {Pane::activate} on
# containing pane. Defaults to `true`.
# * `searchAllPanes` A {Boolean}. If `true`, the workspace will attempt to
# activate an existing item for the given URI on any pane.
# If `false`, only the active pane will be searched for
# an existing item for the same URI. Defaults to `false`.
#
# Returns a promise that resolves to the {Editor} for the file URI.
open: (uri, options={}) ->
@@ -132,7 +145,7 @@ class Workspace extends Model
@openUriInPane(uri, pane, options)
# Public: Open Atom's license in the active pane.
# Open Atom's license in the active pane.
openLicense: ->
@open(join(atom.getLoadSettings().resourcePath, 'LICENSE.md'))
@@ -140,14 +153,14 @@ class Workspace extends Model
# in specs. Calling this in production code will block the UI thread and
# everyone will be mad at you.**
#
# uri - A {String} containing a URI.
# options - An optional options {Object}
# :initialLine - A {Number} indicating which row to move the cursor to
# initially. Defaults to `0`.
# :initialColumn - A {Number} indicating which column to move the cursor to
# initially. Defaults to `0`.
# :activatePane - A {Boolean} indicating whether to call {Pane::activate} on
# the containing pane. Defaults to `true`.
# * `uri` A {String} containing a URI.
# * `options` An optional options {Object}
# * `initialLine` A {Number} indicating which row to move the cursor to
# initially. Defaults to `0`.
# * `initialColumn` A {Number} indicating which column to move the cursor to
# initially. Defaults to `0`.
# * `activatePane` A {Boolean} indicating whether to call {Pane::activate} on
# the containing pane. Defaults to `true`.
openSync: (uri='', options={}) ->
deprecate("Don't use the `changeFocus` option") if options.changeFocus?
@@ -207,14 +220,15 @@ class Workspace extends Model
#
# An {Editor} will be used if no openers return a value.
#
# ## Example
# ```coffeescript
# atom.project.registerOpener (uri) ->
# if path.extname(uri) is '.toml'
# return new TomlEditor(uri)
# ## Examples
#
# ```coffee
# atom.project.registerOpener (uri) ->
# if path.extname(uri) is '.toml'
# return new TomlEditor(uri)
# ```
#
# opener - A {Function} to be called when a path is being opened.
# * `opener` A {Function} to be called when a path is being opened.
registerOpener: (opener) ->
@openers.push(opener)
@@ -251,6 +265,8 @@ class Workspace extends Model
# Public: Get the first pane {Pane} with an item for the given URI.
#
# * `uri` {String} uri
#
# Returns a {Pane} or `undefined` if no pane exists for the given URI.
paneForUri: (uri) ->
@paneContainer.paneForUri(uri)