mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
merge latest upstream master
This commit is contained in:
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.91.0"
|
||||
"atom-package-manager": "0.92.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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' }
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
42
package.json
42
package.json
@@ -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": {
|
||||
|
||||
@@ -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 %>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
129
src/atom.coffee
129
src/atom.coffee
@@ -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
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.')
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 = ''
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(/\./, '-')}")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
{View} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
module.exports =
|
||||
class CursorView extends View
|
||||
@content: ->
|
||||
@div class: 'cursor idle', => @raw ' '
|
||||
|
||||
@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'
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
2342
src/editor.coffee
2342
src/editor.coffee
File diff suppressed because it is too large
Load Diff
118
src/git.coffee
118
src/git.coffee
@@ -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) ->
|
||||
|
||||
@@ -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(' ', 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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))
|
||||
@@ -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: ->
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
@@ -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
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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', =>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user