Comparar commits
296 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| dfc0df42b2 | |||
| 452c78b735 | |||
| 42f67033cc | |||
| 5c417cf9cc | |||
| 81143af4b4 | |||
| 5c048a4f13 | |||
| 2ac9cc9ed1 | |||
| e8f8ead53f | |||
| f3d8c5d7ec | |||
| 7d179ef40f | |||
| 375b8efc22 | |||
| 086a011a63 | |||
| 46ff52b13a | |||
| 0ed4d07c33 | |||
| efe3d65291 | |||
| 8576486d06 | |||
| e5abca138d | |||
| fa24154e24 | |||
| 45c05d3b7d | |||
| bf4ac1d3eb | |||
| 892cdae622 | |||
| f33d7ba27f | |||
| 06436ef530 | |||
| 815244bba0 | |||
| 0303ebb5e5 | |||
| eab791d47e | |||
| 6ad96d98b8 | |||
| ed4ebefdc1 | |||
| a1828fa3a6 | |||
| 345c0f670c | |||
| d78e6fa711 | |||
| c3a6475308 | |||
| 2cc9f6bee8 | |||
| 0c6fba2ce2 | |||
| fa72db87a2 | |||
| a0d7bbdb92 | |||
| 4c3e1ef82b | |||
| 1056418f97 | |||
| 37040ad485 | |||
| dee9cccae7 | |||
| 50e27854cc | |||
| c931071c91 | |||
| 498c8d21b2 | |||
| 263b792eee | |||
| ffd3990a6b | |||
| 31490ca31e | |||
| 37159bb478 | |||
| 16ad957609 | |||
| 137eeab4cf | |||
| ce2076f30a | |||
| 8479d564f5 | |||
| ec6004615a | |||
| 7e7441dc23 | |||
| 2a09d25eb1 | |||
| d5508b338b | |||
| 86bd43bf73 | |||
| 3075b74b64 | |||
| 11d7437ecc | |||
| df439f5ada | |||
| dc28ddbdc8 | |||
| 49e98d368a | |||
| 02e91aa4cd | |||
| 14c8e9cedc | |||
| 4867e3ef14 | |||
| 3a70b98ae1 | |||
| 08f35531ba | |||
| ce2959c0b9 | |||
| 4c4b82fe6c | |||
| f4c3050050 | |||
| 2b148b7720 | |||
| c40dd16466 | |||
| 7ffe5d1385 | |||
| c2ef68a435 | |||
| aa117774b3 | |||
| 6b44369458 | |||
| 177b3a6d14 | |||
| 4d1e488ef4 | |||
| 0a39c28a79 | |||
| e019347ca4 | |||
| b425df1d58 | |||
| 055c632ee9 | |||
| 2ac1862e5f | |||
| 5a82afd333 | |||
| 13f81a452d | |||
| c8144b7e1b | |||
| b3b78fa926 | |||
| 824c00dab3 | |||
| 8d331e94d2 | |||
| bc2dd9f52c | |||
| 5dd9f9c5ad | |||
| 3f1afec15b | |||
| e4705d45c4 | |||
| c0846ab8a6 | |||
| bfbb262844 | |||
| 5669aa39ce | |||
| d1f9133ef4 | |||
| 362c9c8588 | |||
| c63cde1cd3 | |||
| 6d937831b9 | |||
| 0eacbed162 | |||
| 6e034c6319 | |||
| a4f6c674ef | |||
| 405af7fb16 | |||
| 995e89b5f6 | |||
| 6b90b83867 | |||
| 66d469ee10 | |||
| 657cbc9c17 | |||
| 29ea506f93 | |||
| 155d144788 | |||
| 892f341feb | |||
| d5808b5c85 | |||
| d6feb686c1 | |||
| 5069a5b48b | |||
| 8c136b18c2 | |||
| f81bc4b870 | |||
| 111b956f93 | |||
| 88a95ad06b | |||
| 04bd602c23 | |||
| de78e53b35 | |||
| fb1a866447 | |||
| 8925d309b0 | |||
| b94485eafd | |||
| a83acd0e8a | |||
| 4e2c500b64 | |||
| 03f863d558 | |||
| b8ad484db0 | |||
| f0fd7c2682 | |||
| 3f170a8b5e | |||
| 37a7cfaf9a | |||
| 5683491029 | |||
| 4ca630a7da | |||
| 99aedbab89 | |||
| 52c05eade7 | |||
| 1ddb956a94 | |||
| 2570c20da0 | |||
| 299710f08a | |||
| 83e5873f3b | |||
| 4889e03cf2 | |||
| 7f6cc8a100 | |||
| e8e0ee1e34 | |||
| 8e3d8eda31 | |||
| afd1fa995f | |||
| a03c9b6d68 | |||
| cdd31d69a8 | |||
| f94983d4d9 | |||
| ffa528001c | |||
| 305b9bc030 | |||
| 6ed3ef3f60 | |||
| 5c2e55861c | |||
| c0843d467d | |||
| 734a79b7ec | |||
| 55a5e66701 | |||
| da86c86577 | |||
| 072c537b6b | |||
| 1577a28ad4 | |||
| ee23131688 | |||
| 7d8b5bc1fc | |||
| 0243597ee8 | |||
| 50a5609c6b | |||
| e76ea838e1 | |||
| ed5c011963 | |||
| 0bc1407e7d | |||
| 7aa00f3417 | |||
| 06e9dbf48a | |||
| ff5acb6a34 | |||
| 231295cb2c | |||
| c8a18dfaf8 | |||
| b339baa923 | |||
| 92d08b47ad | |||
| 5c3a7a99fc | |||
| ea932a01f9 | |||
| d261df0906 | |||
| fac167379f | |||
| f6d9af8d1a | |||
| a128cf5eb3 | |||
| e9c2ee9675 | |||
| e85d33b2a6 | |||
| a42b3ff171 | |||
| 86fdbfbb15 | |||
| 757abfe3db | |||
| cde7940e40 | |||
| d7d6e506da | |||
| 593fc1e77a | |||
| ae857203fd | |||
| a1c418e995 | |||
| f07f096f56 | |||
| 62c1972c95 | |||
| 314c525d2d | |||
| 04b3eef768 | |||
| 506753a535 | |||
| a8b9e1b790 | |||
| be51ccf786 | |||
| 65e077abd1 | |||
| 79598aaae9 | |||
| 62a43c6fb9 | |||
| 1fe1147901 | |||
| 1c97dcd195 | |||
| d3371dbcd2 | |||
| b2b4860983 | |||
| 57fc3deaed | |||
| 677949d61c | |||
| e09d7159bc | |||
| e02af51a04 | |||
| d2c7a2caca | |||
| 739a629552 | |||
| 88f0183352 | |||
| 7f01a163e5 | |||
| fc44662ba3 | |||
| 5052aaca95 | |||
| 826681b6c2 | |||
| 8334bba484 | |||
| a0ae526017 | |||
| 6a6c7b1852 | |||
| 42040f8a9d | |||
| 9d1db0f3de | |||
| f0b922f643 | |||
| bb92b8697d | |||
| 4c17c9eae6 | |||
| 289f17b119 | |||
| dfd7bcae7f | |||
| 0cfd37acd0 | |||
| 105fc302ea | |||
| f4ddc05b9c | |||
| 948f96dd6f | |||
| db627f5cde | |||
| a6866656b7 | |||
| 40c5289e2b | |||
| 7dffc58c5b | |||
| 67d430d100 | |||
| d745b9ef5f | |||
| b9d89cbf5d | |||
| 934c0720d8 | |||
| 06ac206707 | |||
| 6e1bdbbed7 | |||
| 3e3de50eb3 | |||
| d7a8dfb209 | |||
| b0aea54544 | |||
| 41598af2b2 | |||
| f495db41e7 | |||
| 76187f176c | |||
| d7cb1550bf | |||
| 086be13ac4 | |||
| f0cffcbd84 | |||
| d1f3d7d51e | |||
| bdc0341eb3 | |||
| 171411823f | |||
| 7926531330 | |||
| 440866d79e | |||
| d9c758b940 | |||
| 827a8ba107 | |||
| 65a1fafaf7 | |||
| 9bd6891ac2 | |||
| 8a0755340f | |||
| bdebe575b7 | |||
| 472a48092d | |||
| d877872c71 | |||
| 0a297d7642 | |||
| 87d2026e63 | |||
| 20f6489232 | |||
| 2c737b8927 | |||
| 0df5045edb | |||
| b91c25186f | |||
| 7b8a293f30 | |||
| 26df31aa1f | |||
| 36ff22e30a | |||
| 93c5b4be7b | |||
| 495fa43753 | |||
| 2954aacb1c | |||
| 1bf8f516c3 | |||
| e0a84232c3 | |||
| 8c204bb60e | |||
| 245c77869f | |||
| 508a30efb1 | |||
| 5ad54bbe92 | |||
| 4da6513fb5 | |||
| 1154490a97 | |||
| 0e7e24ca6b | |||
| 944ac14be7 | |||
| 7aa28920cf | |||
| 6ce6553456 | |||
| b4470a14cb | |||
| 5b629e6b29 | |||
| 7304b97547 | |||
| f523c5eb73 | |||
| 5981cfb8c9 | |||
| d1e966349f | |||
| dc19fa4baa | |||
| 84e90d140f | |||
| ea557ab109 | |||
| 503393122f | |||
| b5c6d76999 | |||
| 6c56f2f985 | |||
| a4b959f2b8 | |||
| c59b7f6ead | |||
| 38a6f52ef7 | |||
| 33e2829697 |
@@ -62,6 +62,8 @@ For more information on how to work with Atom's official packages, see
|
||||
* Use `path.join()` to concatenate filenames.
|
||||
* Use `os.tmpdir()` rather than `/tmp` when you need to reference the
|
||||
temporary directory.
|
||||
* Using a plain `return` when returning explicitly at the end of a function.
|
||||
* Not `return null`, `return undefined`, `null`, or `undefined`
|
||||
|
||||
## Git Commit Messages
|
||||
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.102.0"
|
||||
"atom-package-manager": "0.106.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~0.2.9",
|
||||
"donna": "1.0.1",
|
||||
"donna": "1.0.6",
|
||||
"formidable": "~1.0.14",
|
||||
"fs-plus": "2.x",
|
||||
"github-releases": "~0.2.0",
|
||||
@@ -18,7 +18,7 @@
|
||||
"grunt-contrib-csslint": "~0.1.2",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"grunt-cson": "0.10.0",
|
||||
"grunt-download-atom-shell": "~0.8.0",
|
||||
"grunt-download-atom-shell": "~0.10.0",
|
||||
"grunt-lesslint": "0.13.0",
|
||||
"grunt-markdown": "~0.4.0",
|
||||
"grunt-peg": "~1.1.0",
|
||||
|
||||
@@ -153,7 +153,7 @@ module.exports = (grunt) ->
|
||||
fs.writeFileSync path.join(appDir, 'node_modules', 'symbols-view', 'vendor', 'ctags-win32.exe.ignore'), ''
|
||||
fs.writeFileSync path.join(shellAppDir, 'atom.exe.gui'), ''
|
||||
|
||||
dependencies = ['compile', "generate-license:save"]
|
||||
dependencies = ['compile', 'generate-license:save', 'generate-module-cache', 'compile-packages-slug']
|
||||
dependencies.push('copy-info-plist') if process.platform is 'darwin'
|
||||
dependencies.push('set-exe-icon') if process.platform is 'win32'
|
||||
grunt.task.run(dependencies...)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
path = require 'path'
|
||||
CSON = require 'season'
|
||||
fs = require 'fs-plus'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{spawn, rm} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'compile-packages-slug', 'Add bundled package metadata information to the main package.json file', ->
|
||||
appDir = fs.realpathSync(grunt.config.get('atom.appDir'))
|
||||
|
||||
modulesDirectory = path.join(appDir, 'node_modules')
|
||||
packages = {}
|
||||
|
||||
for moduleDirectory in fs.listSync(modulesDirectory)
|
||||
continue if path.basename(moduleDirectory) is '.bin'
|
||||
|
||||
metadataPath = path.join(moduleDirectory, 'package.json')
|
||||
metadata = grunt.file.readJSON(metadataPath)
|
||||
continue unless metadata?.engines?.atom?
|
||||
|
||||
moduleCache = metadata._atomModuleCache ? {}
|
||||
|
||||
rm metadataPath
|
||||
_.remove(moduleCache.extensions?['.json'] ? [], 'package.json')
|
||||
|
||||
for property in ['_from', '_id', 'dist', 'readme', 'readmeFilename']
|
||||
delete metadata[property]
|
||||
|
||||
pack = {metadata, keymaps: {}, menus: {}}
|
||||
|
||||
if metadata.main
|
||||
mainPath = require.resolve(path.resolve(moduleDirectory, metadata.main))
|
||||
pack.main = path.relative(appDir, mainPath)
|
||||
|
||||
for keymapPath in fs.listSync(path.join(moduleDirectory, 'keymaps'), ['.cson', '.json'])
|
||||
relativePath = path.relative(appDir, keymapPath)
|
||||
pack.keymaps[relativePath] = CSON.readFileSync(keymapPath)
|
||||
rm keymapPath
|
||||
|
||||
for menuPath in fs.listSync(path.join(moduleDirectory, 'menus'), ['.cson', '.json'])
|
||||
relativePath = path.relative(appDir, menuPath)
|
||||
pack.menus[relativePath] = CSON.readFileSync(menuPath)
|
||||
rm menuPath
|
||||
|
||||
packages[metadata.name] = pack
|
||||
|
||||
for extension, paths of moduleCache.extensions
|
||||
delete moduleCache.extensions[extension] if paths.length is 0
|
||||
|
||||
metadata = grunt.file.readJSON(path.join(appDir, 'package.json'))
|
||||
metadata._atomPackages = packages
|
||||
|
||||
grunt.file.write(path.join(appDir, 'package.json'), JSON.stringify(metadata))
|
||||
@@ -0,0 +1,39 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
ModuleCache = require '../../src/module-cache'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
grunt.registerTask 'generate-module-cache', 'Generate a module cache for all core modules and packages', ->
|
||||
appDir = grunt.config.get('atom.appDir')
|
||||
|
||||
{packageDependencies} = grunt.file.readJSON('package.json')
|
||||
|
||||
for packageName, version of packageDependencies
|
||||
ModuleCache.create(path.join(appDir, 'node_modules', packageName))
|
||||
|
||||
ModuleCache.create(appDir)
|
||||
|
||||
metadata = grunt.file.readJSON(path.join(appDir, 'package.json'))
|
||||
|
||||
metadata._atomModuleCache.folders.forEach (folder) ->
|
||||
if '' in folder.paths
|
||||
folder.paths = [
|
||||
''
|
||||
'exports'
|
||||
'spec'
|
||||
'src'
|
||||
'src/browser'
|
||||
'static'
|
||||
'vendor'
|
||||
]
|
||||
|
||||
# Reactionary does not have an explicit react dependency
|
||||
metadata._atomModuleCache.folders.push
|
||||
paths: [
|
||||
'node_modules/reactionary-atom-fork/lib'
|
||||
]
|
||||
dependencies: {
|
||||
'react-atom-fork': metadata.dependencies['react-atom-fork']
|
||||
}
|
||||
|
||||
grunt.file.write(path.join(appDir, 'package.json'), JSON.stringify(metadata))
|
||||
@@ -32,7 +32,7 @@ module.exports = (grunt) ->
|
||||
binDir = path.join(installDir, 'bin')
|
||||
shareDir = path.join(installDir, 'share', 'atom')
|
||||
|
||||
iconName = path.join(shareDir,'resources','app','resources','atom.png')
|
||||
iconName = path.join(shareDir,'resources', 'app', 'resources', 'atom.png')
|
||||
|
||||
mkdir binDir
|
||||
cp 'atom.sh', path.join(binDir, 'atom')
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
fillTemplate = (filePath, data) ->
|
||||
template = _.template(String(fs.readFileSync("#{filePath}.in")))
|
||||
filled = template(data)
|
||||
|
||||
outputPath = path.join(grunt.config.get('atom.buildDir'), path.basename(filePath))
|
||||
grunt.file.write(outputPath, filled)
|
||||
outputPath
|
||||
|
||||
grunt.registerTask 'mkrpm', 'Create rpm package', ->
|
||||
done = @async()
|
||||
|
||||
if process.arch is 'ia32'
|
||||
arch = 'i386'
|
||||
else if process.arch is 'x64'
|
||||
arch = 'amd64'
|
||||
else
|
||||
return done("Unsupported arch #{process.arch}")
|
||||
|
||||
{name, version, description} = grunt.file.readJSON('package.json')
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
installDir = grunt.config.get('atom.installDir')
|
||||
shareDir = path.join(installDir, 'share', 'atom')
|
||||
iconName = path.join(shareDir, 'resources', 'app', 'resources', 'atom.png')
|
||||
|
||||
data = {name, version, description, installDir, iconName}
|
||||
specFilePath = fillTemplate(path.join('resources', 'linux', 'redhat', 'atom.spec'), data)
|
||||
desktopFilePath = fillTemplate(path.join('resources', 'linux', 'atom.desktop'), data)
|
||||
|
||||
cmd = path.join('script', 'mkrpm')
|
||||
args = [specFilePath, desktopFilePath, buildDir]
|
||||
spawn {cmd, args}, (error) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
grunt.log.ok "Created rpm package in #{buildDir}"
|
||||
done()
|
||||
@@ -20,9 +20,9 @@ module.exports = (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'
|
||||
tasks = ['upload-assets']
|
||||
tasks.unshift('build-docs', 'prepare-docs') if process.platform is 'darwin'
|
||||
tasks = []
|
||||
tasks.push('build-docs', 'prepare-docs') if process.platform is 'darwin'
|
||||
tasks.push('upload-assets') if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
grunt.task.run(tasks)
|
||||
|
||||
grunt.registerTask 'prepare-docs', 'Move api.json to atom-api.json', ->
|
||||
@@ -31,7 +31,13 @@ module.exports = (gruntObject) ->
|
||||
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()
|
||||
doneCallback = @async()
|
||||
startTime = Date.now()
|
||||
done = (args...) ->
|
||||
elapsedTime = Math.round((Date.now() - startTime) / 100) / 10
|
||||
grunt.log.ok("Upload time: #{elapsedTime}s")
|
||||
doneCallback(args...)
|
||||
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
assets = getAssets()
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ module.exports = (grunt) ->
|
||||
packageJsonPath = path.join(appDir, 'package.json')
|
||||
packageJson = require(packageJsonPath)
|
||||
packageJson.version = version
|
||||
packageJsonString = JSON.stringify(packageJson, null, 2)
|
||||
packageJsonString = JSON.stringify(packageJson)
|
||||
fs.writeFileSync(packageJsonPath, packageJsonString)
|
||||
|
||||
if process.platform is 'darwin'
|
||||
|
||||
@@ -4,6 +4,8 @@ path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
async = require 'async'
|
||||
|
||||
concurrency = 2
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{isAtomPackage, spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
@@ -61,7 +63,7 @@ module.exports = (grunt) ->
|
||||
continue unless isAtomPackage(packagePath)
|
||||
packageSpecQueue.push(packagePath)
|
||||
|
||||
packageSpecQueue.concurrency = 1
|
||||
packageSpecQueue.concurrency = concurrency - 1
|
||||
packageSpecQueue.drain = -> callback(null, failedPackages)
|
||||
|
||||
runCoreSpecs = (callback) ->
|
||||
@@ -84,7 +86,7 @@ module.exports = (grunt) ->
|
||||
fs.unlinkSync('ci.log')
|
||||
else
|
||||
# TODO: Restore concurrency on Windows
|
||||
packageSpecQueue.concurrency = 2
|
||||
packageSpecQueue.concurrency = concurrency
|
||||
|
||||
callback(null, error)
|
||||
|
||||
@@ -102,7 +104,7 @@ module.exports = (grunt) ->
|
||||
method [runCoreSpecs, runPackageSpecs], (error, results) ->
|
||||
[coreSpecFailed, failedPackages] = results
|
||||
elapsedTime = Math.round((Date.now() - startTime) / 100) / 10
|
||||
grunt.verbose.writeln("Total spec time: #{elapsedTime}s")
|
||||
grunt.log.ok("Total spec time: #{elapsedTime}s using #{concurrency} cores")
|
||||
failures = failedPackages
|
||||
failures.push "atom core" if coreSpecFailed
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# Scoped Settings, Scopes and Scope Descriptors
|
||||
|
||||
Atom supports language-specific settings. You can soft wrap only Markdown files, or set the tab length to 4 in Python files.
|
||||
|
||||
Language-specific settings are a subset of something more general we call "scoped settings". Scoped settings allow targeting down to a specific syntax token type. For example, you could conceivably set a setting to target only Ruby comments, only code inside Markdown files, or even only JavaScript function names.
|
||||
|
||||
## Scope names in syntax tokens
|
||||
|
||||
Each token in the editor has a collection of scope names. For example, the aformentioned JavaScript function name might have the scope names `function` and `name`. An open paren might have the scope names `punctuation`, `parameters`, `begin`.
|
||||
|
||||
Scope names work just like CSS classes. In fact, in the editor, scope names are attached to a token's DOM node as CSS classes.
|
||||
|
||||
Take this piece of JavaScript:
|
||||
|
||||
```js
|
||||
function functionName() {
|
||||
console.log('Log it out');
|
||||
}
|
||||
```
|
||||
|
||||
In the dev tools, the first line's markup looks like this.
|
||||
|
||||

|
||||
|
||||
All the class names on the spans are scope names. Any scope name can be used to target a setting's value.
|
||||
|
||||
## Scope Selectors
|
||||
|
||||
Scope selectors allow you to target specific tokens just like a CSS selector targets specific nodes in the DOM. Some examples:
|
||||
|
||||
```coffee
|
||||
'.source.js' # selects all javascript tokens
|
||||
'.source.js .function.name' # selects all javascript function names
|
||||
'.function.name' # selects all function names in any language
|
||||
```
|
||||
|
||||
[Config::set][config-set] accepts a `scopeSelector`. If you'd like to set a setting for JavaScript function names, you can give it the js function name `scopeSelector`:
|
||||
|
||||
```coffee
|
||||
atom.config.set('.source.js .function.name', 'my-package.my-setting', 'special value')
|
||||
```
|
||||
|
||||
## Scope Descriptors
|
||||
|
||||
A scope descriptor is an [Object][scope-descriptor] that wraps an `Array` of
|
||||
`String`s. The Array describes a path from the root of the syntax tree to a
|
||||
token including _all_ scope names for the entire path.
|
||||
|
||||
In our JavaScript example above, a scope descriptor for the function name token would be:
|
||||
|
||||
```coffee
|
||||
['source.js', 'meta.function.js', 'entity.name.function.js']
|
||||
```
|
||||
|
||||
[Config::get][config-get] accepts a `scopeDescriptor`. You can get the value for your setting scoped to JavaScript function names via:
|
||||
|
||||
```coffee
|
||||
scopeDescriptor = ['source.js', 'meta.function.js', 'entity.name.function.js']
|
||||
value = atom.config.get(scopeDescriptor, 'my-package.my-setting')
|
||||
```
|
||||
|
||||
But, you do not need to generate scope descriptors by hand. There are a couple methods available to get the scope descriptor from the editor:
|
||||
|
||||
* [Editor::getRootScopeDescriptor][editor-getRootScopeDescriptor] to get the language's descriptor. eg. `[".source.js"]`
|
||||
* [Editor::scopeDescriptorForBufferPosition][editor-scopeDescriptorForBufferPosition] to get the descriptor at a specific position in the buffer.
|
||||
* [Cursor::getScopeDescriptor][cursor-getScopeDescriptor] to get a cursor's descriptor based on position. eg. if the cursor were in the name of the method in our example it would return `["source.js", "meta.function.js", "entity.name.function.js"]`
|
||||
|
||||
Let's revisit our example using these methods:
|
||||
|
||||
```coffee
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
cursor = editor.getLastCursor()
|
||||
valueAtCursor = atom.config.get(cursor.getScopeDescriptor(), 'my-package.my-setting')
|
||||
valueForLanguage = atom.config.get(editor.getRootScopeDescriptor(), 'my-package.my-setting')
|
||||
```
|
||||
|
||||
|
||||
[config]:https://atom.io/docs/api/latest/Config
|
||||
[config-get]:https://atom.io/docs/api/latest/Config#instance-get
|
||||
[config-set]:https://atom.io/docs/api/latest/Config#instance-set
|
||||
[config-observe]:https://atom.io/docs/api/latest/Config#instance-observe
|
||||
|
||||
[editor-getRootScopeDescriptor]:https://atom.io/docs/api/latest/TextEditor#instance-getRootScopeDescriptor
|
||||
[editor-scopeDescriptorForBufferPosition]:https://atom.io/docs/api/latest/TextEditor#instance-scopeDescriptorForBufferPosition
|
||||
|
||||
[cursor-getScopeDescriptor]:https://atom.io/docs/api/latest/Cursor#instance-getScopeDescriptor
|
||||
[scope-descriptor]:https://atom.io/docs/api/latest/ScopeDescriptor
|
||||
@@ -9,22 +9,27 @@ in the proper package's repository.
|
||||
|
||||
### Cloning
|
||||
|
||||
The first step is creating your own clone. You can of course do this manually
|
||||
with git, or you can use the `apm develop` command to create a clone based on
|
||||
the package's `repository` field in the `package.json`.
|
||||
The first step is creating your own clone.
|
||||
|
||||
For example, if you want to make changes to the `tree-view` package, run the
|
||||
following command:
|
||||
For example, if you want to make changes to the `tree-view` package, fork the repo on your github account, then clone it:
|
||||
|
||||
```
|
||||
> apm develop tree-view
|
||||
Cloning https://github.com/atom/tree-view ✓
|
||||
> git clone git@github.com:your-username/tree-view.git
|
||||
```
|
||||
|
||||
Next install all the dependencies:
|
||||
|
||||
```
|
||||
> cd tree-view
|
||||
> apm install
|
||||
Installing modules ✓
|
||||
~/.atom/dev/packages/tree-view -> ~/github/tree-view
|
||||
```
|
||||
|
||||
This clones the `tree-view` repository to `~/github`. If you prefer a different
|
||||
path, specify it via the `ATOM_REPOS_HOME` environment variable.
|
||||
Now you can link it to development mode so when you run an Atom window with `atom --dev`, you will use your fork instead of the built in package:
|
||||
|
||||
```
|
||||
> apm link -d
|
||||
```
|
||||
|
||||
### Running in Development Mode
|
||||
|
||||
@@ -46,7 +51,4 @@ from the package directory to create and remove dev-mode symlinks.
|
||||
|
||||
### Installing Dependencies
|
||||
|
||||
Finally, you need to install the cloned package's dependencies by running
|
||||
`apm install` within the package directory. This step is also performed
|
||||
automatically the first time you run `apm develop`, but you'll want to keep
|
||||
dependencies up to date by running `apm update` after pulling upstream changes.
|
||||
You'll want to keep dependencies up to date by running `apm update` after pulling any upstream changes.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* [Converting a TextMate Bundle](converting-a-text-mate-bundle.md)
|
||||
* [Converting a TextMate Theme](converting-a-text-mate-theme.md)
|
||||
* [Contributing](contributing.md)
|
||||
* [Contributing to Core Packages](contributing-to-packages.md)
|
||||
* [Debugging](debugging.md)
|
||||
|
||||
### Advanced Topics
|
||||
@@ -18,3 +19,4 @@
|
||||
* [Keymaps](advanced/keymaps.md)
|
||||
* [Serialization](advanced/serialization.md)
|
||||
* [View System](advanced/view-system.md)
|
||||
* [Scopes and Scope Descriptors](advanced/scopes-and-scope-descriptors.md)
|
||||
|
||||
@@ -19,3 +19,7 @@
|
||||
# You can find more information about keymaps in these guides:
|
||||
# * https://atom.io/docs/latest/customizing-atom#customizing-key-bindings
|
||||
# * https://atom.io/docs/latest/advanced/keymaps
|
||||
#
|
||||
# This file uses CoffeeScript Object Notation (CSON).
|
||||
# If you are unfamiliar with CSON, you can read more about it here:
|
||||
# https://github.com/bevry/cson#what-is-cson
|
||||
|
||||
@@ -13,3 +13,6 @@
|
||||
# 'prefix': 'log'
|
||||
# 'body': 'console.log $1'
|
||||
#
|
||||
# This file uses CoffeeScript Object Notation (CSON).
|
||||
# If you are unfamiliar with CSON, you can read more about it here:
|
||||
# https://github.com/bevry/cson#what-is-cson
|
||||
|
||||
+26
-26
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.137.0",
|
||||
"version": "0.139.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -17,7 +17,7 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.18.0",
|
||||
"atomShellVersion": "0.18.2",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^2.2.1",
|
||||
@@ -37,7 +37,7 @@
|
||||
"guid": "0.0.10",
|
||||
"jasmine-json": "~0.0",
|
||||
"jasmine-tagged": "^1.1.2",
|
||||
"less-cache": "0.15.0",
|
||||
"less-cache": "0.17.0",
|
||||
"mixto": "^1",
|
||||
"mkdirp": "0.3.5",
|
||||
"nslog": "^1.0.1",
|
||||
@@ -51,16 +51,16 @@
|
||||
"reactionary-atom-fork": "^1.0.0",
|
||||
"runas": "1.0.1",
|
||||
"scandal": "1.0.2",
|
||||
"scoped-property-store": "^0.13.2",
|
||||
"scoped-property-store": "^0.14.0",
|
||||
"scrollbar-style": "^1.0.2",
|
||||
"season": "^1.0.2",
|
||||
"semver": "1.1.4",
|
||||
"semver": "2.2.1",
|
||||
"serializable": "^1",
|
||||
"space-pen": "3.8.0",
|
||||
"temp": "0.7.0",
|
||||
"text-buffer": "^3.2.8",
|
||||
"text-buffer": "^3.2.9",
|
||||
"theorist": "^1.0.2",
|
||||
"underscore-plus": "^1.5.1",
|
||||
"underscore-plus": "^1.6.1",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
@@ -80,67 +80,67 @@
|
||||
"bookmarks": "0.28.0",
|
||||
"bracket-matcher": "0.61.0",
|
||||
"command-palette": "0.27.0",
|
||||
"deprecation-cop": "0.10.0",
|
||||
"deprecation-cop": "0.11.0",
|
||||
"dev-live-reload": "0.34.0",
|
||||
"exception-reporting": "0.20.0",
|
||||
"feedback": "0.33.0",
|
||||
"find-and-replace": "0.140.0",
|
||||
"fuzzy-finder": "0.58.0",
|
||||
"git-diff": "0.39.0",
|
||||
"git-diff": "0.40.0",
|
||||
"go-to-line": "0.25.0",
|
||||
"grammar-selector": "0.35.0",
|
||||
"image-view": "0.37.0",
|
||||
"incompatible-packages": "0.10.0",
|
||||
"keybinding-resolver": "0.20.0",
|
||||
"link": "0.25.0",
|
||||
"markdown-preview": "0.104.0",
|
||||
"link": "0.26.0",
|
||||
"markdown-preview": "0.105.0",
|
||||
"metrics": "0.36.0",
|
||||
"open-on-github": "0.30.0",
|
||||
"package-generator": "0.31.0",
|
||||
"release-notes": "0.36.0",
|
||||
"settings-view": "0.152.0",
|
||||
"snippets": "0.55.0",
|
||||
"settings-view": "0.153.0",
|
||||
"snippets": "0.56.0",
|
||||
"spell-check": "0.42.0",
|
||||
"status-bar": "0.46.0",
|
||||
"styleguide": "0.30.0",
|
||||
"symbols-view": "0.66.0",
|
||||
"tabs": "0.54.0",
|
||||
"timecop": "0.22.0",
|
||||
"tabs": "0.55.0",
|
||||
"timecop": "0.23.0",
|
||||
"tree-view": "0.131.0",
|
||||
"update-package-dependencies": "0.6.0",
|
||||
"welcome": "0.19.0",
|
||||
"whitespace": "0.25.0",
|
||||
"wrap-guide": "0.23.0",
|
||||
"language-c": "0.28.0",
|
||||
"language-coffee-script": "0.35.0",
|
||||
"language-c": "0.29.0",
|
||||
"language-coffee-script": "0.36.0",
|
||||
"language-css": "0.21.0",
|
||||
"language-gfm": "0.51.0",
|
||||
"language-gfm": "0.53.0",
|
||||
"language-git": "0.9.0",
|
||||
"language-go": "0.18.0",
|
||||
"language-go": "0.19.0",
|
||||
"language-html": "0.26.0",
|
||||
"language-hyperlink": "0.12.0",
|
||||
"language-java": "0.11.0",
|
||||
"language-javascript": "0.40.0",
|
||||
"language-javascript": "0.42.0",
|
||||
"language-json": "0.8.0",
|
||||
"language-less": "0.15.0",
|
||||
"language-less": "0.16.0",
|
||||
"language-make": "0.12.0",
|
||||
"language-mustache": "0.10.0",
|
||||
"language-objective-c": "0.11.0",
|
||||
"language-perl": "0.9.0",
|
||||
"language-php": "0.16.0",
|
||||
"language-property-list": "0.7.0",
|
||||
"language-python": "0.20.0",
|
||||
"language-ruby": "0.39.0",
|
||||
"language-python": "0.21.0",
|
||||
"language-ruby": "0.40.0",
|
||||
"language-ruby-on-rails": "0.18.0",
|
||||
"language-sass": "0.22.0",
|
||||
"language-sass": "0.23.0",
|
||||
"language-shellscript": "0.8.0",
|
||||
"language-source": "0.8.0",
|
||||
"language-sql": "0.11.0",
|
||||
"language-text": "0.6.0",
|
||||
"language-todo": "0.13.0",
|
||||
"language-toml": "0.12.0",
|
||||
"language-toml": "0.14.0",
|
||||
"language-xml": "0.24.0",
|
||||
"language-yaml": "0.18.0"
|
||||
"language-yaml": "0.19.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Name=Atom
|
||||
Comment=<%= description %>
|
||||
GenericName=Text Editor
|
||||
Exec=<%= installDir %>/share/atom/atom %U
|
||||
Icon=<%= iconName %>
|
||||
Type=Application
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
Name: <%= name %>
|
||||
Version: <%= version %>
|
||||
Release: 0.1%{?dist}
|
||||
Summary: Atom is a hackable text editor for the 21st century
|
||||
License: MIT
|
||||
URL: https://atom.io/
|
||||
BuildConflicts: gyp
|
||||
BuildRequires: make, gcc, gcc-c++, glibc-devel, git-core, libgnome-keyring-devel
|
||||
Requires: libgnome-keyring
|
||||
AutoReqProv: no # Avoid libchromiumcontent.so missing dependency
|
||||
|
||||
%description
|
||||
<%= description %>
|
||||
|
||||
%install
|
||||
mkdir -p %{buildroot}/usr/local/share/atom
|
||||
cp -r /tmp/atom-build/Atom/* %{buildroot}/usr/local/share/atom
|
||||
mkdir -p %{buildroot}/usr/local/bin/
|
||||
ln -sf /usr/local/share/atom/resources/app/apm/node_modules/.bin/apm %{buildroot}/usr/local/bin/apm
|
||||
cp atom.sh %{buildroot}/usr/local/bin/atom
|
||||
chmod 755 atom.sh
|
||||
mkdir -p %{buildroot}/usr/local/share/applications/
|
||||
mv atom.desktop %{buildroot}/usr/local/share/applications/
|
||||
|
||||
%files
|
||||
/usr/local/bin/atom
|
||||
/usr/local/bin/apm
|
||||
/usr/local/share/atom/
|
||||
/usr/local/share/applications/atom.desktop
|
||||
Arquivo executável
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
SPEC_FILE="$1"
|
||||
DESKTOP_FILE="$2"
|
||||
BUILD_DIRECTORY="$3"
|
||||
|
||||
RPM_BUILD_ROOT=~/rpmbuild
|
||||
ARCH=`uname -m`
|
||||
|
||||
rpmdev-setuptree
|
||||
|
||||
cp -r $BUILD_DIRECTORY/Atom/* $RPM_BUILD_ROOT/BUILD
|
||||
cp $SPEC_FILE $RPM_BUILD_ROOT/SPECS
|
||||
cp ./atom.sh $RPM_BUILD_ROOT/BUILD
|
||||
cp $DESKTOP_FILE $RPM_BUILD_ROOT/BUILD
|
||||
|
||||
rpmbuild -ba $SPEC_FILE
|
||||
cp $RPM_BUILD_ROOT/RPMS/$ARCH/atom-*.rpm $BUILD_DIRECTORY
|
||||
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
@@ -88,6 +88,19 @@ describe "Config", ->
|
||||
expect(atom.config.getDefault('foo.bar')).toEqual initialDefaultValue
|
||||
expect(atom.config.getDefault('foo.bar')).not.toBe initialDefaultValue
|
||||
|
||||
describe "when scoped settings are used", ->
|
||||
it "returns the global default when no scoped default set", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
expect(atom.config.getDefault('.source.coffee', 'foo.bar.baz')).toBe 10
|
||||
|
||||
it "returns the scoped default when a scoped default is set", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
atom.config.addScopedSettings("default", ".source.coffee", foo: bar: baz: 42)
|
||||
expect(atom.config.getDefault('.source.coffee', 'foo.bar.baz')).toBe 42
|
||||
|
||||
atom.config.set('.source.coffee', 'foo.bar.baz', 55)
|
||||
expect(atom.config.getDefault('.source.coffee', 'foo.bar.baz')).toBe 42
|
||||
|
||||
describe ".isDefault(keyPath)", ->
|
||||
it "returns true when the value of the key path is its default value", ->
|
||||
atom.config.setDefaults("foo", same: 1, changes: 1)
|
||||
@@ -99,6 +112,21 @@ describe "Config", ->
|
||||
expect(atom.config.isDefault('foo.same')).toBe false
|
||||
expect(atom.config.isDefault('foo.changes')).toBe false
|
||||
|
||||
describe "when scoped settings are used", ->
|
||||
it "returns false when a scoped setting was set by the user", ->
|
||||
expect(atom.config.isDefault('.source.coffee', 'foo.bar.baz')).toBe true
|
||||
|
||||
atom.config.addScopedSettings("default", ".source.coffee", foo: bar: baz: 42)
|
||||
expect(atom.config.isDefault('.source.coffee', 'foo.bar.baz')).toBe true
|
||||
|
||||
atom.config.set('.source.coffee', 'foo.bar.baz', 55)
|
||||
expect(atom.config.isDefault('.source.coffee', 'foo.bar.baz')).toBe false
|
||||
|
||||
describe ".setDefaults(keyPath)", ->
|
||||
it "sets a default when the setting's key contains an escaped dot", ->
|
||||
atom.config.setDefaults("foo", 'a\\.b': 1, b: 2)
|
||||
expect(atom.config.get("foo")).toEqual 'a\\.b': 1, b: 2
|
||||
|
||||
describe ".toggle(keyPath)", ->
|
||||
it "negates the boolean value of the current key path value", ->
|
||||
atom.config.set('foo.a', 1)
|
||||
@@ -130,6 +158,66 @@ describe "Config", ->
|
||||
atom.config.restoreDefault('a.c')
|
||||
expect(atom.config.get('a.c')).toBeUndefined()
|
||||
|
||||
it "calls ::save()", ->
|
||||
atom.config.setDefaults('a', b: 3)
|
||||
atom.config.set('a.b', 4)
|
||||
atom.config.save.reset()
|
||||
|
||||
atom.config.restoreDefault('a.c')
|
||||
expect(atom.config.save.callCount).toBe 1
|
||||
|
||||
describe "when scoped settings are used", ->
|
||||
it "restores the global default when no scoped default set", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
atom.config.set('.source.coffee', 'foo.bar.baz', 55)
|
||||
expect(atom.config.get(['.source.coffee'], 'foo.bar.baz')).toBe 55
|
||||
|
||||
atom.config.restoreDefault('.source.coffee', 'foo.bar.baz')
|
||||
expect(atom.config.get(['.source.coffee'], 'foo.bar.baz')).toBe 10
|
||||
|
||||
it "restores the scoped default when a scoped default is set", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
atom.config.addScopedSettings("default", ".source.coffee", foo: bar: baz: 42)
|
||||
atom.config.set('.source.coffee', 'foo.bar.baz', 55)
|
||||
atom.config.set('.source.coffee', 'foo.bar.ok', 100)
|
||||
expect(atom.config.get(['.source.coffee'], 'foo.bar.baz')).toBe 55
|
||||
|
||||
atom.config.restoreDefault('.source.coffee', 'foo.bar.baz')
|
||||
expect(atom.config.get(['.source.coffee'], 'foo.bar.baz')).toBe 42
|
||||
expect(atom.config.get(['.source.coffee'], 'foo.bar.ok')).toBe 100
|
||||
|
||||
it "calls ::save()", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
atom.config.addScopedSettings("default", ".source.coffee", foo: bar: baz: 42)
|
||||
atom.config.set('.source.coffee', 'foo.bar.baz', 55)
|
||||
atom.config.save.reset()
|
||||
|
||||
atom.config.restoreDefault('.source.coffee', 'foo.bar.baz')
|
||||
expect(atom.config.save.callCount).toBe 1
|
||||
|
||||
describe ".getSettings()", ->
|
||||
it "returns all settings including defaults", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
atom.config.set("foo.ok", 12)
|
||||
|
||||
expect(atom.config.getSettings().foo).toEqual
|
||||
ok: 12
|
||||
bar:
|
||||
baz: 10
|
||||
|
||||
describe "when scoped settings are used", ->
|
||||
it "returns all the scoped settings including all the defaults", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
atom.config.set("foo.ok", 12)
|
||||
atom.config.addScopedSettings("default", ".source.coffee", foo: bar: baz: 42)
|
||||
atom.config.addScopedSettings("default", ".source.coffee", foo: bar: omg: 'omg')
|
||||
|
||||
expect(atom.config.getSettings(".source.coffee").foo).toEqual
|
||||
ok: 12
|
||||
bar:
|
||||
baz: 42
|
||||
omg: 'omg'
|
||||
|
||||
describe ".pushAtKeyPath(keyPath, value)", ->
|
||||
it "pushes the given value to the array at the key path and updates observers", ->
|
||||
atom.config.set("foo.bar.baz", ["a"])
|
||||
@@ -948,6 +1036,21 @@ describe "Config", ->
|
||||
expect(atom.config.set('foo.bar.arr', ['two', 'three'])).toBe true
|
||||
expect(atom.config.get('foo.bar.arr')).toEqual ['two', 'three']
|
||||
|
||||
describe "when scoped settings are used", ->
|
||||
beforeEach ->
|
||||
schema =
|
||||
type: 'string'
|
||||
default: 'ok'
|
||||
scopes:
|
||||
'.source.js':
|
||||
default: 'omg'
|
||||
atom.config.setSchema('foo.bar.str', schema)
|
||||
|
||||
it 'it respects the scoped defaults', ->
|
||||
expect(atom.config.get('foo.bar.str')).toBe 'ok'
|
||||
expect(atom.config.get(['.source.js'], 'foo.bar.str')).toBe 'omg'
|
||||
expect(atom.config.get(['.source.coffee'], 'foo.bar.str')).toBe 'ok'
|
||||
|
||||
describe "scoped settings", ->
|
||||
describe ".get(scopeDescriptor, keyPath)", ->
|
||||
it "returns the property with the most specific scope selector", ->
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"foo": "bar"
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
path = require 'path'
|
||||
Module = require 'module'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
ModuleCache = require '../src/module-cache'
|
||||
|
||||
describe 'ModuleCache', ->
|
||||
beforeEach ->
|
||||
spyOn(Module, '_findPath').andCallThrough()
|
||||
|
||||
it 'resolves atom shell module paths without hitting the filesystem', ->
|
||||
builtins = ModuleCache.cache.builtins
|
||||
expect(Object.keys(builtins).length).toBeGreaterThan 0
|
||||
|
||||
for builtinName, builtinPath of builtins
|
||||
expect(require.resolve(builtinName)).toBe builtinPath
|
||||
expect(fs.isFileSync(require.resolve(builtinName)))
|
||||
|
||||
expect(Module._findPath.callCount).toBe 0
|
||||
|
||||
it 'resolves relative core paths without hitting the filesystem', ->
|
||||
ModuleCache.add atom.getLoadSettings().resourcePath, {
|
||||
_atomModuleCache:
|
||||
extensions:
|
||||
'.json': [
|
||||
path.join('spec', 'fixtures', 'module-cache', 'file.json')
|
||||
]
|
||||
}
|
||||
expect(require('./fixtures/module-cache/file.json').foo).toBe 'bar'
|
||||
expect(Module._findPath.callCount).toBe 0
|
||||
|
||||
it 'resolves module paths when a compatible version is provided by core', ->
|
||||
packagePath = fs.realpathSync(temp.mkdirSync('atom-package'))
|
||||
ModuleCache.add packagePath, {
|
||||
_atomModuleCache:
|
||||
folders: [{
|
||||
paths: [
|
||||
''
|
||||
]
|
||||
dependencies:
|
||||
'underscore-plus': '*'
|
||||
}]
|
||||
}
|
||||
ModuleCache.add atom.getLoadSettings().resourcePath, {
|
||||
_atomModuleCache:
|
||||
dependencies: [{
|
||||
name: 'underscore-plus'
|
||||
version: require('underscore-plus/package.json').version
|
||||
path: path.join('node_modules', 'underscore-plus', 'lib', 'underscore-plus.js')
|
||||
}]
|
||||
}
|
||||
|
||||
indexPath = path.join(packagePath, 'index.js')
|
||||
fs.writeFileSync indexPath, """
|
||||
exports.load = function() { require('underscore-plus'); };
|
||||
"""
|
||||
|
||||
packageMain = require(indexPath)
|
||||
Module._findPath.reset()
|
||||
packageMain.load()
|
||||
expect(Module._findPath.callCount).toBe 0
|
||||
|
||||
it 'does not resolve module paths when no compatible version is provided by core', ->
|
||||
packagePath = fs.realpathSync(temp.mkdirSync('atom-package'))
|
||||
ModuleCache.add packagePath, {
|
||||
_atomModuleCache:
|
||||
folders: [{
|
||||
paths: [
|
||||
''
|
||||
]
|
||||
dependencies:
|
||||
'underscore-plus': '0.0.1'
|
||||
}]
|
||||
}
|
||||
ModuleCache.add atom.getLoadSettings().resourcePath, {
|
||||
_atomModuleCache:
|
||||
dependencies: [{
|
||||
name: 'underscore-plus'
|
||||
version: require('underscore-plus/package.json').version
|
||||
path: path.join('node_modules', 'underscore-plus', 'lib', 'underscore-plus.js')
|
||||
}]
|
||||
}
|
||||
|
||||
indexPath = path.join(packagePath, 'index.js')
|
||||
fs.writeFileSync indexPath, """
|
||||
exports.load = function() { require('underscore-plus'); };
|
||||
"""
|
||||
|
||||
packageMain = require(indexPath)
|
||||
Module._findPath.reset()
|
||||
expect(-> packageMain.load()).toThrow()
|
||||
expect(Module._findPath.callCount).toBe 1
|
||||
@@ -0,0 +1,63 @@
|
||||
ViewRegistry = require '../src/view-registry'
|
||||
Panel = require '../src/panel'
|
||||
PanelElement = require '../src/panel-element'
|
||||
PanelContainer = require '../src/panel-container'
|
||||
PanelContainerElement = require '../src/panel-container-element'
|
||||
|
||||
describe "PanelContainerElement", ->
|
||||
[jasmineContent, element, container, viewRegistry] = []
|
||||
|
||||
class TestPanelContainerItem
|
||||
constructior: ->
|
||||
|
||||
class TestPanelContainerItemElement extends HTMLElement
|
||||
createdCallback: ->
|
||||
@classList.add('test-root')
|
||||
setModel: (@model) ->
|
||||
TestPanelContainerItemElement = document.registerElement 'atom-test-container-item-element', prototype: TestPanelContainerItemElement.prototype
|
||||
|
||||
beforeEach ->
|
||||
jasmineContent = document.body.querySelector('#jasmine-content')
|
||||
|
||||
viewRegistry = new ViewRegistry
|
||||
viewRegistry.addViewProvider
|
||||
modelConstructor: Panel
|
||||
viewConstructor: PanelElement
|
||||
viewRegistry.addViewProvider
|
||||
modelConstructor: PanelContainer
|
||||
viewConstructor: PanelContainerElement
|
||||
viewRegistry.addViewProvider
|
||||
modelConstructor: TestPanelContainerItem
|
||||
viewConstructor: TestPanelContainerItemElement
|
||||
|
||||
container = new PanelContainer({viewRegistry, location: 'left'})
|
||||
element = container.getView()
|
||||
jasmineContent.appendChild(element)
|
||||
|
||||
it 'has a location attribute with value from the model', ->
|
||||
expect(element.getAttribute('location')).toBe 'left'
|
||||
|
||||
it 'removes the element when the container is destroyed', ->
|
||||
expect(element.parentNode).toBe jasmineContent
|
||||
container.destroy()
|
||||
expect(element.parentNode).not.toBe jasmineContent
|
||||
|
||||
describe "adding and removing panels", ->
|
||||
it "adds atom-panel elements when a new panel is added to the container; removes them when the panels are destroyed", ->
|
||||
expect(element.childNodes.length).toBe 0
|
||||
|
||||
panel1 = new Panel({viewRegistry, item: new TestPanelContainerItem(), location: 'left'})
|
||||
container.addPanel(panel1)
|
||||
expect(element.childNodes.length).toBe 1
|
||||
|
||||
expect(element.childNodes[0].tagName).toBe 'ATOM-PANEL'
|
||||
|
||||
panel2 = new Panel({viewRegistry, item: new TestPanelContainerItem(), location: 'left'})
|
||||
container.addPanel(panel2)
|
||||
expect(element.childNodes.length).toBe 2
|
||||
|
||||
panel1.destroy()
|
||||
expect(element.childNodes.length).toBe 1
|
||||
|
||||
panel2.destroy()
|
||||
expect(element.childNodes.length).toBe 0
|
||||
@@ -0,0 +1,98 @@
|
||||
ViewRegistry = require '../src/view-registry'
|
||||
Panel = require '../src/panel'
|
||||
PanelContainer = require '../src/panel-container'
|
||||
|
||||
describe "PanelContainer", ->
|
||||
[container, viewRegistry] = []
|
||||
|
||||
class TestPanelItem
|
||||
constructior: ->
|
||||
|
||||
beforeEach ->
|
||||
viewRegistry = new ViewRegistry
|
||||
container = new PanelContainer({viewRegistry})
|
||||
|
||||
describe "::addPanel(panel)", ->
|
||||
it 'emits an onDidAddPanel event with the index the panel was inserted at', ->
|
||||
container.onDidAddPanel addPanelSpy = jasmine.createSpy()
|
||||
|
||||
panel1 = new Panel(item: new TestPanelItem())
|
||||
container.addPanel(panel1)
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0})
|
||||
|
||||
panel2 = new Panel(item: new TestPanelItem())
|
||||
container.addPanel(panel2)
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1})
|
||||
|
||||
describe "when a panel is destroyed", ->
|
||||
it 'emits an onDidRemovePanel event with the index of the removed item', ->
|
||||
container.onDidRemovePanel removePanelSpy = jasmine.createSpy()
|
||||
|
||||
panel1 = new Panel(item: new TestPanelItem())
|
||||
container.addPanel(panel1)
|
||||
panel2 = new Panel(item: new TestPanelItem())
|
||||
container.addPanel(panel2)
|
||||
|
||||
expect(removePanelSpy).not.toHaveBeenCalled()
|
||||
|
||||
panel2.destroy()
|
||||
expect(removePanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1})
|
||||
|
||||
panel1.destroy()
|
||||
expect(removePanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0})
|
||||
|
||||
describe "panel priority", ->
|
||||
describe 'left / top panel container', ->
|
||||
[initialPanel] = []
|
||||
beforeEach ->
|
||||
# 'left' logic is the same as 'top'
|
||||
container = new PanelContainer({viewRegistry, location: 'left'})
|
||||
initialPanel = new Panel(item: new TestPanelItem())
|
||||
container.addPanel(initialPanel)
|
||||
|
||||
describe 'when a panel with low priority is added', ->
|
||||
it 'is inserted at the beginning of the list', ->
|
||||
container.onDidAddPanel addPanelSpy = jasmine.createSpy()
|
||||
panel = new Panel(item: new TestPanelItem(), priority: 0)
|
||||
container.addPanel(panel)
|
||||
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
|
||||
expect(container.getPanels()[0]).toBe panel
|
||||
|
||||
describe 'when a panel with priority between two other panels is added', ->
|
||||
it 'is inserted at the between the two panels', ->
|
||||
panel = new Panel(item: new TestPanelItem(), priority: 1000)
|
||||
container.addPanel(panel)
|
||||
|
||||
container.onDidAddPanel addPanelSpy = jasmine.createSpy()
|
||||
panel = new Panel(item: new TestPanelItem(), priority: 101)
|
||||
container.addPanel(panel)
|
||||
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 1})
|
||||
expect(container.getPanels()[1]).toBe panel
|
||||
|
||||
describe 'right / bottom panel container', ->
|
||||
[initialPanel] = []
|
||||
beforeEach ->
|
||||
# 'bottom' logic is the same as 'right'
|
||||
container = new PanelContainer({viewRegistry, location: 'right'})
|
||||
initialPanel = new Panel(item: new TestPanelItem())
|
||||
container.addPanel(initialPanel)
|
||||
|
||||
describe 'when a panel with high priority is added', ->
|
||||
it 'is inserted at the beginning of the list', ->
|
||||
container.onDidAddPanel addPanelSpy = jasmine.createSpy()
|
||||
panel = new Panel(item: new TestPanelItem(), priority: 1000)
|
||||
container.addPanel(panel)
|
||||
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
|
||||
expect(container.getPanels()[0]).toBe panel
|
||||
|
||||
describe 'when a panel with low priority is added', ->
|
||||
it 'is inserted at the end of the list', ->
|
||||
container.onDidAddPanel addPanelSpy = jasmine.createSpy()
|
||||
panel = new Panel(item: new TestPanelItem(), priority: 0)
|
||||
container.addPanel(panel)
|
||||
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 1})
|
||||
expect(container.getPanels()[1]).toBe panel
|
||||
@@ -0,0 +1,56 @@
|
||||
ViewRegistry = require '../src/view-registry'
|
||||
Panel = require '../src/panel'
|
||||
PanelElement = require '../src/panel-element'
|
||||
|
||||
describe "PanelElement", ->
|
||||
[jasmineContent, element, panel, viewRegistry] = []
|
||||
|
||||
class TestPanelItem
|
||||
constructior: ->
|
||||
|
||||
class TestPanelItemElement extends HTMLElement
|
||||
createdCallback: ->
|
||||
@classList.add('test-root')
|
||||
setModel: (@model) ->
|
||||
TestPanelItemElement = document.registerElement 'atom-test-item-element', prototype: TestPanelItemElement.prototype
|
||||
|
||||
beforeEach ->
|
||||
jasmineContent = document.body.querySelector('#jasmine-content')
|
||||
|
||||
viewRegistry = new ViewRegistry
|
||||
viewRegistry.addViewProvider
|
||||
modelConstructor: Panel
|
||||
viewConstructor: PanelElement
|
||||
viewRegistry.addViewProvider
|
||||
modelConstructor: TestPanelItem
|
||||
viewConstructor: TestPanelItemElement
|
||||
|
||||
it 'removes the element when the panel is destroyed', ->
|
||||
panel = new Panel({viewRegistry, item: new TestPanelItem})
|
||||
element = panel.getView()
|
||||
jasmineContent.appendChild(element)
|
||||
|
||||
expect(element.parentNode).toBe jasmineContent
|
||||
panel.destroy()
|
||||
expect(element.parentNode).not.toBe jasmineContent
|
||||
|
||||
describe "changing panel visibility", ->
|
||||
it 'initially renders panel created with visibile: false', ->
|
||||
panel = new Panel({viewRegistry, visible: false, item: new TestPanelItem})
|
||||
element = panel.getView()
|
||||
jasmineContent.appendChild(element)
|
||||
|
||||
expect(element.style.display).toBe 'none'
|
||||
|
||||
it 'hides and shows the panel element when Panel::hide() and Panel::show() are called', ->
|
||||
panel = new Panel({viewRegistry, item: new TestPanelItem})
|
||||
element = panel.getView()
|
||||
jasmineContent.appendChild(element)
|
||||
|
||||
expect(element.style.display).not.toBe 'none'
|
||||
|
||||
panel.hide()
|
||||
expect(element.style.display).toBe 'none'
|
||||
|
||||
panel.show()
|
||||
expect(element.style.display).not.toBe 'none'
|
||||
@@ -0,0 +1,23 @@
|
||||
Panel = require '../src/panel'
|
||||
|
||||
describe "Panel", ->
|
||||
[panel] = []
|
||||
|
||||
class TestPanelItem
|
||||
constructior: ->
|
||||
|
||||
beforeEach ->
|
||||
panel = new Panel(item: new TestPanelItem())
|
||||
|
||||
describe "changing panel visibility", ->
|
||||
it 'emits an event when visibility changes', ->
|
||||
panel.onDidChangeVisible spy = jasmine.createSpy()
|
||||
|
||||
panel.hide()
|
||||
expect(panel.isVisible()).toBe false
|
||||
expect(spy).toHaveBeenCalledWith(false)
|
||||
spy.reset()
|
||||
|
||||
panel.show()
|
||||
expect(panel.isVisible()).toBe true
|
||||
expect(spy).toHaveBeenCalledWith(true)
|
||||
@@ -29,6 +29,7 @@ atom.packages.packageDirPaths.unshift(fixturePackagesPath)
|
||||
atom.keymaps.loadBundledKeymaps()
|
||||
keyBindingsToRestore = atom.keymaps.getKeyBindings()
|
||||
commandsToRestore = atom.commands.getSnapshot()
|
||||
styleElementsToRestore = atom.styles.getSnapshot()
|
||||
|
||||
window.addEventListener 'core:close', -> window.close()
|
||||
window.addEventListener 'beforeunload', ->
|
||||
@@ -44,8 +45,7 @@ Object.defineProperty document, 'title',
|
||||
|
||||
jasmine.getEnv().addEqualityTester(_.isEqual) # Use underscore's definition of equality for toEqual assertions
|
||||
|
||||
if process.platform is 'win32' and process.env.JANKY_SHA1
|
||||
# Use longer timeout on Windows CI
|
||||
if process.env.JANKY_SHA1 and process.platform is 'win32'
|
||||
jasmine.getEnv().defaultTimeoutInterval = 60000
|
||||
else
|
||||
jasmine.getEnv().defaultTimeoutInterval = 5000
|
||||
@@ -74,6 +74,7 @@ beforeEach ->
|
||||
atom.workspace = new Workspace()
|
||||
atom.keymaps.keyBindings = _.clone(keyBindingsToRestore)
|
||||
atom.commands.restoreSnapshot(commandsToRestore)
|
||||
atom.styles.restoreSnapshot(styleElementsToRestore)
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.packages.packageStates = {}
|
||||
@@ -224,7 +225,7 @@ addCustomMatchers = (spec) ->
|
||||
notText = if @isNot then " not" else ""
|
||||
element = @actual
|
||||
element = element.get(0) if element.jquery
|
||||
@message = -> return "Expected element '#{element}' or its descendants #{notText} to show."
|
||||
@message = -> return "Expected element '#{element}' or its descendants#{notText} to show."
|
||||
element.style.display in ['block', 'inline-block', 'static', 'fixed']
|
||||
|
||||
window.keyIdentifierForKey = (key) ->
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
StyleManager = require '../src/style-manager'
|
||||
|
||||
describe "StyleManager", ->
|
||||
[manager, addEvents, removeEvents, updateEvents] = []
|
||||
|
||||
beforeEach ->
|
||||
manager = new StyleManager
|
||||
addEvents = []
|
||||
removeEvents = []
|
||||
updateEvents = []
|
||||
|
||||
manager.onDidAddStyleElement (event) -> addEvents.push(event)
|
||||
manager.onDidRemoveStyleElement (event) -> removeEvents.push(event)
|
||||
manager.onDidUpdateStyleElement (event) -> updateEvents.push(event)
|
||||
|
||||
describe "::addStyleSheet(source, params)", ->
|
||||
it "adds a stylesheet based on the given source and returns a disposable allowing it to be removed", ->
|
||||
disposable = manager.addStyleSheet("a {color: red;}")
|
||||
|
||||
expect(addEvents.length).toBe 1
|
||||
expect(addEvents[0].textContent).toBe "a {color: red;}"
|
||||
|
||||
styleElements = manager.getStyleElements()
|
||||
expect(styleElements.length).toBe 1
|
||||
expect(styleElements[0].textContent).toBe "a {color: red;}"
|
||||
|
||||
disposable.dispose()
|
||||
|
||||
expect(removeEvents.length).toBe 1
|
||||
expect(removeEvents[0].textContent).toBe "a {color: red;}"
|
||||
expect(manager.getStyleElements().length).toBe 0
|
||||
|
||||
describe "when a sourcePath parameter is specified", ->
|
||||
it "ensures a maximum of one style element for the given source path, updating a previous if it exists", ->
|
||||
disposable1 = manager.addStyleSheet("a {color: red;}", sourcePath: '/foo/bar')
|
||||
|
||||
expect(addEvents.length).toBe 1
|
||||
expect(addEvents[0].getAttribute('source-path')).toBe '/foo/bar'
|
||||
|
||||
disposable2 = manager.addStyleSheet("a {color: blue;}", sourcePath: '/foo/bar')
|
||||
|
||||
expect(addEvents.length).toBe 1
|
||||
expect(updateEvents.length).toBe 1
|
||||
expect(updateEvents[0].getAttribute('source-path')).toBe '/foo/bar'
|
||||
expect(updateEvents[0].textContent).toBe "a {color: blue;}"
|
||||
|
||||
disposable2.dispose()
|
||||
addEvents = []
|
||||
|
||||
manager.addStyleSheet("a {color: yellow;}", sourcePath: '/foo/bar')
|
||||
|
||||
expect(addEvents.length).toBe 1
|
||||
expect(addEvents[0].getAttribute('source-path')).toBe '/foo/bar'
|
||||
expect(addEvents[0].textContent).toBe "a {color: yellow;}"
|
||||
|
||||
describe "when a group parameter is specified", ->
|
||||
it "inserts the stylesheet at the end of any existing stylesheets for the same group", ->
|
||||
manager.addStyleSheet("a {color: red}", group: 'a')
|
||||
manager.addStyleSheet("a {color: blue}", group: 'b')
|
||||
manager.addStyleSheet("a {color: green}", group: 'a')
|
||||
|
||||
expect(manager.getStyleElements().map (elt) -> elt.textContent).toEqual [
|
||||
"a {color: red}"
|
||||
"a {color: green}"
|
||||
"a {color: blue}"
|
||||
]
|
||||
@@ -0,0 +1,54 @@
|
||||
StylesElement = require '../src/styles-element'
|
||||
StyleManager = require '../src/style-manager'
|
||||
|
||||
describe "StylesElement", ->
|
||||
[element, addedStyleElements, removedStyleElements, updatedStyleElements] = []
|
||||
|
||||
beforeEach ->
|
||||
element = new StylesElement
|
||||
document.querySelector('#jasmine-content').appendChild(element)
|
||||
addedStyleElements = []
|
||||
removedStyleElements = []
|
||||
updatedStyleElements = []
|
||||
element.onDidAddStyleElement (element) -> addedStyleElements.push(element)
|
||||
element.onDidRemoveStyleElement (element) -> removedStyleElements.push(element)
|
||||
element.onDidUpdateStyleElement (element) -> updatedStyleElements.push(element)
|
||||
|
||||
it "renders a style tag for all currently active stylesheets in the style manager", ->
|
||||
initialChildCount = element.children.length
|
||||
|
||||
disposable1 = atom.styles.addStyleSheet("a {color: red;}")
|
||||
expect(element.children.length).toBe initialChildCount + 1
|
||||
expect(element.children[initialChildCount].textContent).toBe "a {color: red;}"
|
||||
expect(addedStyleElements).toEqual [element.children[initialChildCount]]
|
||||
|
||||
disposable2 = atom.styles.addStyleSheet("a {color: blue;}")
|
||||
expect(element.children.length).toBe initialChildCount + 2
|
||||
expect(element.children[initialChildCount + 1].textContent).toBe "a {color: blue;}"
|
||||
expect(addedStyleElements).toEqual [element.children[initialChildCount], element.children[initialChildCount + 1]]
|
||||
|
||||
disposable1.dispose()
|
||||
expect(element.children.length).toBe initialChildCount + 1
|
||||
expect(element.children[initialChildCount].textContent).toBe "a {color: blue;}"
|
||||
expect(removedStyleElements).toEqual [addedStyleElements[0]]
|
||||
|
||||
it "orders style elements by group", ->
|
||||
initialChildCount = element.children.length
|
||||
|
||||
atom.styles.addStyleSheet("a {color: red}", group: 'a')
|
||||
atom.styles.addStyleSheet("a {color: blue}", group: 'b')
|
||||
atom.styles.addStyleSheet("a {color: green}", group: 'a')
|
||||
|
||||
expect(element.children[initialChildCount].textContent).toBe "a {color: red}"
|
||||
expect(element.children[initialChildCount + 1].textContent).toBe "a {color: green}"
|
||||
expect(element.children[initialChildCount + 2].textContent).toBe "a {color: blue}"
|
||||
|
||||
it "updates existing style nodes when style elements are updated", ->
|
||||
initialChildCount = element.children.length
|
||||
|
||||
atom.styles.addStyleSheet("a {color: red;}", sourcePath: '/foo/bar')
|
||||
atom.styles.addStyleSheet("a {color: blue;}", sourcePath: '/foo/bar')
|
||||
|
||||
expect(element.children.length).toBe initialChildCount + 1
|
||||
expect(element.children[initialChildCount].textContent).toBe "a {color: blue;}"
|
||||
expect(updatedStyleElements).toEqual [element.children[initialChildCount]]
|
||||
@@ -1230,6 +1230,22 @@ describe "TextEditorComponent", ->
|
||||
beforeEach ->
|
||||
linesNode = componentNode.querySelector('.lines')
|
||||
|
||||
describe "when the mouse is single-clicked below the last line", ->
|
||||
it "moves the cursor to the end of file buffer position", ->
|
||||
editor.setText('foo')
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
height = 4.5 * lineHeightInPixels
|
||||
wrapperNode.style.height = height + 'px'
|
||||
wrapperNode.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
nextAnimationFrame()
|
||||
|
||||
coordinates = clientCoordinatesForScreenPosition([0, 2])
|
||||
coordinates.clientY = height * 2
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', coordinates))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 3]
|
||||
|
||||
describe "when a non-folded line is single-clicked", ->
|
||||
describe "when no modifier keys are held down", ->
|
||||
it "moves the cursor to the nearest screen position", ->
|
||||
|
||||
@@ -3412,9 +3412,12 @@ describe "TextEditor", ->
|
||||
describe "when no text is selected", ->
|
||||
describe "when the line below isn't empty", ->
|
||||
it "joins the line below with the current line separated by a space and moves the cursor to the start of line that was moved up", ->
|
||||
editor.setCursorBufferPosition([0, Infinity])
|
||||
editor.insertText(' ')
|
||||
editor.setCursorBufferPosition([0])
|
||||
editor.joinLines()
|
||||
expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () { var sort = function(items) {'
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 30]
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 29]
|
||||
|
||||
describe "when the line below is empty", ->
|
||||
it "deletes the line below and moves the cursor to the end of the line", ->
|
||||
@@ -3430,6 +3433,13 @@ describe "TextEditor", ->
|
||||
editor.joinLines()
|
||||
expect(editor.lineTextForBufferRow(12)).toBe '};'
|
||||
|
||||
describe "when the line is empty", ->
|
||||
it "joins the line below with the current line with no added space", ->
|
||||
editor.setCursorBufferPosition([10])
|
||||
editor.joinLines()
|
||||
expect(editor.lineTextForBufferRow(10)).toBe 'return sort(Array.apply(this, arguments));'
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
|
||||
|
||||
describe "when text is selected", ->
|
||||
describe "when the selection does not span multiple lines", ->
|
||||
it "joins the line below with the current line separated by a space and retains the selected text", ->
|
||||
@@ -3650,10 +3660,10 @@ describe "TextEditor", ->
|
||||
{tokens} = grammar.tokenizeLine("var i; // http://github.com")
|
||||
|
||||
expect(tokens[0].value).toBe "var"
|
||||
expect(tokens[0].scopeDescriptor).toEqual ["source.js", "storage.modifier.js"]
|
||||
expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"]
|
||||
|
||||
expect(tokens[6].value).toBe "http://github.com"
|
||||
expect(tokens[6].scopeDescriptor).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is added", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
@@ -3665,7 +3675,7 @@ describe "TextEditor", ->
|
||||
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopeDescriptor).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-hyperlink')
|
||||
@@ -3673,7 +3683,7 @@ describe "TextEditor", ->
|
||||
runs ->
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "http://github.com"
|
||||
expect(tokens[2].scopeDescriptor).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is updated", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
@@ -3685,7 +3695,7 @@ describe "TextEditor", ->
|
||||
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopeDescriptor).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-injection-selector')
|
||||
@@ -3693,7 +3703,7 @@ describe "TextEditor", ->
|
||||
runs ->
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopeDescriptor).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-sql')
|
||||
@@ -3701,7 +3711,7 @@ describe "TextEditor", ->
|
||||
runs ->
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "SELECT"
|
||||
expect(tokens[2].scopeDescriptor).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
|
||||
describe ".normalizeTabsInBufferRange()", ->
|
||||
it "normalizes tabs depending on the editor's soft tab/tab length settings", ->
|
||||
|
||||
@@ -92,8 +92,8 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
reloadHandler.reset()
|
||||
expect($('style.theme')).toHaveLength 1
|
||||
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
|
||||
expect($('style[group=theme]')).toHaveLength 1
|
||||
expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-syntax/
|
||||
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -101,9 +101,9 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
reloadHandler.reset()
|
||||
expect($('style.theme')).toHaveLength 2
|
||||
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
|
||||
expect($('style.theme:eq(1)').attr('id')).toMatch /atom-light-syntax/
|
||||
expect($('style[group=theme]')).toHaveLength 2
|
||||
expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-syntax/
|
||||
expect($('style[group=theme]:eq(1)').attr('source-path')).toMatch /atom-light-syntax/
|
||||
atom.config.set('core.themes', [])
|
||||
|
||||
waitsFor ->
|
||||
@@ -111,7 +111,7 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
reloadHandler.reset()
|
||||
expect($('style.theme')).toHaveLength 0
|
||||
expect($('style[group=theme]')).toHaveLength 0
|
||||
# atom-dark-ui has an directory path, the syntax one doesn't
|
||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
|
||||
|
||||
@@ -119,7 +119,7 @@ describe "ThemeManager", ->
|
||||
reloadHandler.callCount == 1
|
||||
|
||||
runs ->
|
||||
expect($('style.theme')).toHaveLength 2
|
||||
expect($('style[group=theme]')).toHaveLength 2
|
||||
importPaths = themeManager.getImportPaths()
|
||||
expect(importPaths.length).toBe 1
|
||||
expect(importPaths[0]).toContain 'atom-dark-ui'
|
||||
@@ -142,8 +142,8 @@ describe "ThemeManager", ->
|
||||
expect(stylesheetAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
element = $('head style[id*="css.css"]')
|
||||
expect(element.attr('id')).toBe themeManager.stringToId(cssPath)
|
||||
element = $('head style[source-path*="css.css"]')
|
||||
expect(element.attr('source-path')).toBe themeManager.stringToId(cssPath)
|
||||
expect(element.text()).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
expect(element[0].sheet).toBe stylesheetAddedHandler.argsForCall[0][0]
|
||||
|
||||
@@ -159,8 +159,8 @@ describe "ThemeManager", ->
|
||||
themeManager.requireStylesheet(lessPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
|
||||
element = $('head style[id*="sample.less"]')
|
||||
expect(element.attr('id')).toBe themeManager.stringToId(lessPath)
|
||||
element = $('head style[source-path*="sample.less"]')
|
||||
expect(element.attr('source-path')).toBe themeManager.stringToId(lessPath)
|
||||
expect(element.text()).toBe """
|
||||
#header {
|
||||
color: #4d926f;
|
||||
@@ -178,9 +178,9 @@ describe "ThemeManager", ->
|
||||
|
||||
it "supports requiring css and less stylesheets without an explicit extension", ->
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'css')
|
||||
expect($('head style[id*="css.css"]').attr('id')).toBe themeManager.stringToId(atom.project.resolve('css.css'))
|
||||
expect($('head style[source-path*="css.css"]').attr('source-path')).toBe themeManager.stringToId(atom.project.resolve('css.css'))
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'sample')
|
||||
expect($('head style[id*="sample.less"]').attr('id')).toBe themeManager.stringToId(atom.project.resolve('sample.less'))
|
||||
expect($('head style[source-path*="sample.less"]').attr('source-path')).toBe themeManager.stringToId(atom.project.resolve('sample.less'))
|
||||
|
||||
$('head style[id*="css.css"]').remove()
|
||||
$('head style[id*="sample.less"]').remove()
|
||||
|
||||
@@ -51,12 +51,12 @@ describe "TokenizedBuffer", ->
|
||||
it "initially creates un-tokenized screen lines, then tokenizes lines chunk at a time in the background", ->
|
||||
line0 = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
expect(line0.tokens.length).toBe 1
|
||||
expect(line0.tokens[0]).toEqual(value: line0.text, scopeDescriptor: ['source.js'])
|
||||
expect(line0.tokens[0]).toEqual(value: line0.text, scopes: ['source.js'])
|
||||
|
||||
line11 = tokenizedBuffer.tokenizedLineForRow(11)
|
||||
expect(line11.tokens.length).toBe 2
|
||||
expect(line11.tokens[0]).toEqual(value: " ", scopeDescriptor: ['source.js'], isAtomic: true)
|
||||
expect(line11.tokens[1]).toEqual(value: "return sort(Array.apply(this, arguments));", scopeDescriptor: ['source.js'])
|
||||
expect(line11.tokens[0]).toEqual(value: " ", scopes: ['source.js'], isAtomic: true)
|
||||
expect(line11.tokens[1]).toEqual(value: "return sort(Array.apply(this, arguments));", scopes: ['source.js'])
|
||||
|
||||
# background tokenization has not begun
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack).toBeUndefined()
|
||||
@@ -149,10 +149,10 @@ describe "TokenizedBuffer", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.setTextInRange([[0, 0], [2, 0]], "foo()\n7\n")
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1]).toEqual(value: '(', scopeDescriptor: ['source.js', 'meta.brace.round.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: '7', scopeDescriptor: ['source.js', 'constant.numeric.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.brace.round.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.js'])
|
||||
# line 2 is unchanged
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'if', scopeDescriptor: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -164,7 +164,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.insert([5, 30], '/* */')
|
||||
changeHandler.reset()
|
||||
buffer.insert([2, 0], '/*')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopeDescriptor).toEqual ['source.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
@@ -172,9 +172,9 @@ describe "TokenizedBuffer", ->
|
||||
changeHandler.reset()
|
||||
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
@@ -185,23 +185,23 @@ describe "TokenizedBuffer", ->
|
||||
buffer.insert([5, 0], '*/')
|
||||
|
||||
buffer.insert([1, 0], 'var ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
|
||||
describe "when lines are both updated and removed", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.setTextInRange([[1, 0], [3, 0]], "foo()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopeDescriptor: ['source.js', 'storage.modifier.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
|
||||
# previous line 3 should be combined with input to form line 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopeDescriptor: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[6]).toEqual(value: '=', scopeDescriptor: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
# lines below deleted regions should be shifted upward
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'while', scopeDescriptor: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[4]).toEqual(value: '=', scopeDescriptor: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: '<', scopeDescriptor: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -214,8 +214,8 @@ describe "TokenizedBuffer", ->
|
||||
changeHandler.reset()
|
||||
|
||||
buffer.setTextInRange([[2, 0], [3, 0]], '/*')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopeDescriptor).toEqual ['source.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
@@ -223,8 +223,8 @@ describe "TokenizedBuffer", ->
|
||||
changeHandler.reset()
|
||||
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
@@ -235,19 +235,19 @@ describe "TokenizedBuffer", ->
|
||||
buffer.setTextInRange([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopeDescriptor: ['source.js', 'storage.modifier.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
|
||||
# 3 new lines inserted
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopeDescriptor: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0]).toEqual(value: 'bar', scopeDescriptor: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0]).toEqual(value: 'baz', scopeDescriptor: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0]).toEqual(value: 'bar', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0]).toEqual(value: 'baz', scopes: ['source.js'])
|
||||
|
||||
# previous line 2 is joined with quux() on line 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0]).toEqual(value: 'quux', scopeDescriptor: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: 'if', scopeDescriptor: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0]).toEqual(value: 'quux', scopes: ['source.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
# previous line 3 is pushed down to become line 5
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4]).toEqual(value: '=', scopeDescriptor: ['source.js', 'keyword.operator.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -264,17 +264,17 @@ describe "TokenizedBuffer", ->
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
expect(event).toEqual(start: 2, end: 2, delta: 2)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopeDescriptor).toEqual ['source.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js']
|
||||
changeHandler.reset()
|
||||
|
||||
advanceClock() # tokenize invalidated lines in background
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).tokens[0].scopeDescriptor).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).tokens[0].scopeDescriptor).not.toBe ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).tokens[0].scopes).not.toBe ['source.js', 'comment.block.js']
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -343,7 +343,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokens[0].value).toBe "#"
|
||||
expect(tokens[1].value).toBe " Econ 101"
|
||||
expect(tokens[2].value).toBe tabAsSpaces
|
||||
expect(tokens[2].scopeDescriptor).toEqual tokens[1].scopeDescriptor
|
||||
expect(tokens[2].scopes).toEqual tokens[1].scopes
|
||||
expect(tokens[2].isAtomic).toBeTruthy()
|
||||
expect(tokens[3].value).toBe ""
|
||||
|
||||
@@ -526,7 +526,7 @@ describe "TokenizedBuffer", ->
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopeDescriptor: ["text.html.ruby"]
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-html')
|
||||
@@ -534,7 +534,7 @@ describe "TokenizedBuffer", ->
|
||||
runs ->
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
expect(tokens[0]).toEqual value: '<', scopeDescriptor: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
|
||||
describe ".tokenForPosition(position)", ->
|
||||
afterEach ->
|
||||
@@ -545,9 +545,9 @@ describe "TokenizedBuffer", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([1,0]).scopeDescriptor).toEqual ["source.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1,1]).scopeDescriptor).toEqual ["source.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1,2]).scopeDescriptor).toEqual ["source.js", "storage.modifier.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1,0]).scopes).toEqual ["source.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1,1]).scopes).toEqual ["source.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1,2]).scopes).toEqual ["source.js", "storage.modifier.js"]
|
||||
|
||||
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -28,7 +28,7 @@ describe "TokenizedLine", ->
|
||||
ensureValidScopeTree(child, scopeDescriptor.concat([scopeTree.scope]))
|
||||
else
|
||||
expect(scopeTree).toBe tokens[tokenIndex++]
|
||||
expect(scopeDescriptor).toEqual scopeTree.scopeDescriptor
|
||||
expect(scopeDescriptor).toEqual scopeTree.scopes
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('coffee.coffee').then (o) -> editor = o
|
||||
|
||||
@@ -456,3 +456,39 @@ describe "Workspace", ->
|
||||
|
||||
expect(item2.isModified()).toBe false
|
||||
expect(atom.setDocumentEdited).toHaveBeenCalledWith(false)
|
||||
|
||||
describe "adding panels", ->
|
||||
class TestPanel
|
||||
constructior: ->
|
||||
|
||||
describe '::addLeftPanel(model)', ->
|
||||
it 'adds a panel to the correct panel container', ->
|
||||
atom.workspace.panelContainers.left.onDidAddPanel addPanelSpy = jasmine.createSpy()
|
||||
panel = atom.workspace.addLeftPanel(item: new TestPanel())
|
||||
|
||||
expect(panel).toBeDefined()
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
|
||||
|
||||
describe '::addRightPanel(model)', ->
|
||||
it 'adds a panel to the correct panel container', ->
|
||||
atom.workspace.panelContainers.right.onDidAddPanel addPanelSpy = jasmine.createSpy()
|
||||
panel = atom.workspace.addRightPanel(item: new TestPanel())
|
||||
|
||||
expect(panel).toBeDefined()
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
|
||||
|
||||
describe '::addTopPanel(model)', ->
|
||||
it 'adds a panel to the correct panel container', ->
|
||||
atom.workspace.panelContainers.top.onDidAddPanel addPanelSpy = jasmine.createSpy()
|
||||
panel = atom.workspace.addTopPanel(item: new TestPanel())
|
||||
|
||||
expect(panel).toBeDefined()
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
|
||||
|
||||
describe '::addBottomPanel(model)', ->
|
||||
it 'adds a panel to the correct panel container', ->
|
||||
atom.workspace.panelContainers.bottom.onDidAddPanel addPanelSpy = jasmine.createSpy()
|
||||
panel = atom.workspace.addBottomPanel(item: new TestPanel())
|
||||
|
||||
expect(panel).toBeDefined()
|
||||
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
|
||||
|
||||
@@ -270,3 +270,19 @@ describe "WorkspaceView", ->
|
||||
atom.config.set('editor.lineHeight', '30px')
|
||||
expect(getComputedStyle(editorNode).lineHeight).toBe atom.config.get('editor.lineHeight')
|
||||
expect(editor.getLineHeightInPixels()).not.toBe initialLineHeight
|
||||
|
||||
describe 'panel containers', ->
|
||||
workspaceElement = null
|
||||
beforeEach ->
|
||||
workspaceElement = atom.workspace.getView(atom.workspace)
|
||||
|
||||
it 'inserts panel container elements in the correct places in the DOM', ->
|
||||
leftContainer = workspaceElement.querySelector('atom-panel-container[location="left"]')
|
||||
rightContainer = workspaceElement.querySelector('atom-panel-container[location="right"]')
|
||||
expect(leftContainer.nextSibling).toBe workspaceElement.verticalAxis
|
||||
expect(rightContainer.previousSibling).toBe workspaceElement.verticalAxis
|
||||
|
||||
topContainer = workspaceElement.querySelector('atom-panel-container[location="top"]')
|
||||
bottomContainer = workspaceElement.querySelector('atom-panel-container[location="bottom"]')
|
||||
expect(topContainer.nextSibling).toBe workspaceElement.paneContainer
|
||||
expect(bottomContainer.previousSibling).toBe workspaceElement.paneContainer
|
||||
|
||||
+12
-2
@@ -14,6 +14,7 @@ fs = require 'fs-plus'
|
||||
|
||||
{$} = require './space-pen-extensions'
|
||||
WindowEventHandler = require './window-event-handler'
|
||||
StylesElement = require './styles-element'
|
||||
|
||||
# Essential: Atom global for dealing with packages, themes, menus, and the window.
|
||||
#
|
||||
@@ -29,7 +30,10 @@ class Atom extends Model
|
||||
#
|
||||
# Returns an Atom instance, fully initialized
|
||||
@loadOrCreate: (mode) ->
|
||||
@deserialize(@loadState(mode)) ? new this({mode, @version})
|
||||
startTime = Date.now()
|
||||
atom = @deserialize(@loadState(mode)) ? new this({mode, @version})
|
||||
atom.deserializeTimings.atom = Date.now() - startTime
|
||||
atom
|
||||
|
||||
# Deserializes the Atom environment from a state object
|
||||
@deserialize: (state) ->
|
||||
@@ -152,6 +156,7 @@ class Atom extends Model
|
||||
{@mode} = @state
|
||||
DeserializerManager = require './deserializer-manager'
|
||||
@deserializers = new DeserializerManager()
|
||||
@deserializeTimings = {}
|
||||
|
||||
# Sets up the basic services that should be available in all modes
|
||||
# (both spec and application).
|
||||
@@ -182,6 +187,7 @@ class Atom extends Model
|
||||
Clipboard = require './clipboard'
|
||||
Syntax = require './syntax'
|
||||
ThemeManager = require './theme-manager'
|
||||
StyleManager = require './style-manager'
|
||||
ContextMenuManager = require './context-menu-manager'
|
||||
MenuManager = require './menu-manager'
|
||||
{devMode, safeMode, resourcePath} = @getLoadSettings()
|
||||
@@ -196,11 +202,16 @@ class Atom extends Model
|
||||
# Make react.js faster
|
||||
process.env.NODE_ENV ?= 'production' unless devMode
|
||||
|
||||
# Set Atom's home so packages don't have to guess it
|
||||
process.env.ATOM_HOME = configDirPath
|
||||
|
||||
@config = new Config({configDirPath, resourcePath})
|
||||
@keymaps = new KeymapManager({configDirPath, resourcePath})
|
||||
@keymap = @keymaps # Deprecated
|
||||
@commands = new CommandRegistry
|
||||
@packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode})
|
||||
@styles = new StyleManager
|
||||
document.head.appendChild(new StylesElement)
|
||||
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath, safeMode})
|
||||
@contextMenu = new ContextMenuManager({resourcePath, devMode})
|
||||
@menu = new MenuManager({resourcePath})
|
||||
@@ -597,7 +608,6 @@ class Atom extends Model
|
||||
delete @state.packageStates
|
||||
|
||||
deserializeEditorWindow: ->
|
||||
@deserializeTimings = {}
|
||||
@deserializePackageStates()
|
||||
@deserializeProject()
|
||||
@deserializeWorkspaceView()
|
||||
|
||||
@@ -10,24 +10,49 @@ _ = require 'underscore-plus'
|
||||
module.exports =
|
||||
class ApplicationMenu
|
||||
constructor: (@version) ->
|
||||
@menu = Menu.buildFromTemplate @getDefaultTemplate()
|
||||
Menu.setApplicationMenu @menu
|
||||
@windowTemplates = new WeakMap()
|
||||
@setActiveTemplate(@getDefaultTemplate())
|
||||
global.atomApplication.autoUpdateManager.on 'state-changed', (state) =>
|
||||
@showUpdateMenuItem(state)
|
||||
|
||||
# Public: Updates the entire menu with the given keybindings.
|
||||
#
|
||||
# window - The BrowserWindow this menu template is associated with.
|
||||
# template - The Object which describes the menu to display.
|
||||
# keystrokesByCommand - An Object where the keys are commands and the values
|
||||
# are Arrays containing the keystroke.
|
||||
update: (template, keystrokesByCommand) ->
|
||||
update: (window, template, keystrokesByCommand) ->
|
||||
return unless window is @lastFocusedWindow
|
||||
|
||||
@translateTemplate(template, keystrokesByCommand)
|
||||
@substituteVersion(template)
|
||||
@menu = Menu.buildFromTemplate(template)
|
||||
Menu.setApplicationMenu(@menu)
|
||||
@windowTemplates.set(window, template)
|
||||
@setActiveTemplate(template)
|
||||
|
||||
@showUpdateMenuItem(global.atomApplication.autoUpdateManager.getState())
|
||||
|
||||
setActiveTemplate: (template) ->
|
||||
unless _.isEqual(template, @activeTemplate)
|
||||
@activeTemplate = template
|
||||
@menu = Menu.buildFromTemplate(_.deepClone(template))
|
||||
Menu.setApplicationMenu(@menu)
|
||||
|
||||
# Register a BrowserWindow with this application menu.
|
||||
addWindow: (window) ->
|
||||
@lastFocusedWindow ?= window
|
||||
|
||||
focusHandler = =>
|
||||
@lastFocusedWindow = window
|
||||
if template = @windowTemplates.get(window)
|
||||
@setActiveTemplate(template)
|
||||
|
||||
window.on 'focus', focusHandler
|
||||
window.once 'closed', =>
|
||||
@windowTemplates.delete(window)
|
||||
window.removeListener 'focus', focusHandler
|
||||
|
||||
@enableWindowSpecificItems(true)
|
||||
|
||||
# Flattens the given menu and submenu items into an single Array.
|
||||
#
|
||||
# menu - A complete menu configuration object for atom-shell's menu API.
|
||||
|
||||
@@ -97,7 +97,7 @@ class AtomApplication
|
||||
# Public: Adds the {AtomWindow} to the global window list.
|
||||
addWindow: (window) ->
|
||||
@windows.push window
|
||||
@applicationMenu?.enableWindowSpecificItems(true)
|
||||
@applicationMenu?.addWindow(window.browserWindow)
|
||||
window.once 'window:loaded', =>
|
||||
@autoUpdateManager.emitUpdateAvailableEvent(window)
|
||||
|
||||
@@ -215,7 +215,8 @@ class AtomApplication
|
||||
@promptForPath({window})
|
||||
|
||||
ipc.on 'update-application-menu', (event, template, keystrokesByCommand) =>
|
||||
@applicationMenu.update(template, keystrokesByCommand)
|
||||
win = BrowserWindow.fromWebContents(event.sender)
|
||||
@applicationMenu.update(win, template, keystrokesByCommand)
|
||||
|
||||
ipc.on 'run-package-specs', (event, specDirectory) =>
|
||||
@runSpecs({resourcePath: global.devResourcePath, specDirectory: specDirectory, exitWhenDone: false})
|
||||
|
||||
@@ -78,13 +78,18 @@ parseCommandLine = ->
|
||||
|
||||
Folder paths will open in an existing window if that folder has already been
|
||||
opened or a new window if it hasn't.
|
||||
|
||||
Environment Variables:
|
||||
ATOM_DEV_RESOURCE_PATH The path from which Atom loads source code in dev mode.
|
||||
Defaults to `~/github/atom`.
|
||||
"""
|
||||
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.')
|
||||
options.alias('h', 'help').boolean('h').describe('h', 'Print this usage message.')
|
||||
options.alias('l', 'log-file').string('l').describe('l', 'Log all output to file.')
|
||||
options.alias('n', 'new-window').boolean('n').describe('n', 'Open a new window.')
|
||||
options.alias('s', 'spec-directory').string('s').describe('s', 'Set the spec directory (default: Atom\'s spec directory).')
|
||||
options.alias('r', 'resource-path').string('r').describe('r', 'Set the path to the Atom source directory and enable dev-mode.')
|
||||
options.alias('s', 'spec-directory').string('s').describe('s', 'Set the directory from which to run package specs (default: Atom\'s spec directory).')
|
||||
options.boolean('safe').describe('safe', 'Do not load packages from ~/.atom/packages or ~/.atom/dev/packages.')
|
||||
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.')
|
||||
options.alias('v', 'version').boolean('v').describe('v', 'Print the version.')
|
||||
@@ -113,8 +118,18 @@ parseCommandLine = ->
|
||||
if args['resource-path']
|
||||
devMode = true
|
||||
resourcePath = args['resource-path']
|
||||
else if devMode
|
||||
resourcePath = global.devResourcePath
|
||||
else
|
||||
# Set resourcePath based on the specDirectory if running specs on atom core
|
||||
if specDirectory?
|
||||
packageDirectoryPath = path.join(specDirectory, '..')
|
||||
packageManifestPath = path.join(packageDirectoryPath, 'package.json')
|
||||
if fs.statSyncNoException(packageManifestPath)
|
||||
try
|
||||
packageManifest = JSON.parse(fs.readFileSync(packageManifestPath))
|
||||
resourcePath = packageDirectoryPath if packageManifest.name is 'atom'
|
||||
|
||||
if devMode
|
||||
resourcePath ?= global.devResourcePath
|
||||
|
||||
unless fs.statSyncNoException(resourcePath)
|
||||
resourcePath = path.dirname(path.dirname(__dirname))
|
||||
|
||||
@@ -45,3 +45,11 @@ module.exports =
|
||||
writable: false
|
||||
value: requireCoffeeScript
|
||||
})
|
||||
addPathToCache: (filePath) ->
|
||||
extension = path.extname(filePath)
|
||||
if extension is '.coffee'
|
||||
content = fs.readFileSync(filePath, 'utf8')
|
||||
cachePath = getCachePath(coffee)
|
||||
compileCoffeeScript(coffee, filePath, cachePath)
|
||||
else if extension is '.cson'
|
||||
CSON.readFileSync(filePath)
|
||||
|
||||
@@ -128,3 +128,7 @@ module.exports =
|
||||
cr:
|
||||
type: ['boolean', 'string']
|
||||
default: '\u00a4'
|
||||
zoomFontWhenCtrlScrolling:
|
||||
type: 'boolean'
|
||||
default: process.platform isnt 'darwin'
|
||||
description: 'Increase/decrease the editor font size when pressing the Ctrl key and scrolling the mouse up/down.'
|
||||
|
||||
+118
-41
@@ -9,6 +9,7 @@ pathWatcher = require 'pathwatcher'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
ScopedPropertyStore = require 'scoped-property-store'
|
||||
ScopeDescriptor = require './scope-descriptor'
|
||||
|
||||
# Essential: Used to access all of Atom's configuration details.
|
||||
#
|
||||
@@ -335,9 +336,11 @@ class Config
|
||||
# # do stuff with value
|
||||
# ```
|
||||
#
|
||||
# * `scopeDescriptor` (optional) {Array} of {String}s describing a path from
|
||||
# * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
|
||||
# the root of the syntax tree to a token. Get one by calling
|
||||
# {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
|
||||
# See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
|
||||
# for more information.
|
||||
# * `keyPath` {String} name of the key to observe
|
||||
# * `callback` {Function} to call when the value of the key changes.
|
||||
# * `value` the new value of the key
|
||||
@@ -349,7 +352,7 @@ class Config
|
||||
if args.length is 2
|
||||
# observe(keyPath, callback)
|
||||
[keyPath, callback, scopeDescriptor, options] = args
|
||||
else if args.length is 3 and Array.isArray(scopeDescriptor)
|
||||
else if args.length is 3 and (Array.isArray(scopeDescriptor) or scopeDescriptor instanceof ScopeDescriptor)
|
||||
# observe(scopeDescriptor, keyPath, callback)
|
||||
[scopeDescriptor, keyPath, callback, options] = args
|
||||
else if args.length is 3 and _.isString(scopeDescriptor) and _.isObject(keyPath)
|
||||
@@ -371,9 +374,11 @@ class Config
|
||||
# Essential: Add a listener for changes to a given key path. If `keyPath` is
|
||||
# not specified, your callback will be called on changes to any key.
|
||||
#
|
||||
# * `scopeDescriptor` (optional) {Array} of {String}s describing a path from
|
||||
# * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
|
||||
# the root of the syntax tree to a token. Get one by calling
|
||||
# {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
|
||||
# See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
|
||||
# for more information.
|
||||
# * `keyPath` (optional) {String} name of the key to observe. Must be
|
||||
# specified if `scopeDescriptor` is specified.
|
||||
# * `callback` {Function} to call when the value of the key changes.
|
||||
@@ -440,9 +445,11 @@ class Config
|
||||
# atom.config.get(scopeDescriptor, 'editor.tabLength') # => 2
|
||||
# ```
|
||||
#
|
||||
# * `scopeDescriptor` (optional) {Array} of {String}s describing a path from
|
||||
# * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
|
||||
# the root of the syntax tree to a token. Get one by calling
|
||||
# {editor.getLastCursor().getScopeDescriptor()}
|
||||
# See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
|
||||
# for more information.
|
||||
# * `keyPath` The {String} name of the key to retrieve.
|
||||
#
|
||||
# Returns the value from Atom's default settings, the user's configuration
|
||||
@@ -486,7 +493,9 @@ class Config
|
||||
# atom.config.get(['source.js'], 'editor.tabLength') # => 4
|
||||
# ```
|
||||
#
|
||||
# * `scope` (optional) {String}. eg. '.source.ruby'
|
||||
# * `scopeSelector` (optional) {String}. eg. '.source.ruby'
|
||||
# See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
|
||||
# for more information.
|
||||
# * `keyPath` The {String} name of the key.
|
||||
# * `value` The value of the setting. Passing `undefined` will revert the
|
||||
# setting to the default value.
|
||||
@@ -494,11 +503,11 @@ class Config
|
||||
# Returns a {Boolean}
|
||||
# * `true` if the value was set.
|
||||
# * `false` if the value was not able to be coerced to the type specified in the setting's schema.
|
||||
set: (scope, keyPath, value) ->
|
||||
set: (scopeSelector, keyPath, value) ->
|
||||
if arguments.length < 3
|
||||
value = keyPath
|
||||
keyPath = scope
|
||||
scope = undefined
|
||||
keyPath = scopeSelector
|
||||
scopeSelector = undefined
|
||||
|
||||
unless value == undefined
|
||||
try
|
||||
@@ -506,8 +515,8 @@ class Config
|
||||
catch e
|
||||
return false
|
||||
|
||||
if scope?
|
||||
@setRawScopedValue(scope, keyPath, value)
|
||||
if scopeSelector?
|
||||
@setRawScopedValue(scopeSelector, keyPath, value)
|
||||
else
|
||||
@setRawValue(keyPath, value)
|
||||
|
||||
@@ -519,29 +528,59 @@ class Config
|
||||
# * `keyPath` The {String} name of the key.
|
||||
#
|
||||
# Returns the new value.
|
||||
restoreDefault: (keyPath) ->
|
||||
@set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath))
|
||||
@get(keyPath)
|
||||
restoreDefault: (scopeSelector, keyPath) ->
|
||||
if arguments.length == 1
|
||||
keyPath = scopeSelector
|
||||
scopeSelector = null
|
||||
|
||||
if scopeSelector?
|
||||
settings = @scopedSettingsStore.propertiesForSourceAndSelector('user-config', scopeSelector)
|
||||
@scopedSettingsStore.removePropertiesForSourceAndSelector('user-config', scopeSelector)
|
||||
_.setValueForKeyPath(settings, keyPath, undefined)
|
||||
@addScopedSettings('user-config', scopeSelector, settings)
|
||||
@save() unless @configFileHasErrors
|
||||
@getDefault(scopeSelector, keyPath)
|
||||
else
|
||||
@set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath))
|
||||
@get(keyPath)
|
||||
|
||||
# Extended: Get the global default value of the key path. _Please note_ that in most
|
||||
# cases calling this is not necessary! {::get} returns the default value when
|
||||
# a custom value is not specified.
|
||||
#
|
||||
# * `scopeSelector` (optional) {String}. eg. '.source.ruby'
|
||||
# * `keyPath` The {String} name of the key.
|
||||
#
|
||||
# Returns the default value.
|
||||
getDefault: (keyPath) ->
|
||||
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
|
||||
getDefault: (scopeSelector, keyPath) ->
|
||||
if arguments.length == 1
|
||||
keyPath = scopeSelector
|
||||
scopeSelector = null
|
||||
|
||||
if scopeSelector?
|
||||
defaultValue = @scopedSettingsStore.getPropertyValue(scopeSelector, keyPath, excludeSources: ['user-config'])
|
||||
defaultValue ?= _.valueForKeyPath(@defaultSettings, keyPath)
|
||||
else
|
||||
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
|
||||
_.deepClone(defaultValue)
|
||||
|
||||
# Extended: Is the value at `keyPath` its default value?
|
||||
#
|
||||
# * `scopeSelector` (optional) {String}. eg. '.source.ruby'
|
||||
# * `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)?
|
||||
isDefault: (scopeSelector, keyPath) ->
|
||||
if arguments.length == 1
|
||||
keyPath = scopeSelector
|
||||
scopeSelector = null
|
||||
|
||||
if scopeSelector?
|
||||
settings = @scopedSettingsStore.propertiesForSourceAndSelector('user-config', scopeSelector)
|
||||
not _.valueForKeyPath(settings, keyPath)?
|
||||
else
|
||||
not _.valueForKeyPath(@settings, keyPath)?
|
||||
|
||||
# Extended: Retrieve the schema for a specific key path. The schema will tell
|
||||
# you what type the keyPath expects, and other metadata about the config
|
||||
@@ -552,7 +591,7 @@ class Config
|
||||
# Returns an {Object} eg. `{type: 'integer', default: 23, minimum: 1}`.
|
||||
# Returns `null` when the keyPath has no schema specified.
|
||||
getSchema: (keyPath) ->
|
||||
keys = keyPath.split('.')
|
||||
keys = splitKeyPath(keyPath)
|
||||
schema = @schema
|
||||
for key in keys
|
||||
break unless schema?
|
||||
@@ -560,9 +599,17 @@ class Config
|
||||
schema
|
||||
|
||||
# Extended: Returns a new {Object} containing all of the global settings and
|
||||
# defaults. This does not include scoped settings.
|
||||
getSettings: ->
|
||||
_.deepExtend(@settings, @defaultSettings)
|
||||
# defaults. Returns the scoped settings when a `scopeSelector` is specified.
|
||||
#
|
||||
# * `scopeSelector` (optional) {String}. eg. '.source.ruby'
|
||||
getSettings: (scopeSelector) ->
|
||||
settings = _.deepExtend(@settings, @defaultSettings)
|
||||
|
||||
if scopeSelector?
|
||||
scopedSettings = @scopedSettingsStore.propertiesForSelector(scopeSelector)
|
||||
settings = _.deepExtend(scopedSettings, settings)
|
||||
|
||||
settings
|
||||
|
||||
# Extended: Get the {String} path to the config file being used.
|
||||
getUserConfigPath: ->
|
||||
@@ -624,7 +671,7 @@ class Config
|
||||
|
||||
rootSchema = @schema
|
||||
if keyPath
|
||||
for key in keyPath.split('.')
|
||||
for key in splitKeyPath(keyPath)
|
||||
rootSchema.type = 'object'
|
||||
rootSchema.properties ?= {}
|
||||
properties = rootSchema.properties
|
||||
@@ -633,6 +680,7 @@ class Config
|
||||
|
||||
_.extend rootSchema, schema
|
||||
@setDefaults(keyPath, @extractDefaultsFromSchema(schema))
|
||||
@setScopedDefaultsFromSchema(keyPath, schema)
|
||||
|
||||
load: ->
|
||||
@initializeConfigDirectory()
|
||||
@@ -708,7 +756,7 @@ class Config
|
||||
|
||||
unsetUnspecifiedValues = (keyPath, value) =>
|
||||
if isPlainObject(value)
|
||||
keys = if keyPath? then keyPath.split('.') else []
|
||||
keys = splitKeyPath(keyPath)
|
||||
for key, childValue of value
|
||||
continue unless value.hasOwnProperty(key)
|
||||
unsetUnspecifiedValues(keys.concat([key]).join('.'), childValue)
|
||||
@@ -721,7 +769,7 @@ class Config
|
||||
|
||||
setRecursive: (keyPath, value) ->
|
||||
if isPlainObject(value)
|
||||
keys = if keyPath? then keyPath.split('.') else []
|
||||
keys = splitKeyPath(keyPath)
|
||||
for key, childValue of value
|
||||
continue unless value.hasOwnProperty(key)
|
||||
@setRecursive(keys.concat([key]).join('.'), childValue)
|
||||
@@ -764,8 +812,8 @@ class Config
|
||||
|
||||
isSubKeyPath: (keyPath, subKeyPath) ->
|
||||
return false unless keyPath? and subKeyPath?
|
||||
pathSubTokens = subKeyPath.split('.')
|
||||
pathTokens = keyPath.split('.').slice(0, pathSubTokens.length)
|
||||
pathSubTokens = splitKeyPath(subKeyPath)
|
||||
pathTokens = splitKeyPath(keyPath).slice(0, pathSubTokens.length)
|
||||
_.isEqual(pathTokens, pathSubTokens)
|
||||
|
||||
setRawDefault: (keyPath, value) ->
|
||||
@@ -776,7 +824,7 @@ class Config
|
||||
|
||||
setDefaults: (keyPath, defaults) ->
|
||||
if defaults? and isPlainObject(defaults)
|
||||
keys = if keyPath? then keyPath.split('.') else []
|
||||
keys = splitKeyPath(keyPath)
|
||||
for key, childValue of defaults
|
||||
continue unless defaults.hasOwnProperty(key)
|
||||
@setDefaults(keys.concat([key]).join('.'), childValue)
|
||||
@@ -787,6 +835,32 @@ class Config
|
||||
catch e
|
||||
console.warn("'#{keyPath}' could not set the default. Attempted default: #{JSON.stringify(defaults)}; Schema: #{JSON.stringify(@getSchema(keyPath))}")
|
||||
|
||||
# `schema` will look something like this
|
||||
#
|
||||
# ```coffee
|
||||
# type: 'string'
|
||||
# default: 'ok'
|
||||
# scopes:
|
||||
# '.source.js':
|
||||
# default: 'omg'
|
||||
# ```
|
||||
setScopedDefaultsFromSchema: (keyPath, schema) ->
|
||||
if schema.scopes? and isPlainObject(schema.scopes)
|
||||
scopedDefaults = {}
|
||||
for scope, scopeSchema of schema.scopes
|
||||
continue unless scopeSchema.hasOwnProperty('default')
|
||||
scopedDefaults[scope] = {}
|
||||
_.setValueForKeyPath(scopedDefaults[scope], keyPath, scopeSchema.default)
|
||||
@scopedSettingsStore.addProperties('schema-default', scopedDefaults)
|
||||
|
||||
if schema.type is 'object' and schema.properties? and isPlainObject(schema.properties)
|
||||
keys = splitKeyPath(keyPath)
|
||||
for key, childValue of schema.properties
|
||||
continue unless schema.properties.hasOwnProperty(key)
|
||||
@setScopedDefaultsFromSchema(keys.concat([key]).join('.'), childValue)
|
||||
|
||||
return
|
||||
|
||||
extractDefaultsFromSchema: (schema) ->
|
||||
if schema.default?
|
||||
schema.default
|
||||
@@ -810,10 +884,10 @@ class Config
|
||||
@usersScopedSettings.add @scopedSettingsStore.addProperties('user-config', newScopedSettings)
|
||||
@emitter.emit 'did-change'
|
||||
|
||||
addScopedSettings: (name, selector, value) ->
|
||||
addScopedSettings: (source, selector, value) ->
|
||||
settingsBySelector = {}
|
||||
settingsBySelector[selector] = value
|
||||
disposable = @scopedSettingsStore.addProperties(name, settingsBySelector)
|
||||
disposable = @scopedSettingsStore.addProperties(source, settingsBySelector)
|
||||
@emitter.emit 'did-change'
|
||||
new Disposable =>
|
||||
disposable.dispose()
|
||||
@@ -831,12 +905,8 @@ class Config
|
||||
@emitter.emit 'did-change'
|
||||
|
||||
getRawScopedValue: (scopeDescriptor, keyPath) ->
|
||||
scopeChain = scopeDescriptor
|
||||
.map (scope) ->
|
||||
scope = ".#{scope}" unless scope[0] is '.'
|
||||
scope
|
||||
.join(' ')
|
||||
@scopedSettingsStore.getPropertyValue(scopeChain, keyPath)
|
||||
scopeDescriptor = ScopeDescriptor.fromObject(scopeDescriptor)
|
||||
@scopedSettingsStore.getPropertyValue(scopeDescriptor.getScopeChain(), keyPath)
|
||||
|
||||
observeScopedKeyPath: (scopeDescriptor, keyPath, callback) ->
|
||||
oldValue = @get(scopeDescriptor, keyPath)
|
||||
@@ -863,12 +933,8 @@ class Config
|
||||
# * language mode uses it for one thing.
|
||||
# * autocomplete uses it for editor.completions
|
||||
settingsForScopeDescriptor: (scopeDescriptor, keyPath) ->
|
||||
scopeChain = scopeDescriptor
|
||||
.map (scope) ->
|
||||
scope = ".#{scope}" unless scope[0] is '.'
|
||||
scope
|
||||
.join(' ')
|
||||
@scopedSettingsStore.getProperties(scopeChain, keyPath)
|
||||
scopeDescriptor = ScopeDescriptor.fromObject(scopeDescriptor)
|
||||
@scopedSettingsStore.getProperties(scopeDescriptor.getScopeChain(), keyPath)
|
||||
|
||||
# Base schema enforcers. These will coerce raw input into the specified type,
|
||||
# and will throw an error when the value cannot be coerced. Throwing the error
|
||||
@@ -968,3 +1034,14 @@ Config.addSchemaEnforcers
|
||||
|
||||
isPlainObject = (value) ->
|
||||
_.isObject(value) and not _.isArray(value) and not _.isFunction(value) and not _.isString(value)
|
||||
|
||||
splitKeyPath = (keyPath) ->
|
||||
return [] unless keyPath?
|
||||
startIndex = 0
|
||||
keyPathArray = []
|
||||
for char, i in keyPath
|
||||
if char is '.' and (i is 0 or keyPath[i-1] != '\\')
|
||||
keyPathArray.push keyPath.substring(startIndex, i)
|
||||
startIndex = i + 1
|
||||
keyPathArray.push keyPath.substr(startIndex, keyPath.length)
|
||||
keyPathArray
|
||||
|
||||
+2
-2
@@ -216,12 +216,12 @@ class Cursor extends Model
|
||||
|
||||
# Public: Retrieves the scope descriptor for the cursor's current position.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
# Returns a {ScopeDescriptor}
|
||||
getScopeDescriptor: ->
|
||||
@editor.scopeDescriptorForBufferPosition(@getBufferPosition())
|
||||
getScopes: ->
|
||||
Grim.deprecate 'Use Cursor::getScopeDescriptor() instead'
|
||||
@getScopeDescriptor()
|
||||
@getScopeDescriptor().getScopesArray()
|
||||
|
||||
# Public: Returns true if this cursor has no non-whitespace characters before
|
||||
# its current position.
|
||||
|
||||
@@ -650,7 +650,7 @@ class DisplayBuffer extends Model
|
||||
left = 0
|
||||
column = 0
|
||||
for token in @tokenizedLineForScreenRow(targetRow).tokens
|
||||
charWidths = @getScopedCharWidths(token.scopeDescriptor)
|
||||
charWidths = @getScopedCharWidths(token.scopes)
|
||||
for char in token.value
|
||||
return {top, left} if column is targetColumn
|
||||
left += charWidths[char] ? defaultCharWidth unless char is '\0'
|
||||
@@ -662,13 +662,14 @@ class DisplayBuffer extends Model
|
||||
targetLeft = pixelPosition.left
|
||||
defaultCharWidth = @defaultCharWidth
|
||||
row = Math.floor(targetTop / @getLineHeightInPixels())
|
||||
targetLeft = Infinity if row > @getLastRow()
|
||||
row = Math.min(row, @getLastRow())
|
||||
row = Math.max(0, row)
|
||||
|
||||
left = 0
|
||||
column = 0
|
||||
for token in @tokenizedLineForScreenRow(row).tokens
|
||||
charWidths = @getScopedCharWidths(token.scopeDescriptor)
|
||||
charWidths = @getScopedCharWidths(token.scopes)
|
||||
for char in token.value
|
||||
charWidth = charWidths[char] ? defaultCharWidth
|
||||
break if targetLeft <= left + (charWidth / 2)
|
||||
@@ -756,7 +757,7 @@ class DisplayBuffer extends Model
|
||||
#
|
||||
# bufferPosition - A {Point} in the {TextBuffer}
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
# Returns a {ScopeDescriptor}.
|
||||
scopeDescriptorForBufferPosition: (bufferPosition) ->
|
||||
@tokenizedBuffer.scopeDescriptorForPosition(bufferPosition)
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ LinesComponent = React.createClass
|
||||
firstTrailingWhitespacePosition = text.search(/\s*$/)
|
||||
lineIsWhitespaceOnly = firstTrailingWhitespacePosition is 0
|
||||
for token in tokens
|
||||
innerHTML += @updateScopeStack(scopeStack, token.scopeDescriptor)
|
||||
innerHTML += @updateScopeStack(scopeStack, token.scopes)
|
||||
hasIndentGuide = not mini and showIndentGuide and (token.hasLeadingWhitespace() or (token.hasTrailingWhitespace() and lineIsWhitespaceOnly))
|
||||
innerHTML += token.getValueAsHtml({hasIndentGuide})
|
||||
|
||||
@@ -308,8 +308,8 @@ LinesComponent = React.createClass
|
||||
iterator = null
|
||||
charIndex = 0
|
||||
|
||||
for {value, scopeDescriptor}, tokenIndex in tokenizedLine.tokens
|
||||
charWidths = editor.getScopedCharWidths(scopeDescriptor)
|
||||
for {value, scopes}, tokenIndex in tokenizedLine.tokens
|
||||
charWidths = editor.getScopedCharWidths(scopes)
|
||||
|
||||
for char in value
|
||||
continue if char is '\0'
|
||||
@@ -331,7 +331,7 @@ LinesComponent = React.createClass
|
||||
rangeForMeasurement.setStart(textNode, i)
|
||||
rangeForMeasurement.setEnd(textNode, i + 1)
|
||||
charWidth = rangeForMeasurement.getBoundingClientRect().width
|
||||
editor.setScopedCharWidth(scopeDescriptor, char, charWidth)
|
||||
editor.setScopedCharWidth(scopes, char, charWidth)
|
||||
|
||||
charIndex++
|
||||
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
Module = require 'module'
|
||||
path = require 'path'
|
||||
semver = require 'semver'
|
||||
|
||||
# Extend semver.Range to memoize matched versions for speed
|
||||
class Range extends semver.Range
|
||||
constructor: ->
|
||||
super
|
||||
@matchedVersions = new Set()
|
||||
@unmatchedVersions = new Set()
|
||||
|
||||
test: (version) ->
|
||||
return true if @matchedVersions.has(version)
|
||||
return false if @unmatchedVersions.has(version)
|
||||
|
||||
matches = super
|
||||
if matches
|
||||
@matchedVersions.add(version)
|
||||
else
|
||||
@unmatchedVersions.add(version)
|
||||
matches
|
||||
|
||||
nativeModules = process.binding('natives')
|
||||
|
||||
cache =
|
||||
builtins: {}
|
||||
debug: false
|
||||
dependencies: {}
|
||||
extensions: {}
|
||||
folders: {}
|
||||
ranges: {}
|
||||
registered: false
|
||||
resourcePath: null
|
||||
resourcePathWithTrailingSlash: null
|
||||
|
||||
# isAbsolute is inlined from fs-plus so that fs-plus itself can be required
|
||||
# from this cache.
|
||||
if process.platform is 'win32'
|
||||
isAbsolute = (pathToCheck) ->
|
||||
pathToCheck and (pathToCheck[1] is ':' or (pathToCheck[0] is '\\' and pathToCheck[1] is '\\'))
|
||||
else
|
||||
isAbsolute = (pathToCheck) ->
|
||||
pathToCheck and pathToCheck[0] is '/'
|
||||
|
||||
isCorePath = (pathToCheck) ->
|
||||
pathToCheck.startsWith(cache.resourcePathWithTrailingSlash)
|
||||
|
||||
loadDependencies = (modulePath, rootPath, rootMetadata, moduleCache) ->
|
||||
fs = require 'fs-plus'
|
||||
|
||||
for childPath in fs.listSync(path.join(modulePath, 'node_modules'))
|
||||
continue if path.basename(childPath) is '.bin'
|
||||
continue if rootPath is modulePath and rootMetadata.packageDependencies?.hasOwnProperty(path.basename(childPath))
|
||||
|
||||
childMetadataPath = path.join(childPath, 'package.json')
|
||||
continue unless fs.isFileSync(childMetadataPath)
|
||||
|
||||
childMetadata = JSON.parse(fs.readFileSync(childMetadataPath))
|
||||
if childMetadata?.version
|
||||
try
|
||||
mainPath = require.resolve(childPath)
|
||||
|
||||
if mainPath
|
||||
moduleCache.dependencies.push
|
||||
name: childMetadata.name
|
||||
version: childMetadata.version
|
||||
path: path.relative(rootPath, mainPath)
|
||||
|
||||
loadDependencies(childPath, rootPath, rootMetadata, moduleCache)
|
||||
|
||||
return
|
||||
|
||||
loadFolderCompatibility = (modulePath, rootPath, rootMetadata, moduleCache) ->
|
||||
fs = require 'fs-plus'
|
||||
|
||||
metadataPath = path.join(modulePath, 'package.json')
|
||||
return unless fs.isFileSync(metadataPath)
|
||||
|
||||
dependencies = JSON.parse(fs.readFileSync(metadataPath))?.dependencies ? {}
|
||||
|
||||
for name, version of dependencies
|
||||
try
|
||||
new Range(version)
|
||||
catch error
|
||||
delete dependencies[name]
|
||||
|
||||
onDirectory = (childPath) ->
|
||||
path.basename(childPath) isnt 'node_modules'
|
||||
|
||||
extensions = ['.js', '.coffee', '.json', '.node']
|
||||
paths = {}
|
||||
onFile = (childPath) ->
|
||||
if path.extname(childPath) in extensions
|
||||
relativePath = path.relative(rootPath, path.dirname(childPath))
|
||||
paths[relativePath] = true
|
||||
fs.traverseTreeSync(modulePath, onFile, onDirectory)
|
||||
|
||||
paths = Object.keys(paths)
|
||||
if paths.length > 0 and Object.keys(dependencies).length > 0
|
||||
moduleCache.folders.push({paths, dependencies})
|
||||
|
||||
for childPath in fs.listSync(path.join(modulePath, 'node_modules'))
|
||||
continue if path.basename(childPath) is '.bin'
|
||||
continue if rootPath is modulePath and rootMetadata.packageDependencies?.hasOwnProperty(path.basename(childPath))
|
||||
|
||||
loadFolderCompatibility(childPath, rootPath, rootMetadata, moduleCache)
|
||||
|
||||
return
|
||||
|
||||
loadExtensions = (modulePath, rootPath, rootMetadata, moduleCache) ->
|
||||
fs = require 'fs-plus'
|
||||
extensions = ['.js', '.coffee', '.json', '.node']
|
||||
nodeModulesPath = path.join(rootPath, 'node_modules')
|
||||
|
||||
onFile = (filePath) ->
|
||||
filePath = path.relative(rootPath, filePath)
|
||||
segments = filePath.split(path.sep)
|
||||
return if 'test' in segments
|
||||
return if 'tests' in segments
|
||||
return if 'spec' in segments
|
||||
return if 'specs' in segments
|
||||
return if segments.length > 1 and not (segments[0] in ['exports', 'lib', 'node_modules', 'src', 'static', 'vendor'])
|
||||
|
||||
extension = path.extname(filePath)
|
||||
if extension in extensions
|
||||
moduleCache.extensions[extension] ?= []
|
||||
moduleCache.extensions[extension].push(filePath)
|
||||
|
||||
onDirectory = (childPath) ->
|
||||
# Don't include extensions from bundled packages
|
||||
# These are generated and stored in the package's own metadata cache
|
||||
if rootMetadata.name is 'atom'
|
||||
parentPath = path.dirname(childPath)
|
||||
if parentPath is nodeModulesPath
|
||||
packageName = path.basename(childPath)
|
||||
return false if rootMetadata.packageDependencies?.hasOwnProperty(packageName)
|
||||
|
||||
true
|
||||
|
||||
fs.traverseTreeSync(rootPath, onFile, onDirectory)
|
||||
|
||||
return
|
||||
|
||||
satisfies = (version, rawRange) ->
|
||||
unless parsedRange = cache.ranges[rawRange]
|
||||
parsedRange = new Range(rawRange)
|
||||
cache.ranges[rawRange] = parsedRange
|
||||
parsedRange.test(version)
|
||||
|
||||
resolveFilePath = (relativePath, parentModule) ->
|
||||
return unless relativePath
|
||||
return unless parentModule?.filename
|
||||
return unless relativePath[0] is '.' or isAbsolute(relativePath)
|
||||
|
||||
resolvedPath = path.resolve(path.dirname(parentModule.filename), relativePath)
|
||||
return unless isCorePath(resolvedPath)
|
||||
|
||||
extension = path.extname(resolvedPath)
|
||||
if extension
|
||||
return resolvedPath if cache.extensions[extension]?.has(resolvedPath)
|
||||
else
|
||||
for extension, paths of cache.extensions
|
||||
resolvedPathWithExtension = "#{resolvedPath}#{extension}"
|
||||
return resolvedPathWithExtension if paths.has(resolvedPathWithExtension)
|
||||
|
||||
return
|
||||
|
||||
resolveModulePath = (relativePath, parentModule) ->
|
||||
return unless relativePath
|
||||
return unless parentModule?.filename
|
||||
|
||||
return if nativeModules.hasOwnProperty(relativePath)
|
||||
return if relativePath[0] is '.'
|
||||
return if isAbsolute(relativePath)
|
||||
|
||||
folderPath = path.dirname(parentModule.filename)
|
||||
|
||||
range = cache.folders[folderPath]?[relativePath]
|
||||
unless range?
|
||||
if builtinPath = cache.builtins[relativePath]
|
||||
return builtinPath
|
||||
else
|
||||
return
|
||||
|
||||
candidates = cache.dependencies[relativePath]
|
||||
return unless candidates?
|
||||
|
||||
for version, resolvedPath of candidates
|
||||
if Module._cache.hasOwnProperty(resolvedPath) or isCorePath(resolvedPath)
|
||||
return resolvedPath if satisfies(version, range)
|
||||
|
||||
return
|
||||
|
||||
registerBuiltins = (devMode) ->
|
||||
if devMode
|
||||
fs = require 'fs-plus'
|
||||
atomCoffeePath = path.join(cache.resourcePath, 'exports', 'atom.coffee')
|
||||
cache.builtins.atom = atomCoffeePath if fs.isFileSync(atomCoffeePath)
|
||||
cache.builtins.atom ?= path.join(cache.resourcePath, 'exports', 'atom.js')
|
||||
|
||||
atomShellRoot = path.join(process.resourcesPath, 'atom')
|
||||
|
||||
commonRoot = path.join(atomShellRoot, 'common', 'api', 'lib')
|
||||
commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'screen', 'shell']
|
||||
for builtin in commonBuiltins
|
||||
cache.builtins[builtin] = path.join(commonRoot, "#{builtin}.js")
|
||||
|
||||
rendererRoot = path.join(atomShellRoot, 'renderer', 'api', 'lib')
|
||||
rendererBuiltins = ['ipc', 'remote']
|
||||
for builtin in rendererBuiltins
|
||||
cache.builtins[builtin] = path.join(rendererRoot, "#{builtin}.js")
|
||||
|
||||
if cache.debug
|
||||
cache.findPathCount = 0
|
||||
cache.findPathTime = 0
|
||||
cache.loadCount = 0
|
||||
cache.requireTime = 0
|
||||
global.moduleCache = cache
|
||||
|
||||
originalLoad = Module::load
|
||||
Module::load = ->
|
||||
cache.loadCount++
|
||||
originalLoad.apply(this, arguments)
|
||||
|
||||
originalRequire = Module::require
|
||||
Module::require = ->
|
||||
startTime = Date.now()
|
||||
exports = originalRequire.apply(this, arguments)
|
||||
cache.requireTime += Date.now() - startTime
|
||||
exports
|
||||
|
||||
originalFindPath = Module._findPath
|
||||
Module._findPath = (request, paths) ->
|
||||
cacheKey = JSON.stringify({request, paths})
|
||||
cache.findPathCount++ unless Module._pathCache[cacheKey]
|
||||
|
||||
startTime = Date.now()
|
||||
foundPath = originalFindPath.apply(global, arguments)
|
||||
cache.findPathTime += Date.now() - startTime
|
||||
foundPath
|
||||
|
||||
exports.create = (modulePath) ->
|
||||
fs = require 'fs-plus'
|
||||
|
||||
modulePath = fs.realpathSync(modulePath)
|
||||
metadataPath = path.join(modulePath, 'package.json')
|
||||
metadata = JSON.parse(fs.readFileSync(metadataPath))
|
||||
|
||||
moduleCache =
|
||||
version: 1
|
||||
dependencies: []
|
||||
extensions: {}
|
||||
folders: []
|
||||
|
||||
loadDependencies(modulePath, modulePath, metadata, moduleCache)
|
||||
loadFolderCompatibility(modulePath, modulePath, metadata, moduleCache)
|
||||
loadExtensions(modulePath, modulePath, metadata, moduleCache)
|
||||
|
||||
metadata._atomModuleCache = moduleCache
|
||||
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2))
|
||||
|
||||
return
|
||||
|
||||
exports.register = ({resourcePath, devMode}={}) ->
|
||||
return if cache.registered
|
||||
|
||||
originalResolveFilename = Module._resolveFilename
|
||||
Module._resolveFilename = (relativePath, parentModule) ->
|
||||
resolvedPath = resolveModulePath(relativePath, parentModule)
|
||||
resolvedPath ?= resolveFilePath(relativePath, parentModule)
|
||||
resolvedPath ? originalResolveFilename(relativePath, parentModule)
|
||||
|
||||
cache.registered = true
|
||||
cache.resourcePath = resourcePath
|
||||
cache.resourcePathWithTrailingSlash = "#{resourcePath}#{path.sep}"
|
||||
registerBuiltins(devMode)
|
||||
|
||||
return
|
||||
|
||||
exports.add = (directoryPath, metadata) ->
|
||||
# path.join isn't used in this function for speed since path.join calls
|
||||
# path.normalize and all the paths are already normalized here.
|
||||
|
||||
unless metadata?
|
||||
try
|
||||
metadata = require("#{directoryPath}#{path.sep}package.json")
|
||||
catch error
|
||||
return
|
||||
|
||||
cacheToAdd = metadata?._atomModuleCache
|
||||
return unless cacheToAdd?
|
||||
|
||||
for dependency in cacheToAdd.dependencies ? []
|
||||
cache.dependencies[dependency.name] ?= {}
|
||||
cache.dependencies[dependency.name][dependency.version] ?= "#{directoryPath}#{path.sep}#{dependency.path}"
|
||||
|
||||
for entry in cacheToAdd.folders ? []
|
||||
for folderPath in entry.paths
|
||||
if folderPath
|
||||
cache.folders["#{directoryPath}#{path.sep}#{folderPath}"] = entry.dependencies
|
||||
else
|
||||
cache.folders[directoryPath] = entry.dependencies
|
||||
|
||||
for extension, paths of cacheToAdd.extensions
|
||||
cache.extensions[extension] ?= new Set()
|
||||
for filePath in paths
|
||||
cache.extensions[extension].add("#{directoryPath}#{path.sep}#{filePath}")
|
||||
|
||||
return
|
||||
|
||||
exports.cache = cache
|
||||
@@ -29,11 +29,11 @@ module.exports =
|
||||
class PackageManager
|
||||
EmitterMixin.includeInto(this)
|
||||
|
||||
constructor: ({configDirPath, devMode, safeMode, @resourcePath}) ->
|
||||
constructor: ({configDirPath, @devMode, safeMode, @resourcePath}) ->
|
||||
@emitter = new Emitter
|
||||
@packageDirPaths = []
|
||||
unless safeMode
|
||||
if devMode
|
||||
if @devMode
|
||||
@packageDirPaths.push(path.join(configDirPath, "dev", "packages"))
|
||||
@packageDirPaths.push(path.join(configDirPath, "packages"))
|
||||
|
||||
|
||||
+55
-17
@@ -10,8 +10,14 @@ Q = require 'q'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
$ = null # Defer require in case this is in the window-less browser process
|
||||
ModuleCache = require './module-cache'
|
||||
ScopedProperties = require './scoped-properties'
|
||||
|
||||
try
|
||||
packagesCache = require('../package.json')?._atomPackages ? {}
|
||||
catch error
|
||||
packagesCache = {}
|
||||
|
||||
# Loads and activates a package's main module and resources such as
|
||||
# stylesheets, keymaps, grammar, editor properties, and menus.
|
||||
module.exports =
|
||||
@@ -20,14 +26,25 @@ class Package
|
||||
|
||||
@stylesheetsDir: 'stylesheets'
|
||||
|
||||
@isBundledPackagePath: (packagePath) ->
|
||||
if atom.packages.devMode
|
||||
return false unless atom.packages.resourcePath.startsWith("#{process.resourcesPath}#{path.sep}")
|
||||
|
||||
@resourcePathWithTrailingSlash ?= "#{atom.packages.resourcePath}#{path.sep}"
|
||||
packagePath?.startsWith(@resourcePathWithTrailingSlash)
|
||||
|
||||
@loadMetadata: (packagePath, ignoreErrors=false) ->
|
||||
if metadataPath = CSON.resolve(path.join(packagePath, 'package'))
|
||||
try
|
||||
metadata = CSON.readFileSync(metadataPath)
|
||||
catch error
|
||||
throw error unless ignoreErrors
|
||||
packageName = path.basename(packagePath)
|
||||
if @isBundledPackagePath(packagePath)
|
||||
metadata = packagesCache[packageName]?.metadata
|
||||
unless metadata?
|
||||
if metadataPath = CSON.resolve(path.join(packagePath, 'package'))
|
||||
try
|
||||
metadata = CSON.readFileSync(metadataPath)
|
||||
catch error
|
||||
throw error unless ignoreErrors
|
||||
metadata ?= {}
|
||||
metadata.name = path.basename(packagePath)
|
||||
metadata.name = packageName
|
||||
metadata
|
||||
|
||||
keymaps: null
|
||||
@@ -46,7 +63,9 @@ class Package
|
||||
constructor: (@path, @metadata) ->
|
||||
@emitter = new Emitter
|
||||
@metadata ?= Package.loadMetadata(@path)
|
||||
@bundledPackage = Package.isBundledPackagePath(@path)
|
||||
@name = @metadata?.name ? path.basename(@path)
|
||||
ModuleCache.add(@path, @metadata)
|
||||
@reset()
|
||||
|
||||
###
|
||||
@@ -175,10 +194,16 @@ class Package
|
||||
@scopedPropertiesActivated = true
|
||||
|
||||
loadKeymaps: ->
|
||||
@keymaps = @getKeymapPaths().map (keymapPath) -> [keymapPath, CSON.readFileSync(keymapPath)]
|
||||
if @bundledPackage and packagesCache[@name]?
|
||||
@keymaps = (["#{atom.packages.resourcePath}#{path.sep}#{keymapPath}", keymapObject] for keymapPath, keymapObject of packagesCache[@name].keymaps)
|
||||
else
|
||||
@keymaps = @getKeymapPaths().map (keymapPath) -> [keymapPath, CSON.readFileSync(keymapPath)]
|
||||
|
||||
loadMenus: ->
|
||||
@menus = @getMenuPaths().map (menuPath) -> [menuPath, CSON.readFileSync(menuPath)]
|
||||
if @bundledPackage and packagesCache[@name]?
|
||||
@menus = (["#{atom.packages.resourcePath}#{path.sep}#{menuPath}", menuObject] for menuPath, menuObject of packagesCache[@name].menus)
|
||||
else
|
||||
@menus = @getMenuPaths().map (menuPath) -> [menuPath, CSON.readFileSync(menuPath)]
|
||||
|
||||
getKeymapPaths: ->
|
||||
keymapsDirPath = path.join(@path, 'keymaps')
|
||||
@@ -312,19 +337,31 @@ class Package
|
||||
|
||||
requireMainModule: ->
|
||||
return @mainModule if @mainModule?
|
||||
return unless @isCompatible()
|
||||
unless @isCompatible()
|
||||
console.warn """
|
||||
Failed to require the main module of '#{@name}' because it requires an incompatible native module.
|
||||
Run `apm rebuild` in the package directory to resolve.
|
||||
"""
|
||||
return
|
||||
mainModulePath = @getMainModulePath()
|
||||
@mainModule = require(mainModulePath) if fs.isFileSync(mainModulePath)
|
||||
|
||||
getMainModulePath: ->
|
||||
return @mainModulePath if @resolvedMainModulePath
|
||||
@resolvedMainModulePath = true
|
||||
mainModulePath =
|
||||
if @metadata.main
|
||||
path.join(@path, @metadata.main)
|
||||
|
||||
if @bundledPackage and packagesCache[@name]?
|
||||
if packagesCache[@name].main
|
||||
@mainModulePath = "#{atom.packages.resourcePath}#{path.sep}#{packagesCache[@name].main}"
|
||||
else
|
||||
path.join(@path, 'index')
|
||||
@mainModulePath = fs.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
|
||||
@mainModulePath = null
|
||||
else
|
||||
mainModulePath =
|
||||
if @metadata.main
|
||||
path.join(@path, @metadata.main)
|
||||
else
|
||||
path.join(@path, 'index')
|
||||
@mainModulePath = fs.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
|
||||
|
||||
hasActivationCommands: ->
|
||||
for selector, commands of @getActivationCommands()
|
||||
@@ -336,8 +373,10 @@ class Package
|
||||
for selector, commands of @getActivationCommands()
|
||||
for command in commands
|
||||
do (selector, command) =>
|
||||
atom.commands.commandRegistered(command)
|
||||
@activationCommandSubscriptions.add(atom.commands.onWillDispatch (event) =>
|
||||
# Add dummy command so it appears in menu.
|
||||
# The real command will be registered on package activation
|
||||
@activationCommandSubscriptions.add atom.commands.add selector, command, ->
|
||||
@activationCommandSubscriptions.add atom.commands.onWillDispatch (event) =>
|
||||
return unless event.type is command
|
||||
currentTarget = event.target
|
||||
while currentTarget
|
||||
@@ -346,7 +385,6 @@ class Package
|
||||
@activateNow()
|
||||
break
|
||||
currentTarget = currentTarget.parentElement
|
||||
)
|
||||
|
||||
getActivationCommands: ->
|
||||
return @activationCommands if @activationCommands?
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
|
||||
class PanelContainerElement extends HTMLElement
|
||||
createdCallback: ->
|
||||
@subscriptions = new CompositeDisposable
|
||||
|
||||
getModel: -> @model
|
||||
|
||||
setModel: (@model) ->
|
||||
@subscriptions.add @model.onDidAddPanel(@panelAdded.bind(this))
|
||||
@subscriptions.add @model.onDidRemovePanel(@panelRemoved.bind(this))
|
||||
@subscriptions.add @model.onDidDestroy(@destroyed.bind(this))
|
||||
|
||||
@setAttribute('location', @model.getLocation())
|
||||
|
||||
panelAdded: ({panel, index}) ->
|
||||
if index >= @childNodes.length
|
||||
@appendChild(panel.getView())
|
||||
else
|
||||
referenceItem = @childNodes[index + 1]
|
||||
@insertBefore(panel.getView(), referenceItem)
|
||||
|
||||
panelRemoved: ({panel, index}) ->
|
||||
@removeChild(@childNodes[index])
|
||||
|
||||
destroyed: ->
|
||||
@subscriptions.dispose()
|
||||
@parentNode?.removeChild(this)
|
||||
|
||||
module.exports = PanelContainerElement = document.registerElement 'atom-panel-container', prototype: PanelContainerElement.prototype
|
||||
@@ -0,0 +1,66 @@
|
||||
{Emitter, CompositeDisposable} = require 'event-kit'
|
||||
|
||||
module.exports =
|
||||
class PanelContainer
|
||||
constructor: ({@viewRegistry, @location}) ->
|
||||
@emitter = new Emitter
|
||||
@subscriptions = new CompositeDisposable
|
||||
@panels = []
|
||||
|
||||
destroy: ->
|
||||
pane.destroy() for pane in @getPanels()
|
||||
@subscriptions.dispose()
|
||||
@emitter.emit 'did-destroy', this
|
||||
@emitter.dispose()
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
onDidAddPanel: (callback) ->
|
||||
@emitter.on 'did-add-panel', callback
|
||||
|
||||
onDidRemovePanel: (callback) ->
|
||||
@emitter.on 'did-remove-panel', callback
|
||||
|
||||
onDidDestroy: (callback) ->
|
||||
@emitter.on 'did-destroy', callback
|
||||
|
||||
###
|
||||
Section: Panels
|
||||
###
|
||||
|
||||
getView: -> @viewRegistry.getView(this)
|
||||
|
||||
getLocation: -> @location
|
||||
|
||||
getPanels: -> @panels
|
||||
|
||||
addPanel: (panel) ->
|
||||
@subscriptions.add panel.onDidDestroy(@panelDestoryed.bind(this))
|
||||
|
||||
index = @getPanelIndex(panel)
|
||||
if index is @panels.length
|
||||
@panels.push(panel)
|
||||
else
|
||||
@panels.splice(index, 0, panel)
|
||||
|
||||
@emitter.emit 'did-add-panel', {panel, index}
|
||||
panel
|
||||
|
||||
panelDestoryed: (panel) ->
|
||||
index = @panels.indexOf(panel)
|
||||
if index > -1
|
||||
@panels.splice(index, 1)
|
||||
@emitter.emit 'did-remove-panel', {panel, index}
|
||||
|
||||
getPanelIndex: (panel) ->
|
||||
priority = panel.getPriority()
|
||||
if @location in ['bottom', 'right']
|
||||
for p, i in @panels by -1
|
||||
return i + 1 if priority < p.getPriority()
|
||||
0
|
||||
else
|
||||
for p, i in @panels
|
||||
return i if priority < p.getPriority()
|
||||
@panels.length
|
||||
@@ -0,0 +1,31 @@
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
{callAttachHooks} = require './space-pen-extensions'
|
||||
|
||||
class PanelElement extends HTMLElement
|
||||
createdCallback: ->
|
||||
@subscriptions = new CompositeDisposable
|
||||
|
||||
getModel: -> @model
|
||||
|
||||
setModel: (@model) ->
|
||||
view = @model.getItemView()
|
||||
@appendChild(view)
|
||||
callAttachHooks(view) # for backward compatibility with SpacePen views
|
||||
|
||||
@subscriptions.add @model.onDidChangeVisible(@visibleChanged.bind(this))
|
||||
@subscriptions.add @model.onDidDestroy(@destroyed.bind(this))
|
||||
|
||||
attachedCallback: ->
|
||||
@visibleChanged(@model.isVisible())
|
||||
|
||||
visibleChanged: (visible) ->
|
||||
if visible
|
||||
@style.display = null
|
||||
else
|
||||
@style.display = 'none'
|
||||
|
||||
destroyed: ->
|
||||
@subscriptions.dispose()
|
||||
@parentNode?.removeChild(this)
|
||||
|
||||
module.exports = PanelElement = document.registerElement 'atom-panel', prototype: PanelElement.prototype
|
||||
@@ -0,0 +1,78 @@
|
||||
{Emitter} = require 'event-kit'
|
||||
|
||||
# Extended: A container representing a panel on the edges of the editor window.
|
||||
# You should not create a `Panel` directly, instead use {Workspace::addTopPanel}
|
||||
# and friends to add panels.
|
||||
#
|
||||
# Examples: [tree-view](https://github.com/atom/tree-view),
|
||||
# [status-bar](https://github.com/atom/status-bar),
|
||||
# and [find-and-replace](https://github.com/atom/find-and-replace) all use
|
||||
# panels.
|
||||
module.exports =
|
||||
class Panel
|
||||
###
|
||||
Section: Construction and Destruction
|
||||
###
|
||||
|
||||
constructor: ({@viewRegistry, @item, @visible, @priority}) ->
|
||||
@emitter = new Emitter
|
||||
@visible ?= true
|
||||
@priority ?= 100
|
||||
|
||||
# Public: Destroy and remove this panel from the UI.
|
||||
destroy: ->
|
||||
@emitter.emit 'did-destroy', this
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Public: Invoke the given callback when the pane hidden or shown.
|
||||
#
|
||||
# * `callback` {Function} to be called when the pane is destroyed.
|
||||
# * `visible` {Boolean} true when the panel has been shown
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeVisible: (callback) ->
|
||||
@emitter.on 'did-change-visible', callback
|
||||
|
||||
# Public: Invoke the given callback when the pane is destroyed.
|
||||
#
|
||||
# * `callback` {Function} to be called when the pane is destroyed.
|
||||
# * `panel` {Panel} this panel
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidDestroy: (callback) ->
|
||||
@emitter.on 'did-destroy', callback
|
||||
|
||||
###
|
||||
Section: Panel Details
|
||||
###
|
||||
|
||||
# Public: Gets this panel model's view DOM node.
|
||||
#
|
||||
# Returns an `<atom-panel>` {Element}
|
||||
getView: -> @viewRegistry.getView(this)
|
||||
|
||||
# Public: Gets your panel contents view.
|
||||
#
|
||||
# Returns an {Element} or jQuery element, depeneding on how you created the panel.
|
||||
getItemView: -> @viewRegistry.getView(@item)
|
||||
|
||||
# Public: Returns a {Number} indicating this panel's priority.
|
||||
getPriority: -> @priority
|
||||
|
||||
# Public: Returns a {Boolean} true when the panel is visible.
|
||||
isVisible: -> @visible
|
||||
|
||||
# Public: Hide this panel
|
||||
hide: ->
|
||||
wasVisible = @visible
|
||||
@visible = false
|
||||
@emitter.emit 'did-change-visible', @visible if wasVisible
|
||||
|
||||
# Public: Show this panel
|
||||
show: ->
|
||||
wasVisible = @visible
|
||||
@visible = true
|
||||
@emitter.emit 'did-change-visible', @visible unless wasVisible
|
||||
@@ -0,0 +1,46 @@
|
||||
# Extended: Wraps an {Array} of `String`s. The Array describes a path from the
|
||||
# root of the syntax tree to a token including _all_ scope names for the entire
|
||||
# path.
|
||||
#
|
||||
# Methods that take a `ScopeDescriptor` will also accept an {Array} of {Strings}
|
||||
# scope names e.g. `['.source.js']`.
|
||||
#
|
||||
# You can use `ScopeDescriptor`s to get language-specific config settings via
|
||||
# {Config::get}.
|
||||
#
|
||||
# You should not need to create a `ScopeDescriptor` directly.
|
||||
#
|
||||
# * {Editor::getRootScopeDescriptor} to get the language's descriptor.
|
||||
# * {Editor::scopeDescriptorForBufferPosition} to get the descriptor at a
|
||||
# specific position in the buffer.
|
||||
# * {Cursor::getScopeDescriptor} to get a cursor's descriptor based on position.
|
||||
#
|
||||
# See the [scopes and scope descriptor guide](https://atom.io/docs/v0.138.0/advanced/scopes-and-scope-descriptors)
|
||||
# for more information.
|
||||
module.exports =
|
||||
class ScopeDescriptor
|
||||
@fromObject: (scopes) ->
|
||||
if scopes instanceof ScopeDescriptor
|
||||
scopes
|
||||
else
|
||||
new ScopeDescriptor({scopes})
|
||||
|
||||
###
|
||||
Section: Construction and Destruction
|
||||
###
|
||||
|
||||
# Public: Create a {ScopeDescriptor} object.
|
||||
#
|
||||
# * `object` {Object}
|
||||
# * `scopes` {Array} of {String}s
|
||||
constructor: ({@scopes}) ->
|
||||
|
||||
# Public: Returns an {Array} of {String}s
|
||||
getScopesArray: -> @scopes
|
||||
|
||||
getScopeChain: ->
|
||||
@scopes
|
||||
.map (scope) ->
|
||||
scope = ".#{scope}" unless scope[0] is '.'
|
||||
scope
|
||||
.join(' ')
|
||||
+24
-5
@@ -461,7 +461,8 @@ class Selection extends Model
|
||||
end--
|
||||
@editor.buffer.deleteRows(start, end)
|
||||
|
||||
# Public: Joins the current line with the one below it.
|
||||
# Public: Joins the current line with the one below it. Lines will
|
||||
# be separated by a single space.
|
||||
#
|
||||
# If there selection spans more than one line, all the lines are joined together.
|
||||
joinLines: ->
|
||||
@@ -475,15 +476,33 @@ class Selection extends Model
|
||||
for row in [0...rowCount]
|
||||
@cursor.setBufferPosition([selectedRange.start.row])
|
||||
@cursor.moveToEndOfLine()
|
||||
nextRow = selectedRange.start.row + 1
|
||||
if nextRow <= @editor.buffer.getLastRow() and @editor.buffer.lineLengthForRow(nextRow) > 0
|
||||
@insertText(' ')
|
||||
@cursor.moveToEndOfLine()
|
||||
|
||||
# Remove trailing whitespace from the current line
|
||||
scanRange = @cursor.getCurrentLineBufferRange()
|
||||
trailingWhitespaceRange = null
|
||||
@editor.scanInBufferRange /[ \t]+$/, scanRange, ({range}) ->
|
||||
trailingWhitespaceRange = range
|
||||
if trailingWhitespaceRange?
|
||||
@setBufferRange(trailingWhitespaceRange)
|
||||
@deleteSelectedText()
|
||||
|
||||
currentRow = selectedRange.start.row
|
||||
nextRow = currentRow + 1
|
||||
insertSpace = nextRow <= @editor.buffer.getLastRow() and
|
||||
@editor.buffer.lineLengthForRow(nextRow) > 0 and
|
||||
@editor.buffer.lineLengthForRow(currentRow) > 0
|
||||
@insertText(' ') if insertSpace
|
||||
|
||||
@cursor.moveToEndOfLine()
|
||||
|
||||
# Remove leading whitespace from the line below
|
||||
@modifySelection =>
|
||||
@cursor.moveRight()
|
||||
@cursor.moveToFirstCharacterOfLine()
|
||||
@deleteSelectedText()
|
||||
|
||||
@cursor.moveLeft() if insertSpace
|
||||
|
||||
if joinMarker?
|
||||
newSelectedRange = joinMarker.getBufferRange()
|
||||
@setBufferRange(newSelectedRange)
|
||||
|
||||
@@ -19,7 +19,8 @@ NativeEventNames = new Set
|
||||
NativeEventNames.add(nativeEvent) for nativeEvent in ["blur", "focus", "focusin",
|
||||
"focusout", "load", "resize", "scroll", "unload", "click", "dblclick", "mousedown",
|
||||
"mouseup", "mousemove", "mouseover", "mouseout", "mouseenter", "mouseleave", "change",
|
||||
"select", "submit", "keydown", "keypress", "keyup", "error", "contextmenu"]
|
||||
"select", "submit", "keydown", "keypress", "keyup", "error", "contextmenu", "textInput",
|
||||
"textinput"]
|
||||
|
||||
JQueryTrigger = jQuery.fn.trigger
|
||||
jQuery.fn.trigger = (eventName, data) ->
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
{Emitter, Disposable} = require 'event-kit'
|
||||
|
||||
module.exports =
|
||||
class StyleManager
|
||||
constructor: ->
|
||||
@emitter = new Emitter
|
||||
@styleElements = []
|
||||
@styleElementsBySourcePath = {}
|
||||
|
||||
observeStyleElements: (callback) ->
|
||||
callback(styleElement) for styleElement in @getStyleElements()
|
||||
@onDidAddStyleElement(callback)
|
||||
|
||||
onDidAddStyleElement: (callback) ->
|
||||
@emitter.on 'did-add-style-element', callback
|
||||
|
||||
onDidRemoveStyleElement: (callback) ->
|
||||
@emitter.on 'did-remove-style-element', callback
|
||||
|
||||
onDidUpdateStyleElement: (callback) ->
|
||||
@emitter.on 'did-update-style-element', callback
|
||||
|
||||
getStyleElements: ->
|
||||
@styleElements.slice()
|
||||
|
||||
addStyleSheet: (source, params) ->
|
||||
sourcePath = params?.sourcePath
|
||||
group = params?.group
|
||||
|
||||
if sourcePath? and styleElement = @styleElementsBySourcePath[sourcePath]
|
||||
updated = true
|
||||
else
|
||||
styleElement = document.createElement('style')
|
||||
if sourcePath?
|
||||
styleElement.sourcePath = sourcePath
|
||||
styleElement.setAttribute('source-path', sourcePath)
|
||||
|
||||
if context?
|
||||
styleElement.context = context
|
||||
styleElement.setAttribute('context', context)
|
||||
|
||||
if group?
|
||||
styleElement.group = group
|
||||
styleElement.setAttribute('group', group)
|
||||
|
||||
styleElement.textContent = source
|
||||
|
||||
if updated
|
||||
@emitter.emit 'did-update-style-element', styleElement
|
||||
else
|
||||
@addStyleElement(styleElement)
|
||||
|
||||
new Disposable => @removeStyleElement(styleElement)
|
||||
|
||||
addStyleElement: (styleElement) ->
|
||||
{sourcePath, group} = styleElement
|
||||
|
||||
if group?
|
||||
for existingElement, index in @styleElements
|
||||
if existingElement.group is group
|
||||
insertIndex = index + 1
|
||||
else
|
||||
break if insertIndex?
|
||||
insertIndex ?= @styleElements.length
|
||||
|
||||
@styleElements.splice(insertIndex, 0, styleElement)
|
||||
@styleElementsBySourcePath[sourcePath] ?= styleElement if sourcePath?
|
||||
@emitter.emit 'did-add-style-element', styleElement
|
||||
|
||||
removeStyleElement: (styleElement) ->
|
||||
index = @styleElements.indexOf(styleElement)
|
||||
unless index is -1
|
||||
@styleElements.splice(index, 1)
|
||||
delete @styleElementsBySourcePath[styleElement.sourcePath] if styleElement.sourcePath?
|
||||
@emitter.emit 'did-remove-style-element', styleElement
|
||||
|
||||
getSnapshot: ->
|
||||
@styleElements.slice()
|
||||
|
||||
restoreSnapshot: (styleElementsToRestore) ->
|
||||
for styleElement in @getStyleElements()
|
||||
@removeStyleElement(styleElement) unless styleElement in styleElementsToRestore
|
||||
|
||||
existingStyleElements = @getStyleElements()
|
||||
for styleElement in styleElementsToRestore
|
||||
@addStyleElement(styleElement) unless styleElement in existingStyleElements
|
||||
@@ -0,0 +1,50 @@
|
||||
{Emitter, CompositeDisposable} = require 'event-kit'
|
||||
|
||||
class StylesElement extends HTMLElement
|
||||
createdCallback: ->
|
||||
@emitter = new Emitter
|
||||
|
||||
onDidAddStyleElement: (callback) ->
|
||||
@emitter.on 'did-add-style-element', callback
|
||||
|
||||
onDidRemoveStyleElement: (callback) ->
|
||||
@emitter.on 'did-remove-style-element', callback
|
||||
|
||||
onDidUpdateStyleElement: (callback) ->
|
||||
@emitter.on 'did-update-style-element', callback
|
||||
|
||||
attachedCallback: ->
|
||||
@subscriptions = new CompositeDisposable
|
||||
@styleElementClonesByOriginalElement = new WeakMap
|
||||
@subscriptions.add atom.styles.observeStyleElements(@styleElementAdded.bind(this))
|
||||
@subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this))
|
||||
@subscriptions.add atom.styles.onDidUpdateStyleElement(@styleElementUpdated.bind(this))
|
||||
|
||||
styleElementAdded: (styleElement) ->
|
||||
styleElementClone = styleElement.cloneNode(true)
|
||||
@styleElementClonesByOriginalElement.set(styleElement, styleElementClone)
|
||||
|
||||
group = styleElement.getAttribute('group')
|
||||
if group?
|
||||
for child in @children
|
||||
if child.getAttribute('group') is group and child.nextSibling?.getAttribute('group') isnt group
|
||||
insertBefore = child.nextSibling
|
||||
break
|
||||
|
||||
@insertBefore(styleElementClone, insertBefore)
|
||||
@emitter.emit 'did-add-style-element', styleElementClone
|
||||
|
||||
styleElementRemoved: (styleElement) ->
|
||||
styleElementClone = @styleElementClonesByOriginalElement.get(styleElement)
|
||||
styleElementClone.remove()
|
||||
@emitter.emit 'did-remove-style-element', styleElementClone
|
||||
|
||||
styleElementUpdated: (styleElement) ->
|
||||
styleElementClone = @styleElementClonesByOriginalElement.get(styleElement)
|
||||
styleElementClone.textContent = styleElement.textContent
|
||||
@emitter.emit 'did-update-style-element', styleElementClone
|
||||
|
||||
detachedCallback: ->
|
||||
@subscriptions.dispose()
|
||||
|
||||
module.exports = StylesElement = document.registerElement 'atom-styles', prototype: StylesElement.prototype
|
||||
+1
-1
@@ -32,7 +32,7 @@ class Syntax extends GrammarRegistry
|
||||
serialize: ->
|
||||
{deserializer: @constructor.name, @grammarOverridesByPath}
|
||||
|
||||
createToken: (value, scopeDescriptor) -> new Token({value, scopeDescriptor})
|
||||
createToken: (value, scopes) -> new Token({value, scopes})
|
||||
|
||||
# Deprecated: Used by settings-view to display snippets for packages
|
||||
@::accessor 'propertyStore', ->
|
||||
|
||||
@@ -482,6 +482,16 @@ TextEditorComponent = React.createClass
|
||||
|
||||
# Only scroll in one direction at a time
|
||||
{wheelDeltaX, wheelDeltaY} = event
|
||||
|
||||
# Ctrl+MouseWheel adjusts font size.
|
||||
if event.ctrlKey and atom.config.get('editor.zoomFontWhenCtrlScrolling')
|
||||
if wheelDeltaY > 0
|
||||
atom.workspace.increaseFontSize()
|
||||
else if wheelDeltaY < 0
|
||||
atom.workspace.decreaseFontSize()
|
||||
event.preventDefault()
|
||||
return
|
||||
|
||||
if Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY)
|
||||
# Scrolling horizontally
|
||||
previousScrollLeft = editor.getScrollLeft()
|
||||
|
||||
+17
-14
@@ -487,7 +487,7 @@ class TextEditor extends Model
|
||||
when 'decoration-updated'
|
||||
deprecate("Use Decoration::onDidChangeProperties instead. You will get the decoration back from `TextEditor::decorateMarker()`")
|
||||
when 'decoration-changed'
|
||||
deprecate("Use Marker::onDidChange instead. eg. `editor::decorateMarker(...).getMarker().onDidChange()`")
|
||||
deprecate("Use Marker::onDidChange instead. e.g. `editor::decorateMarker(...).getMarker().onDidChange()`")
|
||||
|
||||
when 'screen-lines-changed'
|
||||
deprecate("Use TextEditor::onDidChange instead")
|
||||
@@ -1216,7 +1216,7 @@ class TextEditor extends Model
|
||||
# ## Arguments
|
||||
#
|
||||
# * `marker` A {Marker} you want this decoration to follow.
|
||||
# * `decorationParams` An {Object} representing the decoration eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
# * `decorationParams` An {Object} representing the decoration e.g. `{type: 'gutter', class: 'linter-error'}`
|
||||
# * `type` There are a few supported decoration types: `gutter`, `line`, and `highlight`
|
||||
# * `class` This CSS class will be applied to the decorated line number,
|
||||
# line, or highlight.
|
||||
@@ -2370,8 +2370,8 @@ class TextEditor extends Model
|
||||
Section: Managing Syntax Scopes
|
||||
###
|
||||
|
||||
# Essential: Returns the scope descriptor that includes the language. eg.
|
||||
# `['.source.ruby']`, or `['.source.coffee']`. You can use this with
|
||||
# Essential: Returns a {ScopeDescriptor} that includes this editor's language.
|
||||
# e.g. `['.source.ruby']`, or `['.source.coffee']`. You can use this with
|
||||
# {Config::get} to get language specific config values.
|
||||
getRootScopeDescriptor: ->
|
||||
@displayBuffer.getRootScopeDescriptor()
|
||||
@@ -2385,11 +2385,12 @@ class TextEditor extends Model
|
||||
#
|
||||
# * `bufferPosition` A {Point} or {Array} of [row, column].
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
scopeDescriptorForBufferPosition: (bufferPosition) -> @displayBuffer.scopeDescriptorForBufferPosition(bufferPosition)
|
||||
# Returns a {ScopeDescriptor}.
|
||||
scopeDescriptorForBufferPosition: (bufferPosition) ->
|
||||
@displayBuffer.scopeDescriptorForBufferPosition(bufferPosition)
|
||||
scopesForBufferPosition: (bufferPosition) ->
|
||||
deprecate 'Use ::scopeDescriptorForBufferPosition instead'
|
||||
@scopeDescriptorForBufferPosition(bufferPosition)
|
||||
deprecate 'Use ::scopeDescriptorForBufferPosition instead. The return value has changed! It now returns a `ScopeDescriptor`'
|
||||
@scopeDescriptorForBufferPosition(bufferPosition).getScopesArray()
|
||||
|
||||
# Extended: Get the range in buffer coordinates of all tokens surrounding the
|
||||
# cursor that match the given scope selector.
|
||||
@@ -2397,14 +2398,16 @@ class TextEditor extends Model
|
||||
# For example, if you wanted to find the string surrounding the cursor, you
|
||||
# could call `editor.bufferRangeForScopeAtCursor(".string.quoted")`.
|
||||
#
|
||||
# * `scopeSelector` {String} selector. e.g. `'.source.ruby'`
|
||||
#
|
||||
# Returns a {Range}.
|
||||
bufferRangeForScopeAtCursor: (selector) ->
|
||||
@displayBuffer.bufferRangeForScopeAtPosition(selector, @getCursorBufferPosition())
|
||||
bufferRangeForScopeAtCursor: (scopeSelector) ->
|
||||
@displayBuffer.bufferRangeForScopeAtPosition(scopeSelector, @getCursorBufferPosition())
|
||||
|
||||
# Extended: Determine if the given row is entirely a comment
|
||||
isBufferRowCommented: (bufferRow) ->
|
||||
if match = @lineTextForBufferRow(bufferRow).match(/\S/)
|
||||
scopeDescriptor = @tokenForBufferPosition([bufferRow, match.index]).scopeDescriptor
|
||||
scopeDescriptor = @tokenForBufferPosition([bufferRow, match.index]).scopes
|
||||
@commentScopeSelector ?= new TextMateScopeSelector('comment.*')
|
||||
@commentScopeSelector.matches(scopeDescriptor)
|
||||
|
||||
@@ -2415,10 +2418,10 @@ class TextEditor extends Model
|
||||
tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition)
|
||||
|
||||
scopesAtCursor: ->
|
||||
deprecate 'Use editor.getLastCursor().scopesAtCursor() instead'
|
||||
@getLastCursor().getScopeDescriptor()
|
||||
deprecate 'Use editor.getLastCursor().getScopeDescriptor() instead'
|
||||
@getLastCursor().getScopeDescriptor().getScopesArray()
|
||||
getCursorScopes: ->
|
||||
deprecate 'Use editor.getLastCursor().scopesAtCursor() instead'
|
||||
deprecate 'Use editor.getLastCursor().getScopeDescriptor() instead'
|
||||
@scopesAtCursor()
|
||||
|
||||
###
|
||||
|
||||
+34
-50
@@ -19,9 +19,39 @@ class ThemeManager
|
||||
|
||||
constructor: ({@packageManager, @resourcePath, @configDirPath, @safeMode}) ->
|
||||
@emitter = new Emitter
|
||||
@styleSheetDisposablesBySourcePath = {}
|
||||
@lessCache = null
|
||||
@initialLoadComplete = false
|
||||
@packageManager.registerPackageActivator(this, ['theme'])
|
||||
@sheetsByStyleElement = new WeakMap
|
||||
|
||||
stylesElement = document.head.querySelector('atom-styles')
|
||||
stylesElement.onDidAddStyleElement @styleElementAdded.bind(this)
|
||||
stylesElement.onDidRemoveStyleElement @styleElementRemoved.bind(this)
|
||||
stylesElement.onDidUpdateStyleElement @styleElementUpdated.bind(this)
|
||||
|
||||
styleElementAdded: (styleElement) ->
|
||||
{sheet} = styleElement
|
||||
@sheetsByStyleElement.set(styleElement, sheet)
|
||||
@emit 'stylesheet-added', sheet
|
||||
@emitter.emit 'did-add-stylesheet', sheet
|
||||
@emit 'stylesheets-changed'
|
||||
@emitter.emit 'did-change-stylesheets'
|
||||
|
||||
styleElementRemoved: (styleElement) ->
|
||||
sheet = @sheetsByStyleElement.get(styleElement)
|
||||
@emit 'stylesheet-removed', sheet
|
||||
@emitter.emit 'did-remove-stylesheet', sheet
|
||||
@emit 'stylesheets-changed'
|
||||
@emitter.emit 'did-change-stylesheets'
|
||||
|
||||
styleElementUpdated: ({sheet}) ->
|
||||
@emit 'stylesheet-removed', sheet
|
||||
@emitter.emit 'did-remove-stylesheet', sheet
|
||||
@emit 'stylesheet-added', sheet
|
||||
@emitter.emit 'did-add-stylesheet', sheet
|
||||
@emit 'stylesheets-changed'
|
||||
@emitter.emit 'did-change-stylesheets'
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
@@ -188,7 +218,6 @@ class ThemeManager
|
||||
if fullPath = @resolveStylesheet(stylesheetPath)
|
||||
content = @loadStylesheet(fullPath)
|
||||
@applyStylesheet(fullPath, content, type)
|
||||
new Disposable => @removeStylesheet(fullPath)
|
||||
else
|
||||
throw new Error("Could not find a file at path '#{stylesheetPath}'")
|
||||
|
||||
@@ -204,8 +233,7 @@ class ThemeManager
|
||||
|
||||
@userStylesheetPath = userStylesheetPath
|
||||
@userStylesheetFile = new File(userStylesheetPath)
|
||||
@userStylesheetFile.on 'contents-changed moved removed', =>
|
||||
@loadUserStylesheet()
|
||||
@userStylesheetFile.on 'contents-changed moved removed', => @loadUserStylesheet()
|
||||
userStylesheetContents = @loadStylesheet(userStylesheetPath, true)
|
||||
@applyStylesheet(userStylesheetPath, userStylesheetContents, 'userTheme')
|
||||
|
||||
@@ -219,7 +247,7 @@ class ThemeManager
|
||||
@requireStylesheet(nativeStylesheetPath)
|
||||
|
||||
stylesheetElementForId: (id) ->
|
||||
document.head.querySelector("""style[id="#{id}"]""")
|
||||
document.head.querySelector("atom-styles style[source-path=\"#{id}\"]")
|
||||
|
||||
resolveStylesheet: (stylesheetPath) ->
|
||||
if path.extname(stylesheetPath).length > 0
|
||||
@@ -256,40 +284,10 @@ class ThemeManager
|
||||
"""
|
||||
|
||||
removeStylesheet: (stylesheetPath) ->
|
||||
fullPath = @resolveStylesheet(stylesheetPath) ? stylesheetPath
|
||||
element = @stylesheetElementForId(@stringToId(fullPath))
|
||||
if element?
|
||||
{sheet} = element
|
||||
element.remove()
|
||||
@emit 'stylesheet-removed', sheet
|
||||
@emitter.emit 'did-remove-stylesheet', sheet
|
||||
@emit 'stylesheets-changed'
|
||||
@emitter.emit 'did-change-stylesheets'
|
||||
@styleSheetDisposablesBySourcePath[stylesheetPath]?.dispose()
|
||||
|
||||
applyStylesheet: (path, text, type='bundled') ->
|
||||
styleId = @stringToId(path)
|
||||
styleElement = @stylesheetElementForId(styleId)
|
||||
|
||||
if styleElement?
|
||||
@emit 'stylesheet-removed', styleElement.sheet
|
||||
@emitter.emit 'did-remove-stylesheet', styleElement.sheet
|
||||
styleElement.textContent = text
|
||||
else
|
||||
styleElement = document.createElement('style')
|
||||
styleElement.setAttribute('class', type)
|
||||
styleElement.setAttribute('id', styleId)
|
||||
styleElement.textContent = text
|
||||
|
||||
elementToInsertBefore = _.last(document.head.querySelectorAll("style.#{type}"))?.nextElementSibling
|
||||
if elementToInsertBefore?
|
||||
document.head.insertBefore(styleElement, elementToInsertBefore)
|
||||
else
|
||||
document.head.appendChild(styleElement)
|
||||
|
||||
@emit 'stylesheet-added', styleElement.sheet
|
||||
@emitter.emit 'did-add-stylesheet', styleElement.sheet
|
||||
@emit 'stylesheets-changed'
|
||||
@emitter.emit 'did-change-stylesheets'
|
||||
@styleSheetDisposablesBySourcePath[path] = atom.styles.addStyleSheet(text, sourcePath: path, group: type)
|
||||
|
||||
###
|
||||
Section: Private
|
||||
@@ -358,17 +356,3 @@ class ThemeManager
|
||||
themePaths.push(path.join(themePath, Package.stylesheetsDir))
|
||||
|
||||
themePaths.filter (themePath) -> fs.isDirectorySync(themePath)
|
||||
|
||||
updateGlobalEditorStyle: (property, value) ->
|
||||
unless styleNode = @stylesheetElementForId('global-editor-styles')
|
||||
@applyStylesheet('global-editor-styles', 'atom-text-editor {}')
|
||||
styleNode = @stylesheetElementForId('global-editor-styles')
|
||||
|
||||
{sheet} = styleNode
|
||||
editorRule = sheet.cssRules[0]
|
||||
editorRule.style[property] = value
|
||||
|
||||
@emit 'stylesheet-updated', sheet
|
||||
@emitter.emit 'did-update-stylesheet', sheet
|
||||
@emit 'stylesheets-changed'
|
||||
@emitter.emit 'did-change-stylesheets'
|
||||
|
||||
+12
-19
@@ -14,35 +14,28 @@ module.exports =
|
||||
class Token
|
||||
value: null
|
||||
hasPairedCharacter: false
|
||||
scopeDescriptor: null
|
||||
scopes: null
|
||||
isAtomic: null
|
||||
isHardTab: null
|
||||
firstNonWhitespaceIndex: null
|
||||
firstTrailingWhitespaceIndex: null
|
||||
hasInvisibleCharacters: false
|
||||
|
||||
Object.defineProperty @::, 'scopes', get: ->
|
||||
deprecate 'Use ::scopeDescriptor instead'
|
||||
@scopeDescriptor
|
||||
|
||||
constructor: ({@value, @scopeDescriptor, @isAtomic, @bufferDelta, @isHardTab}) ->
|
||||
constructor: ({@value, @scopes, @isAtomic, @bufferDelta, @isHardTab}) ->
|
||||
@screenDelta = @value.length
|
||||
@bufferDelta ?= @screenDelta
|
||||
@hasPairedCharacter = textUtils.hasPairedCharacter(@value)
|
||||
|
||||
isEqual: (other) ->
|
||||
# TODO: scopes is deprecated. This is here for the sake of lang package tests
|
||||
scopeDescriptor = other.scopeDescriptor ? other.scopes
|
||||
deprecate 'Test the Token for `scopeDescriptor` rather than `scopes`' if other.scopes?
|
||||
|
||||
@value == other.value and _.isEqual(@scopeDescriptor, scopeDescriptor) and !!@isAtomic == !!other.isAtomic
|
||||
@value == other.value and _.isEqual(@scopes, other.scopes) and !!@isAtomic == !!other.isAtomic
|
||||
|
||||
isBracket: ->
|
||||
/^meta\.brace\b/.test(_.last(@scopeDescriptor))
|
||||
/^meta\.brace\b/.test(_.last(@scopes))
|
||||
|
||||
splitAt: (splitIndex) ->
|
||||
leftToken = new Token(value: @value.substring(0, splitIndex), scopeDescriptor: @scopeDescriptor)
|
||||
rightToken = new Token(value: @value.substring(splitIndex), scopeDescriptor: @scopeDescriptor)
|
||||
leftToken = new Token(value: @value.substring(0, splitIndex), scopes: @scopes)
|
||||
rightToken = new Token(value: @value.substring(splitIndex), scopes: @scopes)
|
||||
|
||||
if @firstNonWhitespaceIndex?
|
||||
leftToken.firstNonWhitespaceIndex = Math.min(splitIndex, @firstNonWhitespaceIndex)
|
||||
@@ -101,7 +94,7 @@ class Token
|
||||
else
|
||||
breakOutLeadingSoftTabs = false
|
||||
value = match[0]
|
||||
token = new Token({value, @scopeDescriptor})
|
||||
token = new Token({value, @scopes})
|
||||
column += token.value.length
|
||||
outputTokens.push(token)
|
||||
|
||||
@@ -115,7 +108,7 @@ class Token
|
||||
while index < @value.length
|
||||
if textUtils.isPairedCharacter(@value, index)
|
||||
if nonPairStart isnt index
|
||||
outputTokens.push(new Token({value: @value[nonPairStart...index], @scopeDescriptor}))
|
||||
outputTokens.push(new Token({value: @value[nonPairStart...index], @scopes}))
|
||||
outputTokens.push(@buildPairedCharacterToken(@value, index))
|
||||
index += 2
|
||||
nonPairStart = index
|
||||
@@ -123,14 +116,14 @@ class Token
|
||||
index++
|
||||
|
||||
if nonPairStart isnt index
|
||||
outputTokens.push(new Token({value: @value[nonPairStart...index], @scopeDescriptor}))
|
||||
outputTokens.push(new Token({value: @value[nonPairStart...index], @scopes}))
|
||||
|
||||
outputTokens
|
||||
|
||||
buildPairedCharacterToken: (value, index) ->
|
||||
new Token(
|
||||
value: value[index..index + 1]
|
||||
scopeDescriptor: @scopeDescriptor
|
||||
scopes: @scopes
|
||||
isAtomic: true
|
||||
)
|
||||
|
||||
@@ -144,7 +137,7 @@ class Token
|
||||
tabStop = tabLength - (column % tabLength)
|
||||
new Token(
|
||||
value: _.multiplyString(" ", tabStop)
|
||||
scopeDescriptor: @scopeDescriptor
|
||||
scopes: @scopes
|
||||
bufferDelta: if isHardTab then 1 else tabStop
|
||||
isAtomic: true
|
||||
isHardTab: isHardTab
|
||||
@@ -155,7 +148,7 @@ class Token
|
||||
|
||||
matchesScopeSelector: (selector) ->
|
||||
targetClasses = selector.replace(StartDotRegex, '').split('.')
|
||||
_.any @scopeDescriptor, (scope) ->
|
||||
_.any @scopes, (scope) ->
|
||||
scopeClasses = scope.split('.')
|
||||
_.isSubset(targetClasses, scopeClasses)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ EmitterMixin = require('emissary').Emitter
|
||||
Serializable = require 'serializable'
|
||||
TokenizedLine = require './tokenized-line'
|
||||
Token = require './token'
|
||||
ScopeDescriptor = require './scope-descriptor'
|
||||
Grim = require 'grim'
|
||||
|
||||
module.exports =
|
||||
@@ -79,7 +80,7 @@ class TokenizedBuffer extends Model
|
||||
return if grammar is @grammar
|
||||
@unsubscribe(@grammar) if @grammar
|
||||
@grammar = grammar
|
||||
@rootScopeDescriptor = [@grammar.scopeName]
|
||||
@rootScopeDescriptor = new ScopeDescriptor(scopes: [@grammar.scopeName])
|
||||
@currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @buffer.getText())
|
||||
@subscribe @grammar.onDidUpdate => @retokenizeLines()
|
||||
|
||||
@@ -105,7 +106,7 @@ class TokenizedBuffer extends Model
|
||||
hasTokenForSelector: (selector) ->
|
||||
for {tokens} in @tokenizedLines
|
||||
for token in tokens
|
||||
return true if selector.matches(token.scopeDescriptor)
|
||||
return true if selector.matches(token.scopes)
|
||||
false
|
||||
|
||||
retokenizeLines: ->
|
||||
@@ -247,7 +248,7 @@ class TokenizedBuffer extends Model
|
||||
|
||||
buildPlaceholderTokenizedLineForRow: (row) ->
|
||||
line = @buffer.lineForRow(row)
|
||||
tokens = [new Token(value: line, scopeDescriptor: [@grammar.scopeName])]
|
||||
tokens = [new Token(value: line, scopes: [@grammar.scopeName])]
|
||||
tabLength = @getTabLength()
|
||||
indentLevel = @indentLevelForRow(row)
|
||||
lineEnding = @buffer.lineEndingForRow(row)
|
||||
@@ -303,7 +304,7 @@ class TokenizedBuffer extends Model
|
||||
0
|
||||
|
||||
scopeDescriptorForPosition: (position) ->
|
||||
@tokenForPosition(position).scopeDescriptor
|
||||
new ScopeDescriptor(scopes: @tokenForPosition(position).scopes)
|
||||
|
||||
tokenForPosition: (position) ->
|
||||
{row, column} = Point.fromObject(position)
|
||||
|
||||
@@ -194,9 +194,9 @@ class TokenizedLine
|
||||
|
||||
isComment: ->
|
||||
for token in @tokens
|
||||
continue if token.scopeDescriptor.length is 1
|
||||
continue if token.scopes.length is 1
|
||||
continue if token.isOnlyWhitespace()
|
||||
for scope in token.scopeDescriptor
|
||||
for scope in token.scopes
|
||||
return true if _.contains(scope.split('.'), 'comment')
|
||||
break
|
||||
false
|
||||
@@ -226,7 +226,7 @@ class TokenizedLine
|
||||
|
||||
scopeStack = []
|
||||
for token in @tokens
|
||||
@updateScopeStack(scopeStack, token.scopeDescriptor)
|
||||
@updateScopeStack(scopeStack, token.scopes)
|
||||
_.last(scopeStack).children.push(token)
|
||||
|
||||
@scopeTree = scopeStack[0]
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
# Like sands through the hourglass, so are the days of our lives.
|
||||
startTime = Date.now()
|
||||
|
||||
require './window'
|
||||
|
||||
Atom = require './atom'
|
||||
window.atom = Atom.loadOrCreate('editor')
|
||||
atom.initialize()
|
||||
atom.startEditorWindow()
|
||||
window.atom.loadTime = Date.now() - startTime
|
||||
console.log "Window load time: #{atom.getWindowLoadTime()}ms"
|
||||
|
||||
# Workaround for focus getting cleared upon window creation
|
||||
windowFocused = ->
|
||||
|
||||
@@ -8,8 +8,11 @@ WorkspaceView = null
|
||||
|
||||
module.exports =
|
||||
class WorkspaceElement extends HTMLElement
|
||||
globalTextEditorStyleSheet: null
|
||||
|
||||
createdCallback: ->
|
||||
@subscriptions = new CompositeDisposable
|
||||
@initializeGlobalTextEditorStyleSheet()
|
||||
@initializeContent()
|
||||
@observeScrollbarStyle()
|
||||
@observeTextEditorFontConfig()
|
||||
@@ -22,6 +25,10 @@ class WorkspaceElement extends HTMLElement
|
||||
detachedCallback: ->
|
||||
@model.destroy()
|
||||
|
||||
initializeGlobalTextEditorStyleSheet: ->
|
||||
atom.styles.addStyleSheet('atom-text-editor {}', sourcePath: 'global-text-editor-styles')
|
||||
@globalTextEditorStyleSheet = document.head.querySelector('style[source-path="global-text-editor-styles"]').sheet
|
||||
|
||||
initializeContent: ->
|
||||
@classList.add 'workspace'
|
||||
@setAttribute 'tabindex', -1
|
||||
@@ -46,9 +53,9 @@ class WorkspaceElement extends HTMLElement
|
||||
@classList.add("scrollbars-visible-when-scrolling")
|
||||
|
||||
observeTextEditorFontConfig: ->
|
||||
@subscriptions.add atom.config.observe 'editor.fontSize', @setTextEditorFontSize
|
||||
@subscriptions.add atom.config.observe 'editor.fontFamily', @setTextEditorFontFamily
|
||||
@subscriptions.add atom.config.observe 'editor.lineHeight', @setTextEditorLineHeight
|
||||
@subscriptions.add atom.config.observe 'editor.fontSize', @setTextEditorFontSize.bind(this)
|
||||
@subscriptions.add atom.config.observe 'editor.fontFamily', @setTextEditorFontFamily.bind(this)
|
||||
@subscriptions.add atom.config.observe 'editor.lineHeight', @setTextEditorLineHeight.bind(this)
|
||||
|
||||
createSpacePenShim: ->
|
||||
WorkspaceView ?= require './workspace-view'
|
||||
@@ -65,16 +72,33 @@ class WorkspaceElement extends HTMLElement
|
||||
window.addEventListener 'focus', handleWindowFocus
|
||||
@subscriptions.add(new Disposable -> window.removeEventListener 'focus', handleWindowFocus)
|
||||
|
||||
@panelContainers =
|
||||
top: @model.panelContainers.top.getView()
|
||||
left: @model.panelContainers.left.getView()
|
||||
right: @model.panelContainers.right.getView()
|
||||
bottom: @model.panelContainers.bottom.getView()
|
||||
|
||||
@horizontalAxis.insertBefore(@panelContainers.left, @verticalAxis)
|
||||
@horizontalAxis.appendChild(@panelContainers.right)
|
||||
|
||||
@verticalAxis.insertBefore(@panelContainers.top, @paneContainer)
|
||||
@verticalAxis.appendChild(@panelContainers.bottom)
|
||||
|
||||
@__spacePenView.setModel(@model)
|
||||
|
||||
setTextEditorFontSize: (fontSize) ->
|
||||
atom.themes.updateGlobalEditorStyle('font-size', fontSize + 'px')
|
||||
@updateGlobalEditorStyle('font-size', fontSize + 'px')
|
||||
|
||||
setTextEditorFontFamily: (fontFamily) ->
|
||||
atom.themes.updateGlobalEditorStyle('font-family', fontFamily)
|
||||
@updateGlobalEditorStyle('font-family', fontFamily)
|
||||
|
||||
setTextEditorLineHeight: (lineHeight) ->
|
||||
atom.themes.updateGlobalEditorStyle('line-height', lineHeight)
|
||||
@updateGlobalEditorStyle('line-height', lineHeight)
|
||||
|
||||
updateGlobalEditorStyle: (property, value) ->
|
||||
editorRule = @globalTextEditorStyleSheet.cssRules[0]
|
||||
editorRule.style[property] = value
|
||||
atom.themes.emitter.emit 'did-update-stylesheet', @globalTextEditorStyleSheet
|
||||
|
||||
handleFocus: (event) ->
|
||||
@model.getActivePane().activate()
|
||||
|
||||
@@ -141,59 +141,36 @@ class WorkspaceView extends View
|
||||
Section: Adding elements to the workspace
|
||||
###
|
||||
|
||||
# Essential: Prepend an element or view to the panels at the top of the
|
||||
# workspace.
|
||||
#
|
||||
# * `element` jQuery object or DOM element
|
||||
prependToTop: (element) ->
|
||||
deprecate 'Please use Workspace::addTopPanel() instead'
|
||||
@vertical.prepend(element)
|
||||
|
||||
# Essential: Append an element or view to the panels at the top of the workspace.
|
||||
#
|
||||
# * `element` jQuery object or DOM element
|
||||
appendToTop: (element) ->
|
||||
deprecate 'Please use Workspace::addTopPanel() instead'
|
||||
@panes.before(element)
|
||||
|
||||
# Essential: Prepend an element or view to the panels at the bottom of the
|
||||
# workspace.
|
||||
#
|
||||
# * `element` jQuery object or DOM element
|
||||
prependToBottom: (element) ->
|
||||
deprecate 'Please use Workspace::addBottomPanel() instead'
|
||||
@panes.after(element)
|
||||
|
||||
# Essential: Append an element or view to the panels at the bottom of the
|
||||
# workspace.
|
||||
#
|
||||
# * `element` jQuery object or DOM element
|
||||
appendToBottom: (element) ->
|
||||
deprecate 'Please use Workspace::addBottomPanel() instead'
|
||||
@vertical.append(element)
|
||||
|
||||
# Essential: Prepend an element or view to the panels at the left of the
|
||||
# workspace.
|
||||
#
|
||||
# * `element` jQuery object or DOM element
|
||||
prependToLeft: (element) ->
|
||||
deprecate 'Please use Workspace::addLeftPanel() instead'
|
||||
@horizontal.prepend(element)
|
||||
|
||||
# Essential: Append an element or view to the panels at the left of the
|
||||
# workspace.
|
||||
#
|
||||
# * `element` jQuery object or DOM element
|
||||
appendToLeft: (element) ->
|
||||
deprecate 'Please use Workspace::addLeftPanel() instead'
|
||||
@vertical.before(element)
|
||||
|
||||
# Essential: Prepend an element or view to the panels at the right of the
|
||||
# workspace.
|
||||
#
|
||||
# * `element` jQuery object or DOM element
|
||||
prependToRight: (element) ->
|
||||
deprecate 'Please use Workspace::addRightPanel() instead'
|
||||
@vertical.after(element)
|
||||
|
||||
# Essential: Append an element or view to the panels at the right of the
|
||||
# workspace.
|
||||
#
|
||||
# * `element` jQuery object or DOM element
|
||||
appendToRight: (element) ->
|
||||
deprecate 'Please use Workspace::addRightPanel() instead'
|
||||
@horizontal.append(element)
|
||||
|
||||
###
|
||||
|
||||
+89
-1
@@ -10,6 +10,10 @@ Grim = require 'grim'
|
||||
TextEditor = require './text-editor'
|
||||
PaneContainer = require './pane-container'
|
||||
Pane = require './pane'
|
||||
Panel = require './panel'
|
||||
PanelElement = require './panel-element'
|
||||
PanelContainer = require './panel-container'
|
||||
PanelContainerElement = require './panel-container-element'
|
||||
ViewRegistry = require './view-registry'
|
||||
WorkspaceElement = require './workspace-element'
|
||||
|
||||
@@ -45,6 +49,12 @@ class Workspace extends Model
|
||||
@paneContainer ?= new PaneContainer({@viewRegistry})
|
||||
@paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed)
|
||||
|
||||
@panelContainers =
|
||||
top: new PanelContainer({@viewRegistry, location: 'top'})
|
||||
left: new PanelContainer({@viewRegistry, location: 'left'})
|
||||
right: new PanelContainer({@viewRegistry, location: 'right'})
|
||||
bottom: new PanelContainer({@viewRegistry, location: 'bottom'})
|
||||
|
||||
@subscribeToActiveItem()
|
||||
|
||||
@addOpener (filePath) =>
|
||||
@@ -62,6 +72,14 @@ class Workspace extends Model
|
||||
modelConstructor: Workspace
|
||||
viewConstructor: WorkspaceElement
|
||||
|
||||
@addViewProvider
|
||||
modelConstructor: PanelContainer
|
||||
viewConstructor: PanelContainerElement
|
||||
|
||||
@addViewProvider
|
||||
modelConstructor: Panel
|
||||
viewConstructor: PanelElement
|
||||
|
||||
# Called by the Serializable mixin during deserialization
|
||||
deserializeParams: (params) ->
|
||||
for packageName in params.packagesWithActiveGrammars ? []
|
||||
@@ -578,6 +596,76 @@ class Workspace extends Model
|
||||
@paneContainer.destroy()
|
||||
@activeItemSubscriptions?.dispose()
|
||||
|
||||
|
||||
###
|
||||
Section: Panels
|
||||
###
|
||||
|
||||
# Essential: Adds a panel item to the bottom of the editor window.
|
||||
#
|
||||
# * `options` {Object}
|
||||
# * `item` Your panel content. It can be DOM element, a jQuery element, or
|
||||
# a model with a view registered via {::addViewProvider}. We recommend the
|
||||
# latter. See {::addViewProvider} for more information.
|
||||
# * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
|
||||
# (default: true)
|
||||
# * `priority` (optional) {Number} Determines stacking order. Lower priority items are
|
||||
# forced closer to the edges of the window. (default: 100)
|
||||
#
|
||||
# Returns a {Panel}
|
||||
addBottomPanel: (options) ->
|
||||
@addPanel('bottom', options)
|
||||
|
||||
# Essential: Adds a panel item to the left of the editor window.
|
||||
#
|
||||
# * `options` {Object}
|
||||
# * `item` Your panel content. It can be DOM element, a jQuery element, or
|
||||
# a model with a view registered via {::addViewProvider}. We recommend the
|
||||
# latter. See {::addViewProvider} for more information.
|
||||
# * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
|
||||
# (default: true)
|
||||
# * `priority` (optional) {Number} Determines stacking order. Lower priority items are
|
||||
# forced closer to the edges of the window. (default: 100)
|
||||
#
|
||||
# Returns a {Panel}
|
||||
addLeftPanel: (options) ->
|
||||
@addPanel('left', options)
|
||||
|
||||
# Essential: Adds a panel item to the right of the editor window.
|
||||
#
|
||||
# * `options` {Object}
|
||||
# * `item` Your panel content. It can be DOM element, a jQuery element, or
|
||||
# a model with a view registered via {::addViewProvider}. We recommend the
|
||||
# latter. See {::addViewProvider} for more information.
|
||||
# * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
|
||||
# (default: true)
|
||||
# * `priority` (optional) {Number} Determines stacking order. Lower priority items are
|
||||
# forced closer to the edges of the window. (default: 100)
|
||||
#
|
||||
# Returns a {Panel}
|
||||
addRightPanel: (options) ->
|
||||
@addPanel('right', options)
|
||||
|
||||
# Essential: Adds a panel item to the top of the editor window above the tabs.
|
||||
#
|
||||
# * `options` {Object}
|
||||
# * `item` Your panel content. It can be DOM element, a jQuery element, or
|
||||
# a model with a view registered via {::addViewProvider}. We recommend the
|
||||
# latter. See {::addViewProvider} for more information.
|
||||
# * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
|
||||
# (default: true)
|
||||
# * `priority` (optional) {Number} Determines stacking order. Lower priority items are
|
||||
# forced closer to the edges of the window. (default: 100)
|
||||
#
|
||||
# Returns a {Panel}
|
||||
addTopPanel: (options) ->
|
||||
@addPanel('top', options)
|
||||
|
||||
addPanel: (location, options) ->
|
||||
options ?= {}
|
||||
options.viewRegistry = @viewRegistry
|
||||
@panelContainers[location].addPanel(new Panel(options))
|
||||
|
||||
###
|
||||
Section: View Management
|
||||
###
|
||||
@@ -628,7 +716,7 @@ class Workspace extends Model
|
||||
# makes [HTML 5 custom elements](http://www.html5rocks.com/en/tutorials/webcomponents/customelements/)
|
||||
# an ideal tool for implementing views in Atom.
|
||||
#
|
||||
# ## Example
|
||||
# ## Examples
|
||||
#
|
||||
# Text editors are divided into a model and a view layer, so when you interact
|
||||
# with methods like `atom.workspace.getActiveTextEditor()` you're only going
|
||||
|
||||
+24
-5
@@ -1,10 +1,19 @@
|
||||
window.onload = function() {
|
||||
var path = require('path');
|
||||
var ipc = require('ipc');
|
||||
try {
|
||||
var startTime = Date.now();
|
||||
|
||||
// Skip "?loadSettings=".
|
||||
var loadSettings = JSON.parse(decodeURIComponent(location.search.substr(14)));
|
||||
|
||||
// Require before the module cache in dev mode
|
||||
if (loadSettings.devMode) {
|
||||
require('coffee-script').register();
|
||||
}
|
||||
|
||||
ModuleCache = require('../src/module-cache');
|
||||
ModuleCache.register(loadSettings);
|
||||
ModuleCache.add(loadSettings.resourcePath);
|
||||
|
||||
// Start the crash reporter before anything else.
|
||||
require('crash-reporter').start({
|
||||
productName: 'Atom',
|
||||
@@ -15,10 +24,20 @@ window.onload = function() {
|
||||
});
|
||||
|
||||
require('vm-compatibility-layer');
|
||||
require('coffee-script').register();
|
||||
require(path.resolve(__dirname, '..', 'src', 'coffee-cache')).register();
|
||||
|
||||
if (!loadSettings.devMode) {
|
||||
require('coffee-script').register();
|
||||
}
|
||||
|
||||
require('../src/coffee-cache').register();
|
||||
|
||||
require(loadSettings.bootstrapScript);
|
||||
ipc.sendChannel('window-command', 'window:loaded')
|
||||
require('ipc').sendChannel('window-command', 'window:loaded');
|
||||
|
||||
if (global.atom) {
|
||||
global.atom.loadTime = Date.now() - startTime;
|
||||
console.log('Window load time: ' + global.atom.getWindowLoadTime() + 'ms');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
var currentWindow = require('remote').getCurrentWindow();
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
@import "ui-variables";
|
||||
|
||||
atom-panel-container[location="left"],
|
||||
atom-panel-container[location="right"] {
|
||||
display: -webkit-flex;
|
||||
-webkit-flex-direction: row;
|
||||
-webkit-align-items: stretch;
|
||||
height: 100%;
|
||||
atom-panel {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// Override bootstrap styles here.
|
||||
.panel {
|
||||
border-radius: 0;
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário