Comparar commits
465 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 0a0e848e7b | |||
| 5348b912cc | |||
| 3fd4e57162 | |||
| 7c356d2592 | |||
| e49414d2ec | |||
| 1361424673 | |||
| e084bebb54 | |||
| df8d014e3c | |||
| d839ea9aa5 | |||
| 6cdb1a188a | |||
| 4218e0a037 | |||
| 36a0da01cc | |||
| ef2bdf6365 | |||
| ef1ec9b693 | |||
| 68d0a99c6e | |||
| 64f3938f5c | |||
| 3faecb5988 | |||
| bf50b4a128 | |||
| 88df4d2f3e | |||
| 4136ff566b | |||
| 2fe523a664 | |||
| 148180adda | |||
| 01a4032895 | |||
| 2b2b65ec82 | |||
| 5ffc063f0c | |||
| dee0771dd7 | |||
| aef6991ca8 | |||
| e8db3e97ce | |||
| b1dd4f2e8e | |||
| 633b08b9de | |||
| 1228435f9b | |||
| 02b5280587 | |||
| 2e47207701 | |||
| 593b5b4e36 | |||
| 1896c94775 | |||
| 4dc20e2027 | |||
| 238e291888 | |||
| 5ccac143aa | |||
| ae97244664 | |||
| 859e3cd038 | |||
| 07577ff944 | |||
| 72b1821828 | |||
| 781a51ac53 | |||
| 9055e650c6 | |||
| 5195a4aaa7 | |||
| 5e4f34f92c | |||
| dc7b549017 | |||
| 29d5f63cd9 | |||
| 33b4ec8e25 | |||
| 85abed2406 | |||
| 520ece4b13 | |||
| 184068dc55 | |||
| f27b897e91 | |||
| 6e201104bc | |||
| 908a2978ae | |||
| 2eb5ef0816 | |||
| 0c5f2cd067 | |||
| d1e60fb2a5 | |||
| bee7be1d1a | |||
| bee4c9df8a | |||
| c5815d2af9 | |||
| 77717d3eff | |||
| d3e0005b33 | |||
| 8295019891 | |||
| 2edcc517b1 | |||
| 9083103bb3 | |||
| bc391094df | |||
| ffba81a962 | |||
| 1e1f4cf173 | |||
| 7f04149f8d | |||
| cd1fb99142 | |||
| 388763e7cd | |||
| f22e4225c3 | |||
| db4b99d27b | |||
| 17f9cc49f2 | |||
| 609855af3c | |||
| 8410d8587b | |||
| 33c9d5ae24 | |||
| 0c48821465 | |||
| 43259f5c51 | |||
| fe2cb046c3 | |||
| de132d79a4 | |||
| 5af181ffb5 | |||
| 355f54ba00 | |||
| b56d4c6181 | |||
| ad9e2ab869 | |||
| a497b0f90f | |||
| e4fd80399d | |||
| c4c5d72bf1 | |||
| 0504244066 | |||
| 8560526158 | |||
| ec8805e99e | |||
| f8ec2e6da4 | |||
| 2a28eafd04 | |||
| a6bf1af2d5 | |||
| 8eae66fc49 | |||
| 02757fc2de | |||
| fc37ac37bd | |||
| 63587abe97 | |||
| d5ea766541 | |||
| bd0643eda4 | |||
| 7a9710b8c3 | |||
| 468c6598db | |||
| ddb3cdc76f | |||
| 1c8e716cfd | |||
| f1f83a7d36 | |||
| 8d87eb2ed6 | |||
| 29d26a4fae | |||
| 5c6f711bf3 | |||
| 53c363b853 | |||
| bacd612c71 | |||
| 2cded15c4c | |||
| 2e73a46cbc | |||
| 64ae7bcdcc | |||
| abca7f778c | |||
| b7197145c8 | |||
| 7f53bb5753 | |||
| 8d38fc77d6 | |||
| 1975882b9e | |||
| 0bd4d31ab0 | |||
| 4192c121e5 | |||
| e89e2141d7 | |||
| 25c5458bd2 | |||
| ff0cddfd1d | |||
| a7ec7497e2 | |||
| 3790cdb262 | |||
| 4308ce7bb0 | |||
| b302fdc553 | |||
| 2fffbba503 | |||
| 9be1427891 | |||
| 4564a39392 | |||
| 068c1e6249 | |||
| 06a55250b9 | |||
| d8240628a7 | |||
| eaa7593b27 | |||
| 72e4be60c0 | |||
| f3a4d32a32 | |||
| edadedce7b | |||
| 89be77b0a9 | |||
| 99ba20ae0d | |||
| 0255e44f00 | |||
| 4832d36ac1 | |||
| 5d15af943e | |||
| 0a671fc386 | |||
| c06f5911c6 | |||
| 2af8404ea9 | |||
| 345d20dc4a | |||
| 34b93554c3 | |||
| 3de926a1af | |||
| bc85bd2e74 | |||
| 3601aac136 | |||
| de8b498402 | |||
| ef09fbbfb2 | |||
| a3784500ec | |||
| 1bd00f1d0a | |||
| ef7f0ed9ed | |||
| 65e80cd65e | |||
| 14bed8a3c8 | |||
| d0513cb95d | |||
| 6a08618821 | |||
| 72c87bfbc9 | |||
| 54c7c1e98d | |||
| 4e73fe3f24 | |||
| 359793c0b0 | |||
| 34ec15862f | |||
| 04bbe393d4 | |||
| 5b84aa7b18 | |||
| 7ba498a170 | |||
| c21f8a5a6c | |||
| 5788e30269 | |||
| 0312609e19 | |||
| 32fba97c3a | |||
| 5259d5b750 | |||
| dff27eba18 | |||
| d2908c75fc | |||
| 2c04bff0fa | |||
| 4f2f158d0d | |||
| 351dc58354 | |||
| 2867dd98e5 | |||
| 32a0804b9a | |||
| 408e62a993 | |||
| d4057d21c7 | |||
| 031ec9798a | |||
| 1ebdd801f5 | |||
| 084632a985 | |||
| 002e14990b | |||
| 3a3fc4b614 | |||
| b028673b5d | |||
| 6394814142 | |||
| e128212410 | |||
| 2d4360dcf0 | |||
| 120e2a3bdb | |||
| 7142022f05 | |||
| 25520a4cad | |||
| 56da6399b8 | |||
| a7379b067a | |||
| de6fb2802b | |||
| 2bfd7d093a | |||
| 416266b815 | |||
| bcc888bfb4 | |||
| e9bcb851c3 | |||
| 8411d41621 | |||
| a66f81d70a | |||
| 72d1eb24a3 | |||
| ba18c65dac | |||
| 0c94298ea4 | |||
| 9bf374d4e5 | |||
| 9344e3fc98 | |||
| ede6a081a4 | |||
| d83547b9f4 | |||
| ef2337359b | |||
| bc8a5f4b23 | |||
| 261421b609 | |||
| 07eb2585d6 | |||
| bec3d4effc | |||
| 422c4d41d1 | |||
| 7aa233d9d6 | |||
| 6c9df6d91a | |||
| 520e478447 | |||
| fc462fcd21 | |||
| 69b34b7377 | |||
| 3180bd2e3c | |||
| 286c7e8f18 | |||
| 1c6e2f06ed | |||
| 527a18fedc | |||
| 46e1a9254d | |||
| ae46f1a3e1 | |||
| 573c7f9621 | |||
| dbed45c68e | |||
| 9e6756ed6d | |||
| 95818ab21f | |||
| 9cf50960a2 | |||
| f555af0b88 | |||
| 853ee3eec5 | |||
| bb9e052c90 | |||
| 4bade1c976 | |||
| 058d55c829 | |||
| dc323348b7 | |||
| 858e7b62a3 | |||
| 28015339d9 | |||
| ca86e0258f | |||
| 87fcc39045 | |||
| b78ff8c3a9 | |||
| 01fdf016a9 | |||
| 655cce9fd4 | |||
| f3ea2cd9e0 | |||
| ebcae85f1a | |||
| 0b79c31d45 | |||
| d5ec8551e1 | |||
| cd376c2d1d | |||
| 8d84a97b2b | |||
| 0104d7e264 | |||
| 019cccc978 | |||
| 7f1d88a05d | |||
| ec754e5cd9 | |||
| e046bb52d7 | |||
| 34be92b5b0 | |||
| fb0a15b1b3 | |||
| e96d2dbd17 | |||
| 025370b9f8 | |||
| e26ab5513f | |||
| f623a4f2b5 | |||
| 4186a4943b | |||
| e179860519 | |||
| 43f8596fb1 | |||
| 2c7d01e398 | |||
| 1bdf45f7d6 | |||
| 7e6e61905b | |||
| 01b4ff24b9 | |||
| 47e2cb4645 | |||
| 236f4c5342 | |||
| 911390aba8 | |||
| 4569b76dd5 | |||
| 1c410cbf5a | |||
| 5f602ad86e | |||
| 9da9c77e61 | |||
| 8ed05a4201 | |||
| a8533c1010 | |||
| 87915f70e2 | |||
| ec07cb9524 | |||
| d4ae3ac548 | |||
| 1b4426e3ba | |||
| 32245b1d92 | |||
| 030bcd6d4f | |||
| f1628fb1e0 | |||
| 7c1a4a9e08 | |||
| 3b1c70911f | |||
| 70fc084864 | |||
| d9152e856b | |||
| 10dba4b9a0 | |||
| 5e51445118 | |||
| f4b7f86eb0 | |||
| 07308a4e04 | |||
| 8cca198af1 | |||
| efea4c6d05 | |||
| 588d9b97eb | |||
| 4f77a371f4 | |||
| 94dd1eed54 | |||
| 09b91974a7 | |||
| 99611fad34 | |||
| 1b026cc805 | |||
| 4c0d87080a | |||
| a09eb96982 | |||
| 5531d54554 | |||
| b73d267a5f | |||
| 5add0e4f70 | |||
| 7040906473 | |||
| 131aad24c0 | |||
| 64870c733b | |||
| 68ca6a0a0d | |||
| 376010722e | |||
| 31cf907b48 | |||
| 28af52dfab | |||
| 9e7e77bdf8 | |||
| d0385a90ea | |||
| f9c4af301d | |||
| 3cf4b7b3e4 | |||
| 8523df58b0 | |||
| bd2c2bfe46 | |||
| f5a70da6f0 | |||
| a8186b15cf | |||
| fe088ba16a | |||
| 562a047b9e | |||
| 634c995a71 | |||
| 153faefaf9 | |||
| 636a8a8286 | |||
| faada2de30 | |||
| e762efa97e | |||
| a2c9c21cfb | |||
| ea4f99e5ac | |||
| b46ac9080c | |||
| 24a463b18d | |||
| 3d28a04ffa | |||
| 326542644d | |||
| 45f70bb783 | |||
| a227ed7586 | |||
| 5288666bfc | |||
| 0396aac11f | |||
| 5db163a328 | |||
| 4d12e025e4 | |||
| be20148518 | |||
| 756347a716 | |||
| 7379627fcb | |||
| 4b02d3ff61 | |||
| dc6836dc2d | |||
| a2c4caf7ad | |||
| e8b6fb919e | |||
| 2087426afc | |||
| b703ca0ebf | |||
| 601c877136 | |||
| 6c609cb7d2 | |||
| 31d7110bba | |||
| ad522e6ab1 | |||
| e59f242f19 | |||
| bae625a894 | |||
| b5532ee4a3 | |||
| 31b4b7a372 | |||
| 1a1ed56419 | |||
| 13be8d5139 | |||
| 312901ff68 | |||
| 346b6007ca | |||
| a8df77243c | |||
| f30641da44 | |||
| 049531e495 | |||
| e8594ccec4 | |||
| 65ab436da2 | |||
| e7bd8026d2 | |||
| d9e731c84a | |||
| 8e1e5a3760 | |||
| 02594e3f7a | |||
| da5bf6c74c | |||
| 5cd8f5952f | |||
| 1b8be75a76 | |||
| 235180cf03 | |||
| a13990155f | |||
| a72f11594d | |||
| 86d7a45a78 | |||
| a229d696d5 | |||
| ef6ca3853d | |||
| 9e86d5f5f1 | |||
| 3ef91c61d9 | |||
| 5bae58eeb1 | |||
| 6ce859774a | |||
| fa4a6e2d71 | |||
| 9ee54801a2 | |||
| 79578e08ab | |||
| 732e23b8ea | |||
| d51894103d | |||
| eb59196c02 | |||
| 142eedd705 | |||
| 77d269c6d9 | |||
| b23009a8f3 | |||
| 84de8c1bfd | |||
| 6a9faee109 | |||
| 4d6737230a | |||
| 362f696e3e | |||
| 677d3abe0e | |||
| c78f9137cc | |||
| d8d378e083 | |||
| 8cf9e1990b | |||
| e9f116a14e | |||
| b2253ab7ce | |||
| fc5dc46e73 | |||
| cb9d488a1e | |||
| 389b5c7891 | |||
| 6bf97f7a1a | |||
| d0639393ca | |||
| 7ae6cba337 | |||
| a3e85d6758 | |||
| decce0e3a1 | |||
| aa2868efbf | |||
| 8521114c08 | |||
| 43b5a9cfd0 | |||
| 4387e16496 | |||
| 6f3ebe8d0c | |||
| c972d9b022 | |||
| b1563fdfc0 | |||
| a2c41a2ae3 | |||
| 21623b9c37 | |||
| 7dcafb44f1 | |||
| f9c975bba7 | |||
| 1c6f7b00eb | |||
| fc1709f113 | |||
| 35c2ef09a0 | |||
| dbbf310e36 | |||
| ee1c17d787 | |||
| fe3ea229a2 | |||
| 381ebe1a38 | |||
| 965a65d6f8 | |||
| 25b641125d | |||
| 53aff00218 | |||
| 9f8e1fbb80 | |||
| 5e7a26dc9d | |||
| 2f3d74c047 | |||
| 2f45685a06 | |||
| 8577bf9a4c | |||
| 15bb3ee31d | |||
| 2234ff78c7 | |||
| fca6a7758b | |||
| 7dae04b8d4 | |||
| d7ee81418d | |||
| 04e4b917b0 | |||
| 2ee2efeb84 | |||
| 367e429162 | |||
| 8417dfd10e | |||
| 15d1c5f992 | |||
| e0b1a0cfa7 | |||
| cfd9baa0d6 | |||
| 1d80c35708 | |||
| ad88ace448 | |||
| a6d870bf4c | |||
| fa66689c07 | |||
| 905b28975e | |||
| 7e5f667b31 | |||
| f103e72c01 | |||
| 6cba6af743 | |||
| eeb44407e3 | |||
| 68df603512 | |||
| 087b52c9b4 | |||
| 18f7297968 | |||
| 3cd34e0b4d | |||
| 25e3e46166 | |||
| 2c84d69ff2 | |||
| 3fb28803b5 | |||
| 1fe6c498ac |
@@ -6,3 +6,9 @@ spec/fixtures/**/*.less text eol=lf
|
||||
spec/fixtures/**/*.css text eol=lf
|
||||
spec/fixtures/**/*.txt text eol=lf
|
||||
spec/fixtures/dir/**/* text eol=lf
|
||||
|
||||
# Git 1.7 does not support **/* patterns
|
||||
spec/fixtures/css.css text eol=lf
|
||||
spec/fixtures/sample.js text eol=lf
|
||||
spec/fixtures/sample.less text eol=lf
|
||||
spec/fixtures/sample.txt text eol=lf
|
||||
|
||||
@@ -11,6 +11,9 @@ propose changes to this document in a pull request.
|
||||
|
||||
## Submitting Issues
|
||||
|
||||
* Check the [debugging guide](https://atom.io/docs/latest/debugging) for tips
|
||||
on debugging. You might be able to find the cause of the problem and fix
|
||||
things yourself.
|
||||
* Include the version of Atom you are using and the OS.
|
||||
* Include screenshots and animated GIFs whenever possible; they are immensely
|
||||
helpful.
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.68.0"
|
||||
"atom-package-manager": "0.69.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"grunt-contrib-coffee": "~0.9.0",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"grunt-cson": "0.8.0",
|
||||
"grunt-download-atom-shell": "~0.7.2",
|
||||
"grunt-download-atom-shell": "~0.8.0",
|
||||
"grunt-lesslint": "0.13.0",
|
||||
"grunt-markdown": "~0.4.0",
|
||||
"grunt-peg": "~1.1.0",
|
||||
@@ -26,7 +26,7 @@
|
||||
"harmony-collections": "~0.3.8",
|
||||
"json-front-matter": "~0.1.3",
|
||||
"legal-eagle": "~0.4.0",
|
||||
"minidump": "~0.6",
|
||||
"minidump": "~0.7",
|
||||
"read-package-json": "1.1.8",
|
||||
"normalize-package-data": "0.2.12",
|
||||
"rcedit": "~0.1.2",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{cp, isAtomPackage, mkdir, rm} = require('./task-helpers')(grunt)
|
||||
@@ -48,16 +49,22 @@ module.exports = (grunt) ->
|
||||
path.join('bootstrap', 'docs')
|
||||
path.join('bootstrap', 'examples')
|
||||
path.join('pegjs', 'examples')
|
||||
# Add .* to avoid matching hunspell_dictionaries.
|
||||
path.join('spellchecker', 'vendor', 'hunspell', '.*')
|
||||
path.join('plist', 'tests')
|
||||
path.join('xmldom', 'test')
|
||||
path.join('jasmine-reporters', 'ext')
|
||||
path.join('build', 'Release', 'obj.target')
|
||||
path.join('build', 'Release', 'obj')
|
||||
path.join('build', 'Release', '.deps')
|
||||
path.join('vendor', 'apm')
|
||||
path.join('resources', 'mac')
|
||||
path.join('resources', 'win')
|
||||
]
|
||||
ignoredPaths = ignoredPaths.map (ignoredPath) -> _.escapeRegExp(ignoredPath)
|
||||
|
||||
# Add .* to avoid matching hunspell_dictionaries.
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('spellchecker', 'vendor', 'hunspell') + path.sep)}.*"
|
||||
ignoredPaths.push "#{_.escapeRegExp(path.join('build', 'Release') + path.sep)}.*\\.pdb"
|
||||
|
||||
# Hunspell dictionaries are only not needed on OS X.
|
||||
if process.platform is 'darwin'
|
||||
ignoredPaths.push path.join('spellchecker', 'vendor', 'hunspell_dictionaries')
|
||||
@@ -79,6 +86,10 @@ module.exports = (grunt) ->
|
||||
unless /.+\.plist/.test(sourcePath)
|
||||
grunt.file.copy(sourcePath, path.resolve(appDir, '..', subDirectory, filename))
|
||||
|
||||
if process.platform is 'win32'
|
||||
cp path.join('resources', 'win', 'msvcp100.dll'), path.join(shellAppDir, 'msvcp100.dll')
|
||||
cp path.join('resources', 'win', 'msvcr100.dll'), path.join(shellAppDir, 'msvcr100.dll')
|
||||
|
||||
dependencies = ['compile', "generate-license:save"]
|
||||
dependencies.push('copy-info-plist') if process.platform is 'darwin'
|
||||
dependencies.push('set-exe-icon') if process.platform is 'win32'
|
||||
|
||||
@@ -2,6 +2,8 @@ module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'codesign', 'Codesign the app', ->
|
||||
return unless process.platform is 'darwin'
|
||||
|
||||
done = @async()
|
||||
|
||||
if process.env.XCODE_KEYCHAIN
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
runas = null
|
||||
|
||||
fillTemplate = (filePath, data) ->
|
||||
template = _.template(String(fs.readFileSync(filePath + '.in')))
|
||||
filled = template(data)
|
||||
fs.writeFileSync(filePath, filled)
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{cp, mkdir, rm} = require('./task-helpers')(grunt)
|
||||
|
||||
@@ -25,12 +31,23 @@ module.exports = (grunt) ->
|
||||
binDir = path.join(installDir, 'bin')
|
||||
shareDir = path.join(installDir, 'share', 'atom')
|
||||
|
||||
iconName = path.join(shareDir,'resources','app','resources','atom.png')
|
||||
desktopFile = path.join('resources', 'linux', 'Atom.desktop')
|
||||
|
||||
mkdir binDir
|
||||
cp 'atom.sh', path.join(binDir, 'atom')
|
||||
rm shareDir
|
||||
mkdir path.dirname(shareDir)
|
||||
cp shellAppDir, shareDir
|
||||
|
||||
# Create Atom.desktop if installation in '/usr/local'
|
||||
applicationsDir = path.join('/usr','share','applications')
|
||||
tmpDir = if process.env.TMPDIR? then process.env.TMPDIR else '/tmp'
|
||||
if installDir.indexOf(tmpDir) isnt 0 and fs.isDirectorySync(applicationsDir)
|
||||
{description} = grunt.file.readJSON('package.json')
|
||||
fillTemplate(desktopFile, {description, installDir, iconName})
|
||||
cp desktopFile, path.join(applicationsDir,'Atom.desktop')
|
||||
|
||||
# Create relative symbol link for apm.
|
||||
process.chdir(binDir)
|
||||
rm('apm')
|
||||
|
||||
@@ -13,11 +13,19 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask 'mkdeb', 'Create debian 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')
|
||||
section = 'devel'
|
||||
arch = 'amd64'
|
||||
maintainer = 'GitHub <atom@github.com>'
|
||||
data = {name, version, description, section, arch, maintainer}
|
||||
installDir = '/usr'
|
||||
iconName = 'atom'
|
||||
data = {name, version, description, section, arch, maintainer, installDir, iconName}
|
||||
|
||||
control = path.join('resources', 'linux', 'debian', 'control')
|
||||
fillTemplate(control, data)
|
||||
@@ -27,5 +35,5 @@ module.exports = (grunt) ->
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
|
||||
cmd = path.join('script', 'mkdeb')
|
||||
args = [version, control, desktop, icon, buildDir]
|
||||
args = [version, arch, control, desktop, icon, buildDir]
|
||||
spawn({cmd, args}, done)
|
||||
|
||||
@@ -8,11 +8,17 @@ GitHub = require 'github-releases'
|
||||
request = require 'request'
|
||||
|
||||
grunt = null
|
||||
maxReleases = 10
|
||||
assets = [
|
||||
{assetName: 'atom-mac.zip', sourceName: 'Atom.app'}
|
||||
{assetName: 'atom-mac-symbols.zip', sourceName: 'Atom.breakpad.syms'}
|
||||
]
|
||||
|
||||
if process.platform is 'darwin'
|
||||
assets = [
|
||||
{assetName: 'atom-mac.zip', sourceName: 'Atom.app'}
|
||||
{assetName: 'atom-mac-symbols.zip', sourceName: 'Atom.breakpad.syms'}
|
||||
]
|
||||
else
|
||||
assets = [
|
||||
{assetName: 'atom-windows.zip', sourceName: 'Atom'}
|
||||
]
|
||||
|
||||
commitSha = process.env.JANKY_SHA1
|
||||
token = process.env.ATOM_ACCESS_TOKEN
|
||||
defaultHeaders =
|
||||
@@ -23,7 +29,6 @@ module.exports = (gruntObject) ->
|
||||
grunt = gruntObject
|
||||
|
||||
grunt.registerTask 'publish-build', 'Publish the built app', ->
|
||||
return unless process.platform is 'darwin'
|
||||
return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
|
||||
|
||||
done = @async()
|
||||
@@ -45,10 +50,13 @@ logError = (message, error, details) ->
|
||||
|
||||
zipApps = (buildDir, assets, callback) ->
|
||||
zip = (directory, sourceName, assetName, callback) ->
|
||||
if process.platform is 'win32'
|
||||
zipCommand = "C:/psmodules/7z.exe a -r #{assetName} #{sourceName}"
|
||||
else
|
||||
zipCommand = "zip -r --symlinks #{assetName} #{sourceName}"
|
||||
options = {cwd: directory, maxBuffer: Infinity}
|
||||
child_process.exec "zip -r --symlinks #{assetName} #{sourceName}", options, (error, stdout, stderr) ->
|
||||
if error?
|
||||
logError("Zipping #{sourceName} failed", error, stderr)
|
||||
child_process.exec zipCommand, options, (error, stdout, stderr) ->
|
||||
logError("Zipping #{sourceName} failed", error, stderr) if error?
|
||||
callback(error)
|
||||
|
||||
tasks = []
|
||||
|
||||
@@ -39,7 +39,8 @@ module.exports = (grunt) ->
|
||||
grunt.verbose.writeln "Launching #{path.basename(packagePath)} specs."
|
||||
spawn options, (error, results, code) ->
|
||||
if process.platform is 'win32'
|
||||
process.stderr.write(fs.readFileSync(path.join(packagePath, 'ci.log')))
|
||||
if error
|
||||
process.stderr.write(fs.readFileSync(path.join(packagePath, 'ci.log')))
|
||||
fs.unlinkSync(path.join(packagePath, 'ci.log'))
|
||||
|
||||
failedPackages.push path.basename(packagePath) if error
|
||||
@@ -77,7 +78,7 @@ module.exports = (grunt) ->
|
||||
|
||||
spawn options, (error, results, code) ->
|
||||
if process.platform is 'win32'
|
||||
process.stderr.write(fs.readFileSync('ci.log'))
|
||||
process.stderr.write(fs.readFileSync('ci.log')) if error
|
||||
fs.unlinkSync('ci.log')
|
||||
else
|
||||
# TODO: Restore concurrency on Windows
|
||||
@@ -104,9 +105,4 @@ module.exports = (grunt) ->
|
||||
failures.push "atom core" if coreSpecFailed
|
||||
|
||||
grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0
|
||||
|
||||
# TODO: Mark the build as green on Windows until specs pass.
|
||||
if process.platform is 'darwin'
|
||||
done(!coreSpecFailed and failedPackages.length == 0)
|
||||
else if process.platform is 'win32'
|
||||
done(true)
|
||||
done(!coreSpecFailed and failedPackages.length == 0)
|
||||
|
||||
@@ -4,9 +4,16 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
|
||||
## Requirements
|
||||
|
||||
* OS with 64-bit architecture
|
||||
* OS with 64-bit or 32-bit architecture
|
||||
* C++ toolchain
|
||||
* on Ubuntu/Debian: `sudo apt-get install build-essential`
|
||||
* on Fedora: `sudo yum --assumeyes install make gcc gcc-c++ glibc-devel`
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* [npm](http://www.npmjs.org/) v1.4.x
|
||||
* [Ubuntu/Debian/Mint instructions](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os)
|
||||
* [Fedora instructions](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#fedora)
|
||||
* [npm](http://www.npmjs.org/) v1.4.x
|
||||
* `npm` comes with node.js so no explicit installation is needed here.
|
||||
* You can check `npm` 1.4 or above is installed by running `npm -v`.
|
||||
* libgnome-keyring-dev
|
||||
* on Ubuntu/Debian: `sudo apt-get install libgnome-keyring-dev`
|
||||
* on Fedora: `sudo yum --assumeyes install libgnome-keyring-devel`
|
||||
@@ -14,7 +21,9 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
* `npm config set python /usr/bin/python2 -g` to ensure that gyp uses Python 2
|
||||
* This command may require `sudo` depending on how you have
|
||||
[configured npm](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os).
|
||||
|
||||
* Git
|
||||
* on Ubuntu/Debian: `sudo apt-get install git`
|
||||
* on Fedora: `sudo yum install git-core`
|
||||
|
||||
## Instructions
|
||||
|
||||
@@ -53,4 +62,5 @@ and restart Atom. If Atom now works fine, you can make this setting permanent:
|
||||
See also https://github.com/atom/atom/issues/2082.
|
||||
|
||||
### Linux build error reports in atom/atom
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Alinux&type=Issues) to get a list of reports about build errors on Linux.
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Alinux&type=Issues)
|
||||
to get a list of reports about build errors on Linux.
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
* For 64-bit builds of node and native modules you **must** have the
|
||||
[Windows 7 64-bit SDK](http://www.microsoft.com/en-us/download/details.aspx?id=8279).
|
||||
You may also need the [compiler update for the Windows SDK 7.1](http://www.microsoft.com/en-us/download/details.aspx?id=4422)
|
||||
|
||||
* [Python](http://www.python.org/download/) v2.7.x
|
||||
* [Python](http://www.python.org/download/) v2.7.
|
||||
* The python.exe must be available at `%SystemDrive%\Python27\python.exe`.
|
||||
If it is installed elsewhere, you can create a symbolic link to the
|
||||
directory containing the python.exe using:
|
||||
`mklink /d %SystemDrive%\Python27 D:\elsewhere\Python27`
|
||||
* [GitHub for Windows](http://windows.github.com/)
|
||||
|
||||
### On Windows 8
|
||||
@@ -45,5 +48,19 @@ fix this, you probably need to fiddle with your system PATH.
|
||||
* If you just installed node you need to restart your computer before node is
|
||||
available on your Path.
|
||||
|
||||
|
||||
* `script/build` outputs only the Node and Python versions before returning
|
||||
|
||||
* Try moving the repository to `C:\atom`. Most likely, the path is too long.
|
||||
See [issue #2200](https://github.com/atom/atom/issues/2200).
|
||||
|
||||
* `error MSB4025: The project file could not be loaded. Invalid character in the given encoding.`
|
||||
|
||||
* These can occur because your home directory (`%USERPROFILE%`) has non-ASCII
|
||||
characters in it. This is a bug in [gyp](https://code.google.com/p/gyp/)
|
||||
which is used to build native node modules and there is no known workaround.
|
||||
* https://github.com/TooTallNate/node-gyp/issues/297
|
||||
* https://code.google.com/p/gyp/issues/detail?id=393
|
||||
|
||||
### Windows build error reports in atom/atom
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Awindows&type=Issues) to get a list of reports about build errors on Windows.
|
||||
|
||||
+8
-1
@@ -1,6 +1,13 @@
|
||||
# Debugging
|
||||
|
||||
Atom provides several tools to help you understand unexpected behavior and debug problems. This guide describes some of those tools and a few approaches to help you debug and provide more helpful information when [submitting issues].
|
||||
Atom provides several tools to help you understand unexpected behavior and debug problems. This guide describes some of those tools and a few approaches to help you debug and provide more helpful information when [submitting issues]:
|
||||
|
||||
* [Update to the latest version](#update-to-the-latest-version)
|
||||
* [Check Atom and package settings](#check-atom-and-package-settings)
|
||||
* [Check the keybindings](#check-the-keybindings)
|
||||
* [Check if the problem shows up in safe mode](#check-if-the-problem-shows-up-in-safe-mode)
|
||||
* [Check your config files](#check-your-config-files)
|
||||
* [Check for errors in the developer tools](#check-for-errors-in-the-developer-tools)
|
||||
|
||||
## Update to the latest version
|
||||
|
||||
|
||||
@@ -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)
|
||||
* [Debugging](debugging.md)
|
||||
|
||||
### Advanced Topics
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'shift-pageup': 'core:select-page-up'
|
||||
'shift-pagedown': 'core:select-page-down'
|
||||
'delete': 'core:delete'
|
||||
'shift-delete': 'core:delete'
|
||||
'pageup': 'core:page-up'
|
||||
|
||||
@@ -29,10 +29,14 @@
|
||||
'ctrl-x': 'core:cut'
|
||||
'ctrl-c': 'core:copy'
|
||||
'ctrl-v': 'core:paste'
|
||||
'ctrl-insert': 'core:copy'
|
||||
'shift-insert': 'core:paste'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'shift-pageup': 'core:select-page-up'
|
||||
'shift-pagedown': 'core:select-page-down'
|
||||
'delete': 'core:delete'
|
||||
'shift-delete': 'core:delete'
|
||||
'pageup': 'core:page-up'
|
||||
|
||||
@@ -31,10 +31,14 @@
|
||||
'ctrl-x': 'core:cut'
|
||||
'ctrl-c': 'core:copy'
|
||||
'ctrl-v': 'core:paste'
|
||||
'ctrl-insert': 'core:copy'
|
||||
'shift-insert': 'core:paste'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'shift-pageup': 'core:select-page-up'
|
||||
'shift-pagedown': 'core:select-page-down'
|
||||
'delete': 'core:delete'
|
||||
'shift-delete': 'core:delete'
|
||||
'pageup': 'core:page-up'
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
submenu: [
|
||||
{ label: 'Add Selection Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection Below', command: 'editor:add-selection-below' }
|
||||
{ label: 'Single Selection', command: 'editor:consolidate-selections'}
|
||||
{ label: 'Split into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to Top', command: 'core:select-to-top' }
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
|
||||
{ label: 'S&plit into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ label: 'Single Selection', command: 'editor:consolidate-selections'}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to &Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
|
||||
{ label: 'S&plit into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ label: 'Single Selection', command: 'editor:consolidate-selections'}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to &Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
|
||||
|
||||
+43
-43
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.101.0",
|
||||
"version": "0.107.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -17,22 +17,22 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.12.7",
|
||||
"atomShellVersion": "0.13.2",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^0.26.0",
|
||||
"atom-keymap": "^0.27.0",
|
||||
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"clear-cut": "0.4.0",
|
||||
"coffee-script": "1.7.0",
|
||||
"coffeestack": "0.7.0",
|
||||
"delegato": "^1",
|
||||
"emissary": "^1.2.1",
|
||||
"first-mate": "^1.6.1",
|
||||
"first-mate": "^1.7",
|
||||
"fs-plus": "^2.2.3",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "^1.1",
|
||||
"git-utils": "^1.3",
|
||||
"grim": "0.10.0",
|
||||
"grim": "0.11.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-tagged": "^1.1.2",
|
||||
"less-cache": "0.12.0",
|
||||
@@ -48,75 +48,75 @@
|
||||
"react-atom-fork": "^0.10.0",
|
||||
"reactionary-atom-fork": "^0.9.0",
|
||||
"runas": "^0.5",
|
||||
"scandal": "0.15.2",
|
||||
"scandal": "0.15.3",
|
||||
"scoped-property-store": "^0.9.0",
|
||||
"scrollbar-style": "^0.4.0",
|
||||
"season": "^1.0.2",
|
||||
"semver": "1.1.4",
|
||||
"serializable": "^1",
|
||||
"space-pen": "3.2.0",
|
||||
"temp": "0.5.0",
|
||||
"text-buffer": "^2.2.2",
|
||||
"temp": "0.7.0",
|
||||
"text-buffer": "^2.4.2",
|
||||
"theorist": "^1",
|
||||
"underscore-plus": "^1.4.1",
|
||||
"underscore-plus": "^1.5.0",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.16.0",
|
||||
"atom-dark-syntax": "0.17.0",
|
||||
"atom-dark-ui": "0.29.0",
|
||||
"atom-light-syntax": "0.17.0",
|
||||
"atom-light-syntax": "0.18.0",
|
||||
"atom-light-ui": "0.25.0",
|
||||
"base16-tomorrow-dark-theme": "0.16.0",
|
||||
"solarized-dark-syntax": "0.16.0",
|
||||
"solarized-light-syntax": "0.8.0",
|
||||
"archive-view": "0.31.0",
|
||||
"base16-tomorrow-dark-theme": "0.17.0",
|
||||
"solarized-dark-syntax": "0.18.0",
|
||||
"solarized-light-syntax": "0.9.0",
|
||||
"archive-view": "0.33.0",
|
||||
"autocomplete": "0.28.0",
|
||||
"autoflow": "0.17.0",
|
||||
"autosave": "0.13.0",
|
||||
"autosave": "0.14.0",
|
||||
"background-tips": "0.14.0",
|
||||
"bookmarks": "0.22.0",
|
||||
"bracket-matcher": "0.43.0",
|
||||
"command-palette": "0.21.0",
|
||||
"deprecation-cop": "0.6.0",
|
||||
"bookmarks": "0.25.0",
|
||||
"bracket-matcher": "0.47.0",
|
||||
"command-palette": "0.23.0",
|
||||
"deprecation-cop": "0.7.0",
|
||||
"dev-live-reload": "0.31.0",
|
||||
"exception-reporting": "0.18.0",
|
||||
"feedback": "0.33.0",
|
||||
"find-and-replace": "0.113.0",
|
||||
"fuzzy-finder": "0.54.0",
|
||||
"git-diff": "0.28.0",
|
||||
"go-to-line": "0.22.0",
|
||||
"find-and-replace": "0.120.0",
|
||||
"fuzzy-finder": "0.55.0",
|
||||
"git-diff": "0.34.0",
|
||||
"go-to-line": "0.23.0",
|
||||
"grammar-selector": "0.27.0",
|
||||
"image-view": "0.33.0",
|
||||
"image-view": "0.35.0",
|
||||
"keybinding-resolver": "0.18.0",
|
||||
"link": "0.22.0",
|
||||
"markdown-preview": "0.74.0",
|
||||
"link": "0.24.0",
|
||||
"markdown-preview": "0.83.0",
|
||||
"metrics": "0.32.0",
|
||||
"open-on-github": "0.28.0",
|
||||
"package-generator": "0.30.0",
|
||||
"package-generator": "0.31.0",
|
||||
"release-notes": "0.32.0",
|
||||
"settings-view": "0.119.0",
|
||||
"snippets": "0.45.0",
|
||||
"spell-check": "0.36.0",
|
||||
"status-bar": "0.40.0",
|
||||
"settings-view": "0.128.0",
|
||||
"snippets": "0.46.0",
|
||||
"spell-check": "0.38.0",
|
||||
"status-bar": "0.41.0",
|
||||
"styleguide": "0.29.0",
|
||||
"symbols-view": "0.55.0",
|
||||
"tabs": "0.41.0",
|
||||
"symbols-view": "0.56.0",
|
||||
"tabs": "0.42.0",
|
||||
"timecop": "0.19.0",
|
||||
"tree-view": "0.97.0",
|
||||
"tree-view": "0.104.0",
|
||||
"update-package-dependencies": "0.6.0",
|
||||
"welcome": "0.16.0",
|
||||
"whitespace": "0.22.0",
|
||||
"wrap-guide": "0.18.0",
|
||||
"language-c": "0.16.0",
|
||||
"wrap-guide": "0.19.0",
|
||||
"language-c": "0.21.0",
|
||||
"language-coffee-script": "0.22.0",
|
||||
"language-css": "0.17.0",
|
||||
"language-gfm": "0.39.0",
|
||||
"language-gfm": "0.40.0",
|
||||
"language-git": "0.9.0",
|
||||
"language-go": "0.12.0",
|
||||
"language-html": "0.22.0",
|
||||
"language-hyperlink": "0.10.0",
|
||||
"language-java": "0.10.0",
|
||||
"language-javascript": "0.26.0",
|
||||
"language-javascript": "0.28.0",
|
||||
"language-json": "0.8.0",
|
||||
"language-less": "0.9.0",
|
||||
"language-make": "0.10.0",
|
||||
@@ -125,17 +125,17 @@
|
||||
"language-php": "0.15.0",
|
||||
"language-property-list": "0.7.0",
|
||||
"language-python": "0.18.0",
|
||||
"language-ruby": "0.27.0",
|
||||
"language-ruby": "0.30.0",
|
||||
"language-ruby-on-rails": "0.14.0",
|
||||
"language-sass": "0.12.0",
|
||||
"language-sass": "0.13.0",
|
||||
"language-shellscript": "0.8.0",
|
||||
"language-source": "0.7.0",
|
||||
"language-sql": "0.8.0",
|
||||
"language-text": "0.6.0",
|
||||
"language-todo": "0.10.0",
|
||||
"language-toml": "0.12.0",
|
||||
"language-xml": "0.14.0",
|
||||
"language-yaml": "0.6.0"
|
||||
"language-xml": "0.15.0",
|
||||
"language-yaml": "0.7.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Name=Atom
|
||||
Comment=<%= description %>
|
||||
Exec=/usr/share/atom/atom %U
|
||||
Icon=atom
|
||||
Exec=<%= installDir %>/share/atom/atom %U
|
||||
Icon=<%= iconName %>
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=GNOME;GTK;Utility;TextEditor;
|
||||
|
||||
Arquivo binário não exibido.
Arquivo binário não exibido.
+10
-10
@@ -10,6 +10,8 @@ function executeCommands(commands, done, index) {
|
||||
index = (index == undefined ? 0 : index);
|
||||
if (index < commands.length) {
|
||||
var command = commands[index];
|
||||
if (command.message)
|
||||
console.log(command.message);
|
||||
var options = null;
|
||||
if (typeof command !== 'string') {
|
||||
options = command.options;
|
||||
@@ -24,9 +26,9 @@ function executeCommands(commands, done, index) {
|
||||
function bootstrap() {
|
||||
var apmInstallPath = path.resolve(__dirname, '..', 'apm');
|
||||
if (!fs.existsSync(apmInstallPath))
|
||||
fs.mkdirSync(apmInstallPath);
|
||||
fs.mkdirSync(apmInstallPath);
|
||||
if (!fs.existsSync(path.join(apmInstallPath, 'node_modules')))
|
||||
fs.mkdirSync(path.join(apmInstallPath, 'node_modules'));
|
||||
fs.mkdirSync(path.join(apmInstallPath, 'node_modules'));
|
||||
|
||||
var apmPath = path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', 'apm')
|
||||
var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? '--no-color' : '';
|
||||
@@ -35,16 +37,14 @@ function bootstrap() {
|
||||
var initialNpmCommand = fs.existsSync(npmPath) ? npmPath : 'npm';
|
||||
var npmFlags = ' --userconfig=' + path.resolve('.npmrc') + ' ';
|
||||
|
||||
var packagesToDedupe = ['fs-plus', 'humanize-plus', 'oniguruma', 'roaster', 'season'];
|
||||
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
var packagesToDedupe = ['fs-plus', 'humanize-plus', 'oniguruma', 'roaster', 'season', 'grim'];
|
||||
|
||||
var commands = [
|
||||
{command: initialNpmCommand + npmFlags + 'install --quiet', options: {cwd: path.resolve(__dirname, '..', 'build'), ignoreStdout: true}},
|
||||
{command: npmPath + npmFlags + 'install --quiet', options: {cwd: apmInstallPath, ignoreStdout: true}},
|
||||
echoNewLine,
|
||||
apmPath + ' clean ' + apmFlags,
|
||||
apmPath + ' install --quiet ' + apmFlags,
|
||||
apmPath + ' dedupe --quiet ' + apmFlags + ' ' + packagesToDedupe.join(' '),
|
||||
{command: initialNpmCommand + npmFlags + 'install --quiet', message: 'Installing build modules...', options: {cwd: path.resolve(__dirname, '..', 'build'), ignoreStdout: true}},
|
||||
{command: npmPath + npmFlags + 'install --quiet', message: 'Installing apm...', options: {cwd: apmInstallPath, ignoreStdout: true}},
|
||||
apmPath + ' clean ' + apmFlags,
|
||||
apmPath + ' install --quiet ' + apmFlags,
|
||||
apmPath + ' dedupe --quiet ' + apmFlags + ' ' + packagesToDedupe.join(' '),
|
||||
];
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
+9
-3
@@ -19,12 +19,18 @@ function loadEnvironmentVariables(filePath) {
|
||||
var value = parts[1].trim().substr(1, parts[1].length - 2);
|
||||
process.env[key] = value;
|
||||
}
|
||||
} catch(error) { }
|
||||
} catch(error) {
|
||||
console.error("Failed to load environment variables: " + filePath, error.code);
|
||||
}
|
||||
}
|
||||
|
||||
function readEnvironmentVariables() {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials')
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain')
|
||||
if (process.platform === 'win32')
|
||||
loadEnvironmentVariables(path.resolve('/jenkins/config/atomcredentials'));
|
||||
else {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials');
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain');
|
||||
}
|
||||
}
|
||||
|
||||
readEnvironmentVariables();
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
path = require 'path'
|
||||
CommandInstaller = require '../src/command-installer'
|
||||
|
||||
callback = (error) ->
|
||||
console.warn error.message if error?
|
||||
|
||||
CommandInstaller.installAtomCommand(path.resolve(__dirname, '..'), callback)
|
||||
CommandInstaller.installApmCommand(path.resolve(__dirname, '..'), callback)
|
||||
+7
-6
@@ -8,14 +8,15 @@ ROOT=`readlink -f $(dirname $SCRIPT)/..`
|
||||
cd $ROOT
|
||||
|
||||
VERSION="$1"
|
||||
CONTROL_FILE="$2"
|
||||
DESKTOP_FILE="$3"
|
||||
ICON_FILE="$4"
|
||||
DEB_PATH="$5"
|
||||
ARCH="$2"
|
||||
CONTROL_FILE="$3"
|
||||
DESKTOP_FILE="$4"
|
||||
ICON_FILE="$5"
|
||||
DEB_PATH="$6"
|
||||
|
||||
TARGET_ROOT="`mktemp -d`"
|
||||
chmod 755 "$TARGET_ROOT"
|
||||
TARGET="$TARGET_ROOT/atom-$VERSION-amd64"
|
||||
TARGET="$TARGET_ROOT/atom-$VERSION-$ARCH"
|
||||
|
||||
mkdir -p "$TARGET/usr"
|
||||
env INSTALL_PREFIX="$TARGET/usr" script/grunt install
|
||||
@@ -30,5 +31,5 @@ mkdir -p "$TARGET/usr/share/pixmaps"
|
||||
cp "$ICON_FILE" "$TARGET/usr/share/pixmaps"
|
||||
|
||||
dpkg-deb -b "$TARGET"
|
||||
mv "$TARGET_ROOT/atom-$VERSION-amd64.deb" "$DEB_PATH"
|
||||
mv "$TARGET_ROOT/atom-$VERSION-$ARCH.deb" "$DEB_PATH"
|
||||
rm -rf $TARGET_ROOT
|
||||
|
||||
@@ -11,8 +11,15 @@ module.exports = function(cb) {
|
||||
return;
|
||||
}
|
||||
|
||||
verifyPython27(function(error, pythonSuccessMessage) {
|
||||
cb(error, (nodeSuccessMessage + "\n" + pythonSuccessMessage).trim());
|
||||
verifyNpm(function(error, npmSuccessMessage) {
|
||||
if (error) {
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
verifyPython27(function(error, pythonSuccessMessage) {
|
||||
cb(error, (nodeSuccessMessage + "\n" + npmSuccessMessage + "\n" + pythonSuccessMessage).trim());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,6 +39,31 @@ function verifyNode(cb) {
|
||||
}
|
||||
}
|
||||
|
||||
function verifyNpm(cb) {
|
||||
var localNpmPath = path.resolve(__dirname, '..', '..', 'build', 'node_modules', '.bin', 'npm');
|
||||
if (process.platform === 'win32')
|
||||
localNpmPath += ".cmd";
|
||||
|
||||
var npmCommand = fs.existsSync(localNpmPath) ? localNpmPath : 'npm';
|
||||
if (npmCommand === 'npm' && process.platform === 'win32')
|
||||
npmCommand += ".cmd";
|
||||
|
||||
childProcess.execFile(npmCommand, ['-v'], { env: process.env }, function(err, stdout) {
|
||||
if (err)
|
||||
return cb("npm 1.4 is required to build Atom. An error (" + err + ") occured when checking the version.");
|
||||
|
||||
var npmVersion = stdout ? stdout.trim() : '';
|
||||
var versionArray = npmVersion.split('.');
|
||||
var npmMajorVersion = +versionArray[0] || 0;
|
||||
var npmMinorVersion = +versionArray[1] || 0;
|
||||
if (npmMajorVersion === 1 && npmMinorVersion < 4)
|
||||
cb("npm v1.4+ is required to build Atom.");
|
||||
else
|
||||
cb(null, "npm: v" + npmVersion);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function verifyPython27(cb) {
|
||||
if (process.platform == 'win32') {
|
||||
if (!pythonExecutable) {
|
||||
|
||||
@@ -2,6 +2,7 @@ path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
{convertStackTrace} = require 'coffeestack'
|
||||
{View, $, $$} = require '../src/space-pen-extensions'
|
||||
grim = require 'grim'
|
||||
|
||||
sourceMaps = {}
|
||||
formatStackTrace = (spec, message='', stackTrace) ->
|
||||
@@ -52,6 +53,11 @@ class AtomReporter extends View
|
||||
@div outlet: "message", class: 'message'
|
||||
@div outlet: "results", class: 'results'
|
||||
|
||||
@div outlet: "deprecations", class: 'status alert alert-warning', style: 'display: none', =>
|
||||
@span outlet: 'deprecationStatus', '0 deprecations'
|
||||
@div class: 'deprecation-toggle'
|
||||
@div outlet: 'deprecationList', class: 'deprecation-list'
|
||||
|
||||
startedAt: null
|
||||
runningSpecCount: 0
|
||||
completeSpecCount: 0
|
||||
@@ -59,6 +65,7 @@ class AtomReporter extends View
|
||||
failedCount: 0
|
||||
skippedCount: 0
|
||||
totalSpecCount: 0
|
||||
deprecationCount: 0
|
||||
@timeoutId: 0
|
||||
|
||||
reportRunnerStarting: (runner) ->
|
||||
@@ -88,6 +95,29 @@ class AtomReporter extends View
|
||||
reportSpecStarting: (spec) ->
|
||||
@specStarted(spec)
|
||||
|
||||
addDeprecations: (spec) ->
|
||||
deprecations = grim.getDeprecations()
|
||||
@deprecationCount += deprecations.length
|
||||
@deprecations.show() if @deprecationCount > 0
|
||||
if @deprecationCount is 1
|
||||
@deprecationStatus.text("1 deprecation")
|
||||
else
|
||||
@deprecationStatus.text("#{@deprecationCount} deprecations")
|
||||
|
||||
for deprecation in deprecations
|
||||
@deprecationList.append $$ ->
|
||||
@div class: 'padded', =>
|
||||
@div class: 'result-message fail deprecation-message', deprecation.message
|
||||
|
||||
for stack in deprecation.stacks
|
||||
fullStack = stack.map ({functionName, location}) ->
|
||||
if functionName is '<unknown>'
|
||||
" at #{location}"
|
||||
else
|
||||
" at #{functionName} (#{location})"
|
||||
@pre class: 'stack-trace padded', formatStackTrace(spec, deprecation.message, fullStack.join('\n'))
|
||||
grim.clearDeprecations()
|
||||
|
||||
handleEvents: ->
|
||||
$(document).on "click", ".spec-toggle", ({currentTarget}) =>
|
||||
element = $(currentTarget)
|
||||
@@ -96,6 +126,13 @@ class AtomReporter extends View
|
||||
element.toggleClass('folded')
|
||||
false
|
||||
|
||||
$(document).on "click", ".deprecation-toggle", ({currentTarget}) =>
|
||||
element = $(currentTarget)
|
||||
deprecationList = $(document).find('.deprecation-list')
|
||||
deprecationList.toggle()
|
||||
element.toggleClass('folded')
|
||||
false
|
||||
|
||||
updateSpecCounts: ->
|
||||
if @skippedCount
|
||||
specCount = "#{@completeSpecCount - @skippedCount}/#{@totalSpecCount - @skippedCount} (#{@skippedCount} skipped)"
|
||||
@@ -175,6 +212,7 @@ class AtomReporter extends View
|
||||
specView = new SpecResultView(spec)
|
||||
specView.attach()
|
||||
@failedCount++
|
||||
@addDeprecations(spec)
|
||||
|
||||
class SuiteResultView extends View
|
||||
@content: ->
|
||||
|
||||
@@ -912,6 +912,42 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.findMarkers(class: 'a', startBufferRow: 0, endBufferRow: 3)).toEqual [marker1]
|
||||
expect(displayBuffer.findMarkers(endBufferRow: 10)).toEqual [marker3]
|
||||
|
||||
it "allows the startScreenRow and endScreenRow to be specified", ->
|
||||
marker1 = displayBuffer.markBufferRange([[6, 0], [7, 0]], class: 'a')
|
||||
marker2 = displayBuffer.markBufferRange([[9, 0], [10, 0]], class: 'a')
|
||||
displayBuffer.createFold(4, 7)
|
||||
expect(displayBuffer.findMarkers(class: 'a', startScreenRow: 6, endScreenRow: 7)).toEqual [marker2]
|
||||
|
||||
it "allows intersectsBufferRowRange to be specified", ->
|
||||
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
|
||||
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
|
||||
displayBuffer.createFold(4, 7)
|
||||
expect(displayBuffer.findMarkers(class: 'a', intersectsBufferRowRange: [5, 6])).toEqual [marker1]
|
||||
|
||||
it "allows intersectsScreenRowRange to be specified", ->
|
||||
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
|
||||
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
|
||||
displayBuffer.createFold(4, 7)
|
||||
expect(displayBuffer.findMarkers(class: 'a', intersectsScreenRowRange: [5, 10])).toEqual [marker2]
|
||||
|
||||
it "allows containedInScreenRange to be specified", ->
|
||||
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
|
||||
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
|
||||
displayBuffer.createFold(4, 7)
|
||||
expect(displayBuffer.findMarkers(class: 'a', containedInScreenRange: [[5, 0], [7, 0]])).toEqual [marker2]
|
||||
|
||||
it "allows intersectsBufferRange to be specified", ->
|
||||
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
|
||||
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
|
||||
displayBuffer.createFold(4, 7)
|
||||
expect(displayBuffer.findMarkers(class: 'a', intersectsBufferRange: [[5, 0], [6, 0]])).toEqual [marker1]
|
||||
|
||||
it "allows intersectsScreenRange to be specified", ->
|
||||
marker1 = displayBuffer.markBufferRange([[5, 0], [5, 0]], class: 'a')
|
||||
marker2 = displayBuffer.markBufferRange([[8, 0], [8, 0]], class: 'a')
|
||||
displayBuffer.createFold(4, 7)
|
||||
expect(displayBuffer.findMarkers(class: 'a', intersectsScreenRange: [[5, 0], [10, 0]])).toEqual [marker2]
|
||||
|
||||
describe "marker destruction", ->
|
||||
it "allows markers to be destroyed", ->
|
||||
marker = displayBuffer.markScreenRange([[5, 4], [5, 10]])
|
||||
@@ -956,6 +992,17 @@ describe "DisplayBuffer", ->
|
||||
expect(start.top).toBe 5 * 20
|
||||
expect(start.left).toBe (4 * 10) + (6 * 11)
|
||||
|
||||
describe "decorations", ->
|
||||
it "can add decorations associated with markers and remove them", ->
|
||||
decoration = {type: 'gutter', class: 'one'}
|
||||
marker = displayBuffer.markBufferRange([[2, 13], [3, 15]])
|
||||
|
||||
displayBuffer.addDecorationForMarker(marker, decoration)
|
||||
expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration
|
||||
|
||||
displayBuffer.removeDecorationForMarker(marker, decoration)
|
||||
expect(displayBuffer.decorationsForScreenRowRange(2, 3)[marker.id]).not.toBeDefined()
|
||||
|
||||
describe "::setScrollTop", ->
|
||||
beforeEach ->
|
||||
displayBuffer.manageScrollPosition = true
|
||||
@@ -997,17 +1044,26 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.setScrollLeft(maxScrollLeft + 50)).toBe maxScrollLeft
|
||||
expect(displayBuffer.getScrollLeft()).toBe maxScrollLeft
|
||||
|
||||
describe "::scrollToScreenPosition(position)", ->
|
||||
it "sets the scroll top and scroll left so the given screen position is in view", ->
|
||||
describe "::scrollToScreenPosition(position, [options])", ->
|
||||
beforeEach ->
|
||||
displayBuffer.manageScrollPosition = true
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
displayBuffer.setHorizontalScrollbarHeight(0)
|
||||
|
||||
displayBuffer.setHeight(50)
|
||||
displayBuffer.setWidth(50)
|
||||
maxScrollTop = displayBuffer.getScrollHeight() - displayBuffer.getHeight()
|
||||
|
||||
it "sets the scroll top and scroll left so the given screen position is in view", ->
|
||||
displayBuffer.scrollToScreenPosition([8, 20])
|
||||
expect(displayBuffer.getScrollBottom()).toBe (9 + displayBuffer.getVerticalScrollMargin()) * 10
|
||||
expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10
|
||||
|
||||
describe "when the 'center' option is true", ->
|
||||
it "vertically scrolls to center the given position vertically", ->
|
||||
displayBuffer.scrollToScreenPosition([8, 20], center: true)
|
||||
expect(displayBuffer.getScrollTop()).toBe (8 * 10) + 5 - (50 / 2)
|
||||
expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10
|
||||
|
||||
it "does not scroll vertically if the position is already in view", ->
|
||||
displayBuffer.scrollToScreenPosition([4, 20], center: true)
|
||||
expect(displayBuffer.getScrollTop()).toBe 0
|
||||
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+115
-2
@@ -863,6 +863,30 @@ describe "Editor", ->
|
||||
expect(selection1.getScreenRange()).toEqual [[3, 0], [4, 5]]
|
||||
expect(selection2.getScreenRange()).toEqual [[5, 6], [6, 2]]
|
||||
|
||||
describe ".selectToBeginningOfNextParagraph()", ->
|
||||
it "selects from the cursor to first line of the next paragraph", ->
|
||||
editor.setSelectedBufferRange([[3, 0], [4, 5]])
|
||||
editor.addCursorAtScreenPosition([5, 6])
|
||||
editor.selectToScreenPosition([6, 2])
|
||||
|
||||
editor.selectToBeginningOfNextParagraph()
|
||||
|
||||
selections = editor.getSelections()
|
||||
expect(selections.length).toBe 1
|
||||
expect(selections[0].getScreenRange()).toEqual [[3, 0], [10, 0]]
|
||||
|
||||
describe ".selectToBeginningOfPreviousParagraph()", ->
|
||||
it "selects from the cursor to the first line of the pevious paragraph", ->
|
||||
editor.setSelectedBufferRange([[3, 0], [4, 5]])
|
||||
editor.addCursorAtScreenPosition([5, 6])
|
||||
editor.selectToScreenPosition([6, 2])
|
||||
|
||||
editor.selectToBeginningOfPreviousParagraph()
|
||||
|
||||
selections = editor.getSelections()
|
||||
expect(selections.length).toBe 1
|
||||
expect(selections[0].getScreenRange()).toEqual [[0, 0], [5, 6]]
|
||||
|
||||
it "merges selections if they intersect, maintaining the directionality of the last selection", ->
|
||||
editor.setCursorScreenPosition([4, 10])
|
||||
editor.selectToScreenPosition([5, 27])
|
||||
@@ -1196,6 +1220,21 @@ describe "Editor", ->
|
||||
expect(editor.selectMarker(marker)).toBeFalsy()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 0]]
|
||||
|
||||
describe ".addSelectionForBufferRange(bufferRange)", ->
|
||||
it "adds a selection for the specified buffer range", ->
|
||||
editor.addSelectionForBufferRange([[3, 4], [5, 6]])
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 0]], [[3, 4], [5, 6]]]
|
||||
|
||||
it "autoscrolls to the added selection if needed", ->
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHeight(50)
|
||||
editor.setWidth(50)
|
||||
|
||||
editor.addSelectionForBufferRange([[8, 10], [8, 15]])
|
||||
expect(editor.getScrollTop()).toBe 75
|
||||
expect(editor.getScrollLeft()).toBe 160
|
||||
|
||||
describe ".addSelectionBelow()", ->
|
||||
describe "when the selection is non-empty", ->
|
||||
it "selects the same region of the line below current selections if possible", ->
|
||||
@@ -1622,7 +1661,7 @@ describe "Editor", ->
|
||||
editor.setCursorBufferPosition([9,2])
|
||||
editor.insertNewline()
|
||||
expect(editor.lineForBufferRow(10)).toBe ' };'
|
||||
|
||||
|
||||
describe ".backspace()", ->
|
||||
describe "when there is a single cursor", ->
|
||||
changeScreenRangeHandler = null
|
||||
@@ -1807,6 +1846,32 @@ describe "Editor", ->
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
|
||||
expect(buffer.lineForRow(2)).toBe 'if (items.length <= 1) return items;'
|
||||
|
||||
describe '.deleteToEndOfLine()', ->
|
||||
describe 'when no text is selected', ->
|
||||
it 'deletes all text between the cursor and the end of the line', ->
|
||||
editor.setCursorBufferPosition([1, 24])
|
||||
editor.addCursorAtBufferPosition([2, 5])
|
||||
[cursor1, cursor2] = editor.getCursors()
|
||||
|
||||
editor.deleteToEndOfLine()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(it'
|
||||
expect(buffer.lineForRow(2)).toBe ' i'
|
||||
expect(cursor1.getBufferPosition()).toEqual [1, 24]
|
||||
expect(cursor2.getBufferPosition()).toEqual [2, 5]
|
||||
|
||||
describe 'when at the end of the line', ->
|
||||
it 'deletes the next newline', ->
|
||||
editor.setCursorBufferPosition([1, 30])
|
||||
editor.deleteToEndOfLine()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) { if (items.length <= 1) return items;'
|
||||
|
||||
describe 'when text is selected', ->
|
||||
it 'deletes only the text in the selection', ->
|
||||
editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]])
|
||||
editor.deleteToEndOfLine()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
|
||||
expect(buffer.lineForRow(2)).toBe 'if (items.length <= 1) return items;'
|
||||
|
||||
describe ".deleteToBeginningOfLine()", ->
|
||||
describe "when no text is selected", ->
|
||||
it "deletes all text between the cursor and the beginning of the line", ->
|
||||
@@ -2234,6 +2299,15 @@ describe "Editor", ->
|
||||
editor.outdentSelectedRows()
|
||||
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
|
||||
|
||||
it "outdents only up to the first non-space non-tab character", ->
|
||||
editor.insertText(' \tfoo\t ')
|
||||
editor.outdentSelectedRows()
|
||||
expect(buffer.lineForRow(0)).toBe "\tfoo\t var quicksort = function () {"
|
||||
editor.outdentSelectedRows()
|
||||
expect(buffer.lineForRow(0)).toBe "foo\t var quicksort = function () {"
|
||||
editor.outdentSelectedRows()
|
||||
expect(buffer.lineForRow(0)).toBe "foo\t var quicksort = function () {"
|
||||
|
||||
describe "when one line is selected", ->
|
||||
it "outdents line and retains editor", ->
|
||||
editor.setSelectedBufferRange([[1,4], [1,14]])
|
||||
@@ -3187,21 +3261,60 @@ describe "Editor", ->
|
||||
expect(editor.getScrollRight()).toBe (9 + editor.getHorizontalScrollMargin()) * 10
|
||||
|
||||
describe ".pageUp/Down()", ->
|
||||
it "scrolls one screen height up or down", ->
|
||||
it "scrolls one screen height up or down and moves the cursor one page length", ->
|
||||
editor.manageScrollPosition = true
|
||||
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setHeight(50)
|
||||
expect(editor.getScrollHeight()).toBe 130
|
||||
expect(editor.getCursorBufferPosition().row).toBe 0
|
||||
|
||||
editor.pageDown()
|
||||
expect(editor.getScrollTop()).toBe 50
|
||||
expect(editor.getCursorBufferPosition().row).toBe 5
|
||||
|
||||
editor.pageDown()
|
||||
expect(editor.getScrollTop()).toBe 80
|
||||
expect(editor.getCursorBufferPosition().row).toBe 10
|
||||
|
||||
editor.pageUp()
|
||||
expect(editor.getScrollTop()).toBe 30
|
||||
expect(editor.getCursorBufferPosition().row).toBe 5
|
||||
|
||||
editor.pageUp()
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
expect(editor.getCursorBufferPosition().row).toBe 0
|
||||
|
||||
describe ".selectPageUp/Down()", ->
|
||||
it "selects one screen height of text up or down", ->
|
||||
editor.manageScrollPosition = true
|
||||
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setHeight(50)
|
||||
expect(editor.getScrollHeight()).toBe 130
|
||||
expect(editor.getCursorBufferPosition().row).toBe 0
|
||||
|
||||
editor.selectPageDown()
|
||||
expect(editor.getScrollTop()).toBe 30
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [5,0]]]
|
||||
|
||||
editor.selectPageDown()
|
||||
expect(editor.getScrollTop()).toBe 80
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [10,0]]]
|
||||
|
||||
editor.selectPageDown()
|
||||
expect(editor.getScrollTop()).toBe 80
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [12,2]]]
|
||||
|
||||
editor.moveCursorToBottom()
|
||||
editor.selectPageUp()
|
||||
expect(editor.getScrollTop()).toBe 50
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[7,0], [12,2]]]
|
||||
|
||||
editor.selectPageUp()
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[2,0], [12,2]]]
|
||||
|
||||
editor.selectPageUp()
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [12,2]]]
|
||||
|
||||
@@ -10,6 +10,8 @@ describe "EditorView", ->
|
||||
[buffer, editorView, editor, cachedEditor, cachedLineHeight, cachedCharWidth, fart] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.config.set 'core.useReactEditor', false
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
@@ -1609,7 +1611,7 @@ describe "EditorView", ->
|
||||
editorView.attachToDom()
|
||||
|
||||
expect(atom.config.get("editor.showInvisibles")).toBeFalsy()
|
||||
expect(editorView.renderedLines.find('.line').text()).toBe " a line with tabs and spaces "
|
||||
expect(editorView.renderedLines.find('.line').text()).toBe " a line with tabs and spaces "
|
||||
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
space = editorView.invisibles?.space
|
||||
@@ -1618,10 +1620,10 @@ describe "EditorView", ->
|
||||
expect(tab).toBeTruthy()
|
||||
eol = editorView.invisibles?.eol
|
||||
expect(eol).toBeTruthy()
|
||||
expect(editorView.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab} and spaces#{space}#{eol}"
|
||||
expect(editorView.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab}and spaces#{space}#{eol}"
|
||||
|
||||
atom.config.set("editor.showInvisibles", false)
|
||||
expect(editorView.renderedLines.find('.line').text()).toBe " a line with tabs and spaces "
|
||||
expect(editorView.renderedLines.find('.line').text()).toBe " a line with tabs and spaces "
|
||||
|
||||
it "displays newlines as their own token outside of the other tokens scope", ->
|
||||
editorView.setShowInvisibles(true)
|
||||
@@ -1634,7 +1636,7 @@ describe "EditorView", ->
|
||||
editorView.attachToDom()
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
atom.config.set("editor.invisibles", eol: ";", space: "_", tab: "tab")
|
||||
expect(editorView.find(".line:first").text()).toBe "_tab _;"
|
||||
expect(editorView.find(".line:first").text()).toBe "_tab_;"
|
||||
|
||||
it "displays trailing carriage return using a visible non-empty value", ->
|
||||
editor.setText "a line that ends with a carriage return\r\n"
|
||||
@@ -2080,7 +2082,7 @@ describe "EditorView", ->
|
||||
tab = miniEditor.invisibles?.tab
|
||||
expect(tab).toBeTruthy()
|
||||
miniEditor.getEditor().setText(" a line with tabs\tand spaces ")
|
||||
expect(miniEditor.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab} and spaces#{space}"
|
||||
expect(miniEditor.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab}and spaces#{space}"
|
||||
|
||||
it "doesn't show the indent guide", ->
|
||||
atom.config.set "editor.showIndentGuide", true
|
||||
|
||||
@@ -52,12 +52,17 @@ describe "LanguageMode", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe " // "
|
||||
|
||||
buffer.setText (' a\n \n b')
|
||||
buffer.setText(' a\n \n b')
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 2)
|
||||
expect(buffer.lineForRow(0)).toBe " // a"
|
||||
expect(buffer.lineForRow(1)).toBe " // "
|
||||
expect(buffer.lineForRow(2)).toBe " // b"
|
||||
|
||||
buffer.setText(' \n // var i;')
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 1)
|
||||
expect(buffer.lineForRow(0)).toBe ' '
|
||||
expect(buffer.lineForRow(1)).toBe ' var i;'
|
||||
|
||||
describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
|
||||
@@ -16,6 +16,7 @@ describe "PaneContainer", ->
|
||||
containerA = new PaneContainer(root: pane1A)
|
||||
pane2A = pane1A.splitRight(items: [new Item])
|
||||
pane3A = pane2A.splitDown(items: [new Item])
|
||||
pane3A.focus()
|
||||
|
||||
it "preserves the focused pane across serialization", ->
|
||||
expect(pane3A.focused).toBe true
|
||||
|
||||
@@ -439,10 +439,11 @@ describe "Pane", ->
|
||||
expect(column.orientation).toBe 'vertical'
|
||||
expect(column.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
it "sets up the new pane to be focused", ->
|
||||
expect(pane1.focused).toBe false
|
||||
it "activates the new pane", ->
|
||||
expect(pane1.isActive()).toBe true
|
||||
pane2 = pane1.splitRight()
|
||||
expect(pane2.focused).toBe true
|
||||
expect(pane1.isActive()).toBe false
|
||||
expect(pane2.isActive()).toBe true
|
||||
|
||||
describe "::destroy()", ->
|
||||
[container, pane1, pane2] = []
|
||||
|
||||
@@ -199,6 +199,7 @@ describe "PaneView", ->
|
||||
|
||||
it "focuses the next pane", ->
|
||||
container.attachToDom()
|
||||
pane2.activate()
|
||||
expect(pane.hasFocus()).toBe false
|
||||
expect(pane2.hasFocus()).toBe true
|
||||
pane2Model.destroy()
|
||||
|
||||
@@ -24,19 +24,6 @@ describe "SpacePen extensions", ->
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
describe "tooltips", ->
|
||||
describe "humanizeKeystrokes", ->
|
||||
humanizeKeystrokes = $.fn.setTooltip.humanizeKeystrokes
|
||||
|
||||
it "replaces single keystroke", ->
|
||||
expect(humanizeKeystrokes('cmd-O')).toEqual '⌘⇧O'
|
||||
expect(humanizeKeystrokes('cmd-shift-up')).toEqual '⌘⇧↑'
|
||||
expect(humanizeKeystrokes('cmd-option-down')).toEqual '⌘⌥↓'
|
||||
expect(humanizeKeystrokes('cmd-option-left')).toEqual '⌘⌥←'
|
||||
expect(humanizeKeystrokes('cmd-option-right')).toEqual '⌘⌥→'
|
||||
|
||||
it "replaces multiple keystroke", ->
|
||||
expect(humanizeKeystrokes('cmd-o ctrl-2')).toEqual '⌘O ⌃2'
|
||||
|
||||
describe "when the window is resized", ->
|
||||
it "hides the tooltips", ->
|
||||
class TooltipView extends View
|
||||
|
||||
@@ -15,6 +15,7 @@ Project = require '../src/project'
|
||||
Editor = require '../src/editor'
|
||||
EditorView = require '../src/editor-view'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
EditorComponent = require '../src/editor-component'
|
||||
pathwatcher = require 'pathwatcher'
|
||||
clipboard = require 'clipboard'
|
||||
|
||||
@@ -33,7 +34,12 @@ $(window).on 'unload', ->
|
||||
$('html,body').css('overflow', 'auto')
|
||||
|
||||
jasmine.getEnv().addEqualityTester(_.isEqual) # Use underscore's definition of equality for toEqual assertions
|
||||
jasmine.getEnv().defaultTimeoutInterval = 5000
|
||||
|
||||
if process.platform is 'win32' and process.env.JANKY_SHA1
|
||||
# Use longer timeout on Windows CI
|
||||
jasmine.getEnv().defaultTimeoutInterval = 30000
|
||||
else
|
||||
jasmine.getEnv().defaultTimeoutInterval = 5000
|
||||
|
||||
specPackageName = null
|
||||
specPackagePath = null
|
||||
@@ -89,12 +95,14 @@ beforeEach ->
|
||||
config.set "editor.autoIndent", false
|
||||
config.set "core.disabledPackages", ["package-that-throws-an-exception",
|
||||
"package-with-broken-package-json", "package-with-broken-keymap"]
|
||||
config.set "core.useReactEditor", false
|
||||
config.set "core.useReactEditor", true
|
||||
config.save.reset()
|
||||
atom.config = config
|
||||
|
||||
# make editor display updates synchronous
|
||||
spyOn(EditorView.prototype, 'requestDisplayUpdate').andCallFake -> @updateDisplay()
|
||||
EditorComponent.performSyncUpdates = true
|
||||
|
||||
spyOn(WorkspaceView.prototype, 'setTitle').andCallFake (@title) ->
|
||||
spyOn(window, "setTimeout").andCallFake window.fakeSetTimeout
|
||||
spyOn(window, "clearTimeout").andCallFake window.fakeClearTimeout
|
||||
|
||||
@@ -344,6 +344,73 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand"
|
||||
|
||||
it "aligns the hard tabs to the correct tab stop column", ->
|
||||
buffer.setText """
|
||||
1\t2 \t3\t4
|
||||
12\t3 \t4\t5
|
||||
123\t4 \t5\t6
|
||||
"""
|
||||
|
||||
tokenizedBuffer.setTabLength(4)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 3
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 2
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1
|
||||
|
||||
tokenizedBuffer.setTabLength(3)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 2
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 3
|
||||
|
||||
tokenizedBuffer.setTabLength(2)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 2
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1
|
||||
|
||||
tokenizedBuffer.setTabLength(1)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1
|
||||
|
||||
describe "when the buffer contains surrogate pairs", ->
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
|
||||
@@ -196,12 +196,14 @@ describe "WorkspaceView", ->
|
||||
atom.workspaceView.height(200)
|
||||
atom.workspaceView.attachToDom()
|
||||
rightEditorView = atom.workspaceView.getActiveView()
|
||||
rightEditorView.getEditor().setText(" \t ")
|
||||
rightEditorView.getEditor().setText("\t ")
|
||||
leftEditorView = rightEditorView.splitLeft()
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
withInvisiblesShowing = "#{rightEditorView.invisibles.space}#{rightEditorView.invisibles.tab} #{rightEditorView.invisibles.space}#{rightEditorView.invisibles.eol}"
|
||||
{invisibles} = rightEditorView.component.state
|
||||
{space, tab, eol} = invisibles
|
||||
withInvisiblesShowing = "#{tab} #{space}#{space}#{eol}"
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
@@ -135,6 +135,13 @@ class Atom extends Model
|
||||
@executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()')
|
||||
@emit 'uncaught-error', arguments...
|
||||
|
||||
nativeConsoleError = console.error
|
||||
|
||||
console.error = =>
|
||||
@openDevTools()
|
||||
@executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()')
|
||||
nativeConsoleError.call console, arguments...
|
||||
|
||||
@unsubscribe()
|
||||
@setBodyPlatformClass()
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@ CursorComponent = React.createClass
|
||||
displayName: 'CursorComponent'
|
||||
|
||||
render: ->
|
||||
{editor, screenRange, scrollTop, scrollLeft} = @props
|
||||
{editor, screenRange, scrollTop, scrollLeft, defaultCharWidth} = @props
|
||||
{top, left, height, width} = editor.pixelRectForScreenRange(screenRange)
|
||||
top -= scrollTop
|
||||
left -= scrollLeft
|
||||
width = defaultCharWidth if width is 0
|
||||
WebkitTransform = "translate3d(#{left}px, #{top}px, 0px)"
|
||||
|
||||
div className: 'cursor', style: {height, width, WebkitTransform}
|
||||
|
||||
+2
-1
@@ -114,9 +114,10 @@ class Cursor extends Model
|
||||
|
||||
# Public: Get the RegExp used by the cursor to determine what a "word" is.
|
||||
#
|
||||
# options: An {Object} with the following keys:
|
||||
# options: An optional {Object} with the following keys:
|
||||
# :includeNonWordCharacters - A {Boolean} indicating whether to include
|
||||
# non-word characters in the regex.
|
||||
# (default: true)
|
||||
#
|
||||
# Returns a {RegExp}.
|
||||
wordRegExp: ({includeNonWordCharacters}={})->
|
||||
|
||||
@@ -12,7 +12,7 @@ CursorsComponent = React.createClass
|
||||
cursorBlinkIntervalHandle: null
|
||||
|
||||
render: ->
|
||||
{editor, cursorScreenRanges, scrollTop, scrollLeft} = @props
|
||||
{editor, cursorScreenRanges, scrollTop, scrollLeft, defaultCharWidth} = @props
|
||||
{blinkOff} = @state
|
||||
|
||||
className = 'cursors'
|
||||
@@ -21,7 +21,7 @@ CursorsComponent = React.createClass
|
||||
div {className},
|
||||
if @isMounted()
|
||||
for key, screenRange of cursorScreenRanges
|
||||
CursorComponent({key, editor, screenRange, scrollTop, scrollLeft})
|
||||
CursorComponent({key, editor, screenRange, scrollTop, scrollLeft, defaultCharWidth})
|
||||
|
||||
getInitialState: ->
|
||||
blinkOff: false
|
||||
@@ -34,7 +34,10 @@ CursorsComponent = React.createClass
|
||||
|
||||
shouldComponentUpdate: (newProps, newState) ->
|
||||
not newState.blinkOff is @state.blinkOff or
|
||||
not isEqualForProperties(newProps, @props, 'cursorScreenRanges', 'scrollTop', 'scrollLeft', 'lineHeightInPixels', 'defaultCharWidth')
|
||||
not isEqualForProperties(newProps, @props,
|
||||
'cursorScreenRanges', 'scrollTop', 'scrollLeft', 'lineHeightInPixels',
|
||||
'defaultCharWidth', 'scopedCharacterWidthsChangeCount'
|
||||
)
|
||||
|
||||
componentWillUpdate: (newProps) ->
|
||||
@pauseCursorBlinking() if @props.cursorScreenRanges and not isEqual(newProps.cursorScreenRanges, @props.cursorScreenRanges)
|
||||
|
||||
@@ -111,6 +111,34 @@ class DisplayBufferMarker
|
||||
setTailBufferPosition: (bufferPosition) ->
|
||||
@bufferMarker.setTailPosition(bufferPosition)
|
||||
|
||||
# Retrieves the screen position of the marker's start. This will always be
|
||||
# less than or equal to the result of {DisplayBufferMarker::getEndScreenPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getStartScreenPosition: ->
|
||||
@displayBuffer.screenPositionForBufferPosition(@getStartBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Retrieves the buffer position of the marker's start. This will always be
|
||||
# less than or equal to the result of {DisplayBufferMarker::getEndBufferPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getStartBufferPosition: ->
|
||||
@bufferMarker.getStartPosition()
|
||||
|
||||
# Retrieves the screen position of the marker's end. This will always be
|
||||
# greater than or equal to the result of {DisplayBufferMarker::getStartScreenPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getEndScreenPosition: ->
|
||||
@displayBuffer.screenPositionForBufferPosition(@getEndBufferPosition(), wrapAtSoftNewlines: true)
|
||||
|
||||
# Retrieves the buffer position of the marker's end. This will always be
|
||||
# greater than or equal to the result of {DisplayBufferMarker::getStartBufferPosition}.
|
||||
#
|
||||
# Returns a {Point}.
|
||||
getEndBufferPosition: ->
|
||||
@bufferMarker.getEndPosition()
|
||||
|
||||
# Sets the marker's tail to the same position as the marker's head.
|
||||
#
|
||||
# This only works if there isn't already a tail position.
|
||||
|
||||
+106
-14
@@ -34,6 +34,7 @@ class DisplayBuffer extends Model
|
||||
horizontalScrollMargin: 6
|
||||
horizontalScrollbarHeight: 15
|
||||
verticalScrollbarWidth: 15
|
||||
scopedCharacterWidthsChangeCount: 0
|
||||
|
||||
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) ->
|
||||
super
|
||||
@@ -43,6 +44,8 @@ class DisplayBuffer extends Model
|
||||
@charWidthsByScope = {}
|
||||
@markers = {}
|
||||
@foldsByMarkerId = {}
|
||||
@decorationsByMarkerId = {}
|
||||
@decorationMarkerSubscriptions = {}
|
||||
@updateAllScreenLines()
|
||||
@createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes())
|
||||
@subscribe @tokenizedBuffer, 'grammar-changed', (grammar) => @emit 'grammar-changed', grammar
|
||||
@@ -220,16 +223,17 @@ class DisplayBuffer extends Model
|
||||
|
||||
setScopedCharWidth: (scopeNames, char, width) ->
|
||||
@getScopedCharWidths(scopeNames)[char] = width
|
||||
@emit 'character-widths-changed', @scopedCharacterWidthsChangeCount++
|
||||
|
||||
setScopedCharWidths: (scopeNames, charWidths) ->
|
||||
_.extend(@getScopedCharWidths(scopeNames), charWidths)
|
||||
@emit 'character-widths-changed', @scopedCharacterWidthsChangeCount++
|
||||
|
||||
clearScopedCharWidths: ->
|
||||
@charWidthsByScope = {}
|
||||
|
||||
getScrollHeight: ->
|
||||
unless @getLineHeightInPixels() > 0
|
||||
throw new Error("You must assign lineHeightInPixels before calling ::getScrollHeight()")
|
||||
return 0 unless @getLineHeightInPixels() > 0
|
||||
|
||||
@getLineCount() * @getLineHeightInPixels()
|
||||
|
||||
@@ -237,8 +241,7 @@ class DisplayBuffer extends Model
|
||||
(@getMaxLineLength() * @getDefaultCharWidth()) + @getCursorWidth()
|
||||
|
||||
getVisibleRowRange: ->
|
||||
unless @getLineHeightInPixels() > 0
|
||||
throw new Error("You must assign a non-zero lineHeightInPixels before calling ::getVisibleRowRange()")
|
||||
return [0, 0] unless @getLineHeightInPixels() > 0
|
||||
|
||||
heightInLines = Math.ceil(@getHeight() / @getLineHeightInPixels()) + 1
|
||||
startRow = Math.floor(@getScrollTop() / @getLineHeightInPixels())
|
||||
@@ -254,15 +257,23 @@ class DisplayBuffer extends Model
|
||||
{start, end} = selection.getScreenRange()
|
||||
@intersectsVisibleRowRange(start.row, end.row + 1)
|
||||
|
||||
scrollToScreenRange: (screenRange) ->
|
||||
scrollToScreenRange: (screenRange, options) ->
|
||||
verticalScrollMarginInPixels = @getVerticalScrollMargin() * @getLineHeightInPixels()
|
||||
horizontalScrollMarginInPixels = @getHorizontalScrollMargin() * @getDefaultCharWidth()
|
||||
|
||||
{top, left, height, width} = @pixelRectForScreenRange(screenRange)
|
||||
bottom = top + height
|
||||
right = left + width
|
||||
desiredScrollTop = top - verticalScrollMarginInPixels
|
||||
desiredScrollBottom = bottom + verticalScrollMarginInPixels
|
||||
|
||||
if options?.center
|
||||
desiredScrollCenter = top + height / 2
|
||||
unless @getScrollTop() < desiredScrollCenter < @getScrollBottom()
|
||||
desiredScrollTop = desiredScrollCenter - @getHeight() / 2
|
||||
desiredScrollBottom = desiredScrollCenter + @getHeight() / 2
|
||||
else
|
||||
desiredScrollTop = top - verticalScrollMarginInPixels
|
||||
desiredScrollBottom = bottom + verticalScrollMarginInPixels
|
||||
|
||||
desiredScrollLeft = left - horizontalScrollMarginInPixels
|
||||
desiredScrollRight = right + horizontalScrollMarginInPixels
|
||||
|
||||
@@ -276,11 +287,11 @@ class DisplayBuffer extends Model
|
||||
else if desiredScrollRight > @getScrollRight()
|
||||
@setScrollRight(desiredScrollRight)
|
||||
|
||||
scrollToScreenPosition: (screenPosition) ->
|
||||
@scrollToScreenRange(new Range(screenPosition, screenPosition))
|
||||
scrollToScreenPosition: (screenPosition, options) ->
|
||||
@scrollToScreenRange(new Range(screenPosition, screenPosition), options)
|
||||
|
||||
scrollToBufferPosition: (bufferPosition) ->
|
||||
@scrollToScreenPosition(@screenPositionForBufferPosition(bufferPosition))
|
||||
scrollToBufferPosition: (bufferPosition, options) ->
|
||||
@scrollToScreenPosition(@screenPositionForBufferPosition(bufferPosition), options)
|
||||
|
||||
pixelRectForScreenRange: (screenRange) ->
|
||||
if screenRange.end.row > screenRange.start.row
|
||||
@@ -289,9 +300,9 @@ class DisplayBuffer extends Model
|
||||
height = (screenRange.end.row - screenRange.start.row + 1) * @getLineHeightInPixels()
|
||||
width = @getScrollWidth()
|
||||
else
|
||||
{top, left} = @pixelPositionForScreenPosition(screenRange.start)
|
||||
{top, left} = @pixelPositionForScreenPosition(screenRange.start, false)
|
||||
height = @getLineHeightInPixels()
|
||||
width = @pixelPositionForScreenPosition(screenRange.end).left - left
|
||||
width = @pixelPositionForScreenPosition(screenRange.end, false).left - left
|
||||
|
||||
{top, left, width, height}
|
||||
|
||||
@@ -520,7 +531,7 @@ class DisplayBuffer extends Model
|
||||
charWidths = @getScopedCharWidths(token.scopes)
|
||||
for char in token.value
|
||||
return {top, left} if column is targetColumn
|
||||
left += charWidths[char] ? defaultCharWidth
|
||||
left += charWidths[char] ? defaultCharWidth unless char is '\0'
|
||||
column++
|
||||
{top, left}
|
||||
|
||||
@@ -718,6 +729,65 @@ class DisplayBuffer extends Model
|
||||
rangeForAllLines: ->
|
||||
new Range([0, 0], @clipScreenPosition([Infinity, Infinity]))
|
||||
|
||||
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
decorationsByMarkerId = {}
|
||||
|
||||
for marker in @findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow])
|
||||
if decorations = @decorationsByMarkerId[marker.id]
|
||||
decorationsByMarkerId[marker.id] = decorations
|
||||
decorationsByMarkerId
|
||||
|
||||
decorationMatchesType: (decoration, type) ->
|
||||
if _.isArray(decoration.type)
|
||||
type in decoration.type
|
||||
else
|
||||
type is decoration.type
|
||||
|
||||
decorationMatchesPattern: (decoration, decorationPattern) ->
|
||||
return false unless decoration? and decorationPattern?
|
||||
for key, value of decorationPattern
|
||||
return false if decoration[key] != value
|
||||
true
|
||||
|
||||
addDecorationForMarker: (marker, decoration) ->
|
||||
unless marker?
|
||||
console.warn 'A null marker cannot be decorated'
|
||||
return
|
||||
|
||||
marker = @getMarker(marker.id)
|
||||
@decorationMarkerSubscriptions[marker.id] ?= @subscribe marker, 'destroyed', => @removeAllDecorationsForMarker(marker)
|
||||
|
||||
@decorationsByMarkerId[marker.id] ?= []
|
||||
@decorationsByMarkerId[marker.id].push(decoration)
|
||||
@emit 'decoration-added', marker, decoration
|
||||
|
||||
removeDecorationForMarker: (marker, decorationPattern) ->
|
||||
unless marker?
|
||||
console.warn 'A decoration cannot be removed from a null marker'
|
||||
return
|
||||
|
||||
return unless @decorationMarkerSubscriptions[marker.id]?
|
||||
|
||||
decorations = @decorationsByMarkerId[marker.id]
|
||||
for i in [decorations.length - 1..0]
|
||||
decoration = decorations[i]
|
||||
if @decorationMatchesPattern(decoration, decorationPattern)
|
||||
decorations.splice(i, 1)
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
|
||||
@removedAllMarkerDecorations(marker) if decorations.length is 0
|
||||
|
||||
removeAllDecorationsForMarker: (marker) ->
|
||||
decorations = @decorationsByMarkerId[marker.id].slice()
|
||||
for decoration in decorations
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
@removedAllMarkerDecorations(marker)
|
||||
|
||||
removedAllMarkerDecorations: (marker) ->
|
||||
@decorationMarkerSubscriptions[marker.id].off()
|
||||
delete @decorationsByMarkerId[marker.id]
|
||||
delete @decorationMarkerSubscriptions[marker.id]
|
||||
|
||||
# Retrieves a {DisplayBufferMarker} based on its id.
|
||||
#
|
||||
# id - A {Number} representing a marker id
|
||||
@@ -821,13 +891,34 @@ class DisplayBuffer extends Model
|
||||
key = 'startRow'
|
||||
when 'endBufferRow'
|
||||
key = 'endRow'
|
||||
when 'startScreenRow'
|
||||
key = 'startRow'
|
||||
value = @bufferRowForScreenRow(value)
|
||||
when 'endScreenRow'
|
||||
key = 'endRow'
|
||||
value = @bufferRowForScreenRow(value)
|
||||
when 'intersectsBufferRowRange'
|
||||
key = 'intersectsRowRange'
|
||||
when 'intersectsScreenRowRange'
|
||||
key = 'intersectsRowRange'
|
||||
[startRow, endRow] = value
|
||||
value = [@bufferRowForScreenRow(startRow), @bufferRowForScreenRow(endRow)]
|
||||
when 'containsBufferRange'
|
||||
key = 'containsRange'
|
||||
when 'containsBufferPosition'
|
||||
key = 'containsPosition'
|
||||
when 'containedInBufferRange'
|
||||
key = 'containedInRange'
|
||||
when 'containedInScreenRange'
|
||||
key = 'containedInRange'
|
||||
value = @bufferRangeForScreenRange(value)
|
||||
when 'intersectsBufferRange'
|
||||
key = 'intersectsRange'
|
||||
when 'intersectsScreenRange'
|
||||
key = 'intersectsRange'
|
||||
value = @bufferRangeForScreenRange(value)
|
||||
bufferMarkerParams[key] = value
|
||||
|
||||
bufferMarkerParams
|
||||
|
||||
findFoldMarker: (attributes) ->
|
||||
@@ -961,6 +1052,7 @@ class DisplayBuffer extends Model
|
||||
@emit 'marker-created', @getMarker(marker.id)
|
||||
|
||||
createFoldForMarker: (marker) ->
|
||||
@addDecorationForMarker(marker, type: 'gutter', class: 'folded')
|
||||
new Fold(this, marker)
|
||||
|
||||
foldForMarker: (marker) ->
|
||||
|
||||
+298
-161
@@ -12,15 +12,19 @@ ScrollbarComponent = require './scrollbar-component'
|
||||
ScrollbarCornerComponent = require './scrollbar-corner-component'
|
||||
SubscriberMixin = require './subscriber-mixin'
|
||||
|
||||
DummyHighlightDecoration = {id: 'dummy', screenRange: new Range(new Point(0, 0), new Point(0, 0)), decorations: [{class: 'dummy'}]}
|
||||
|
||||
module.exports =
|
||||
EditorComponent = React.createClass
|
||||
displayName: 'EditorComponent'
|
||||
mixins: [SubscriberMixin]
|
||||
|
||||
statics:
|
||||
performSyncUpdates: false
|
||||
|
||||
pendingScrollTop: null
|
||||
pendingScrollLeft: null
|
||||
selectOnMouseMove: false
|
||||
batchingUpdates: false
|
||||
updateRequested: false
|
||||
cursorsMoved: false
|
||||
selectionChanged: false
|
||||
@@ -33,22 +37,30 @@ EditorComponent = React.createClass
|
||||
pendingHorizontalScrollDelta: 0
|
||||
mouseWheelScreenRow: null
|
||||
mouseWheelScreenRowClearDelay: 150
|
||||
scrollSensitivity: 0.4
|
||||
scrollViewMeasurementRequested: false
|
||||
overflowChangedEventsPaused: false
|
||||
overflowChangedWhilePaused: false
|
||||
measureLineHeightAndDefaultCharWidthWhenShown: false
|
||||
remeasureCharacterWidthsWhenShown: false
|
||||
inputEnabled: true
|
||||
scrollViewMeasurementInterval: 100
|
||||
scopedCharacterWidthsChangeCount: null
|
||||
|
||||
render: ->
|
||||
{focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, visible} = @state
|
||||
{editor, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
|
||||
maxLineNumberDigits = editor.getScreenLineCount().toString().length
|
||||
maxLineNumberDigits = editor.getLineCount().toString().length
|
||||
invisibles = if showInvisibles then @state.invisibles else {}
|
||||
hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty()
|
||||
|
||||
if @isMounted()
|
||||
renderedRowRange = @getRenderedRowRange()
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
cursorScreenRanges = @getCursorScreenRanges(renderedRowRange)
|
||||
selectionScreenRanges = @getSelectionScreenRanges(renderedRowRange)
|
||||
|
||||
decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow)
|
||||
highlightDecorations = @getHighlightDecorations(decorations)
|
||||
lineDecorations = @getLineDecorations(decorations)
|
||||
|
||||
scrollHeight = editor.getScrollHeight()
|
||||
scrollWidth = editor.getScrollWidth()
|
||||
scrollTop = editor.getScrollTop()
|
||||
@@ -67,11 +79,13 @@ EditorComponent = React.createClass
|
||||
|
||||
className = 'editor-contents editor-colors'
|
||||
className += ' is-focused' if focused
|
||||
className += ' has-selection' if hasSelection
|
||||
|
||||
div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1,
|
||||
GutterComponent {
|
||||
ref: 'gutter', editor, renderedRowRange, maxLineNumberDigits, scrollTop,
|
||||
scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow
|
||||
ref: 'gutter', onMouseDown: @onGutterMouseDown, onWidthChanged: @onGutterWidthChanged,
|
||||
lineDecorations, defaultCharWidth, editor, renderedRowRange, maxLineNumberDigits, scrollViewHeight,
|
||||
scrollTop, scrollHeight, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow
|
||||
}
|
||||
|
||||
div ref: 'scrollView', className: 'scroll-view', onMouseDown: @onMouseDown,
|
||||
@@ -79,19 +93,19 @@ EditorComponent = React.createClass
|
||||
ref: 'input'
|
||||
className: 'hidden-input'
|
||||
style: hiddenInputStyle
|
||||
onInput: @onInput
|
||||
onFocus: @onInputFocused
|
||||
onBlur: @onInputBlurred
|
||||
|
||||
CursorsComponent {
|
||||
editor, scrollTop, scrollLeft, cursorScreenRanges, cursorBlinkPeriod, cursorBlinkResumeDelay,
|
||||
lineHeightInPixels, defaultCharWidth
|
||||
lineHeightInPixels, defaultCharWidth, @scopedCharacterWidthsChangeCount
|
||||
}
|
||||
LinesComponent {
|
||||
ref: 'lines', editor, lineHeightInPixels, defaultCharWidth,
|
||||
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft, @scrollingVertically,
|
||||
selectionScreenRanges, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
|
||||
visible, scrollViewHeight
|
||||
ref: 'lines',
|
||||
editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations,
|
||||
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft,
|
||||
@scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
|
||||
visible, scrollViewHeight, @scopedCharacterWidthsChangeCount
|
||||
}
|
||||
|
||||
ScrollbarComponent
|
||||
@@ -126,6 +140,10 @@ EditorComponent = React.createClass
|
||||
height: horizontalScrollbarHeight
|
||||
width: verticalScrollbarWidth
|
||||
|
||||
getPageRows: ->
|
||||
{editor} = @props
|
||||
Math.max(1, Math.ceil(editor.getHeight() / editor.getLineHeightInPixels()))
|
||||
|
||||
getInitialState: ->
|
||||
visible: true
|
||||
|
||||
@@ -138,10 +156,13 @@ EditorComponent = React.createClass
|
||||
@pendingChanges = []
|
||||
@props.editor.manageScrollPosition = true
|
||||
@observeConfig()
|
||||
@setScrollSensitivity(atom.config.get('editor.scrollSensitivity'))
|
||||
|
||||
componentDidMount: ->
|
||||
{editor} = @props
|
||||
|
||||
@scrollViewMeasurementIntervalId = setInterval(@requestScrollViewMeasurement, @scrollViewMeasurementInterval)
|
||||
|
||||
@observeEditor()
|
||||
@listenForDOMEvents()
|
||||
@listenForCommands()
|
||||
@@ -151,31 +172,43 @@ EditorComponent = React.createClass
|
||||
|
||||
editor.setVisible(true)
|
||||
|
||||
editor.batchUpdates =>
|
||||
@measureLineHeightAndDefaultCharWidth()
|
||||
@measureScrollView()
|
||||
@measureScrollbars()
|
||||
@measureLineHeightAndDefaultCharWidth()
|
||||
@measureScrollView()
|
||||
@measureScrollbars()
|
||||
|
||||
componentWillUnmount: ->
|
||||
@unsubscribe()
|
||||
window.removeEventListener('resize', @onWindowResize)
|
||||
clearInterval(@scrollViewMeasurementIntervalId)
|
||||
@scrollViewMeasurementIntervalId = null
|
||||
|
||||
componentWillUpdate: ->
|
||||
@props.parentView.trigger 'cursor:moved' if @cursorsMoved
|
||||
|
||||
componentDidUpdate: (prevProps, prevState) ->
|
||||
cursorsMoved = @cursorsMoved
|
||||
selectionChanged = @selectionChanged
|
||||
@pendingChanges.length = 0
|
||||
@cursorsMoved = false
|
||||
@selectionChanged = false
|
||||
@refreshingScrollbars = false
|
||||
|
||||
if @props.editor.isAlive()
|
||||
@updateParentViewFocusedClassIfNeeded(prevState)
|
||||
@props.parentView.trigger 'cursor:moved' if cursorsMoved
|
||||
@props.parentView.trigger 'selection:changed' if selectionChanged
|
||||
@props.parentView.trigger 'editor:display-updated'
|
||||
|
||||
@measureScrollbars() if @measuringScrollbars
|
||||
@measureLineHeightAndCharWidthsIfNeeded(prevState)
|
||||
@pauseOverflowChangedEvents()
|
||||
@props.parentView.trigger 'editor:display-updated'
|
||||
@remeasureCharacterWidthsIfNeeded(prevState)
|
||||
|
||||
requestUpdate: ->
|
||||
if @batchingUpdates
|
||||
@updateRequested = true
|
||||
else
|
||||
if @performSyncUpdates ? EditorComponent.performSyncUpdates
|
||||
@forceUpdate()
|
||||
else unless @updateRequested
|
||||
@updateRequested = true
|
||||
process.nextTick =>
|
||||
@updateRequested = false
|
||||
@forceUpdate() if @isMounted()
|
||||
|
||||
getRenderedRowRange: ->
|
||||
{editor, lineOverdrawMargin} = @props
|
||||
@@ -209,30 +242,52 @@ EditorComponent = React.createClass
|
||||
cursorScreenRanges[cursor.id] = screenRange
|
||||
cursorScreenRanges
|
||||
|
||||
getSelectionScreenRanges: (renderedRowRange) ->
|
||||
getLineDecorations: (decorationsByMarkerId) ->
|
||||
{editor} = @props
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
decorationsByScreenRow = {}
|
||||
for markerId, decorations of decorationsByMarkerId
|
||||
marker = editor.getMarker(markerId)
|
||||
screenRange = null
|
||||
if marker.isValid()
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'gutter') or editor.decorationMatchesType(decoration, 'line')
|
||||
screenRange ?= marker.getScreenRange()
|
||||
startRow = screenRange.start.row
|
||||
endRow = screenRange.end.row
|
||||
endRow-- if not screenRange.isEmpty() and screenRange.end.column == 0
|
||||
for screenRow in [startRow..endRow]
|
||||
decorationsByScreenRow[screenRow] ?= []
|
||||
decorationsByScreenRow[screenRow].push decoration
|
||||
|
||||
selectionScreenRanges = {}
|
||||
for selection, index in editor.getSelections()
|
||||
screenRange = selection.getScreenRange()
|
||||
decorationsByScreenRow
|
||||
|
||||
if not screenRange.isEmpty() and screenRange.intersectsRowRange(renderedStartRow, renderedEndRow)
|
||||
selectionScreenRanges[selection.id] = screenRange
|
||||
getHighlightDecorations: (decorationsByMarkerId) ->
|
||||
{editor} = @props
|
||||
filteredDecorations = {}
|
||||
for markerId, decorations of decorationsByMarkerId
|
||||
marker = editor.getMarker(markerId)
|
||||
if marker.isValid() and not marker.getScreenRange().isEmpty()
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'highlight')
|
||||
filteredDecorations[markerId] ?= {id: markerId, screenRange: marker.getScreenRange(), decorations: []}
|
||||
filteredDecorations[markerId].decorations.push decoration
|
||||
|
||||
else if index is 0 # Rendering artifacts occur on the lines GPU layer if we remove the last selection
|
||||
selectionScreenRanges[selection.id] = new Range(new Point(renderedStartRow, 0), new Point(renderedStartRow, 0))
|
||||
# At least in Chromium 31, removing the last highlight causes a rendering
|
||||
# artifact where chunks of the lines disappear, so we always leave this
|
||||
# dummy highlight in place to prevent that.
|
||||
filteredDecorations['dummy'] = DummyHighlightDecoration
|
||||
|
||||
selectionScreenRanges
|
||||
filteredDecorations
|
||||
|
||||
observeEditor: ->
|
||||
{editor} = @props
|
||||
@subscribe editor, 'batched-updates-started', @onBatchedUpdatesStarted
|
||||
@subscribe editor, 'batched-updates-ended', @onBatchedUpdatesEnded
|
||||
@subscribe editor, 'screen-lines-changed', @onScreenLinesChanged
|
||||
@subscribe editor, 'cursors-moved', @onCursorsMoved
|
||||
@subscribe editor, 'selection-removed selection-screen-range-changed', @onSelectionChanged
|
||||
@subscribe editor, 'selection-added', @onSelectionAdded
|
||||
@subscribe editor, 'decoration-added', @onDecorationChanged
|
||||
@subscribe editor, 'decoration-removed', @onDecorationChanged
|
||||
@subscribe editor, 'character-widths-changed', @onCharacterWidthsChanged
|
||||
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
|
||||
@subscribe editor.$scrollLeft.changes, @requestUpdate
|
||||
@subscribe editor.$height.changes, @requestUpdate
|
||||
@@ -244,11 +299,38 @@ EditorComponent = React.createClass
|
||||
node = @getDOMNode()
|
||||
node.addEventListener 'mousewheel', @onMouseWheel
|
||||
node.addEventListener 'focus', @onFocus # For some reason, React's built in focus events seem to bubble
|
||||
node.addEventListener 'textInput', @onTextInput
|
||||
|
||||
scrollViewNode = @refs.scrollView.getDOMNode()
|
||||
scrollViewNode.addEventListener 'overflowchanged', @onScrollViewOverflowChanged
|
||||
scrollViewNode.addEventListener 'scroll', @onScrollViewScroll
|
||||
window.addEventListener('resize', @onWindowResize)
|
||||
window.addEventListener 'resize', @requestScrollViewMeasurement
|
||||
|
||||
@listenForIMEEvents()
|
||||
|
||||
listenForIMEEvents: ->
|
||||
node = @getDOMNode()
|
||||
{editor} = @props
|
||||
|
||||
# The IME composition events work like this:
|
||||
#
|
||||
# User types 's', chromium pops up the completion helper
|
||||
# 1. compositionstart fired
|
||||
# 2. compositionupdate fired; event.data == 's'
|
||||
# User hits arrow keys to move around in completion helper
|
||||
# 3. compositionupdate fired; event.data == 's' for each arry key press
|
||||
# User escape to cancel
|
||||
# 4. compositionend fired
|
||||
# OR User chooses a completion
|
||||
# 4. compositionend fired
|
||||
# 5. textInput fired; event.data == the completion string
|
||||
|
||||
selectedText = null
|
||||
node.addEventListener 'compositionstart', =>
|
||||
selectedText = editor.getSelectedText()
|
||||
node.addEventListener 'compositionupdate', (event) =>
|
||||
editor.insertText(event.data, select: true, undo: 'skip')
|
||||
node.addEventListener 'compositionend', =>
|
||||
editor.insertText(selectedText, select: true, undo: 'skip')
|
||||
|
||||
listenForCommands: ->
|
||||
{parentView, editor, mini} = @props
|
||||
@@ -271,6 +353,7 @@ EditorComponent = React.createClass
|
||||
'editor:consolidate-selections': @consolidateSelections
|
||||
'editor:delete-to-beginning-of-word': => editor.deleteToBeginningOfWord()
|
||||
'editor:delete-to-beginning-of-line': => editor.deleteToBeginningOfLine()
|
||||
'editor:delete-to-end-of-line': => editor.deleteToEndOfLine()
|
||||
'editor:delete-to-end-of-word': => editor.deleteToEndOfWord()
|
||||
'editor:delete-line': => editor.deleteLine()
|
||||
'editor:cut-to-end-of-line': => editor.cutToEndOfLine()
|
||||
@@ -286,6 +369,8 @@ EditorComponent = React.createClass
|
||||
'editor:move-to-beginning-of-next-word': => editor.moveCursorToBeginningOfNextWord()
|
||||
'editor:move-to-previous-word-boundary': => editor.moveCursorToPreviousWordBoundary()
|
||||
'editor:move-to-next-word-boundary': => editor.moveCursorToNextWordBoundary()
|
||||
'editor:select-to-beginning-of-next-paragraph': => editor.selectToBeginningOfNextParagraph()
|
||||
'editor:select-to-beginning-of-previous-paragraph': => editor.selectToBeginningOfPreviousParagraph()
|
||||
'editor:select-to-end-of-line': => editor.selectToEndOfLine()
|
||||
'editor:select-to-beginning-of-line': => editor.selectToBeginningOfLine()
|
||||
'editor:select-to-end-of-word': => editor.selectToEndOfWord()
|
||||
@@ -305,10 +390,14 @@ EditorComponent = React.createClass
|
||||
'core:move-down': => editor.moveCursorDown()
|
||||
'core:move-to-top': => editor.moveCursorToTop()
|
||||
'core:move-to-bottom': => editor.moveCursorToBottom()
|
||||
'core:page-up': => editor.pageUp()
|
||||
'core:page-down': => editor.pageDown()
|
||||
'core:select-up': => editor.selectUp()
|
||||
'core:select-down': => editor.selectDown()
|
||||
'core:select-to-top': => editor.selectToTop()
|
||||
'core:select-to-bottom': => editor.selectToBottom()
|
||||
'core:select-page-up': => editor.selectPageUp()
|
||||
'core:select-page-down': => editor.selectPageDown()
|
||||
'editor:indent': => editor.indent()
|
||||
'editor:auto-indent': => editor.autoIndentSelectedRows()
|
||||
'editor:indent-selected-rows': => editor.indentSelectedRows()
|
||||
@@ -346,8 +435,6 @@ EditorComponent = React.createClass
|
||||
'editor:toggle-indent-guide': => atom.config.toggle('editor.showIndentGuide')
|
||||
'editor:toggle-line-numbers': => atom.config.toggle('editor.showLineNumbers')
|
||||
'editor:scroll-to-cursor': => editor.scrollToCursorPosition()
|
||||
'core:page-up': => editor.pageUp()
|
||||
'core:page-down': => editor.pageDown()
|
||||
'benchmark:scroll': @runScrollBenchmark
|
||||
|
||||
addCommandListeners: (listenersByCommandName) ->
|
||||
@@ -363,10 +450,31 @@ EditorComponent = React.createClass
|
||||
@subscribe atom.config.observe 'editor.showIndentGuide', @setShowIndentGuide
|
||||
@subscribe atom.config.observe 'editor.invisibles', @setInvisibles
|
||||
@subscribe atom.config.observe 'editor.showInvisibles', @setShowInvisibles
|
||||
@subscribe atom.config.observe 'editor.scrollSensitivity', @setScrollSensitivity
|
||||
|
||||
onFocus: ->
|
||||
@refs.input.focus()
|
||||
|
||||
onTextInput: (event) ->
|
||||
return unless @isInputEnabled()
|
||||
|
||||
{editor} = @props
|
||||
inputNode = event.target
|
||||
|
||||
# Work around of the accented character suggestion feature in OS X.
|
||||
# Text input fires before a character is inserted, and if the browser is
|
||||
# replacing the previous un-accented character with an accented variant, it
|
||||
# will select backward over it.
|
||||
selectedLength = inputNode.selectionEnd - inputNode.selectionStart
|
||||
editor.selectLeft() if selectedLength is 1
|
||||
|
||||
editor.insertText(event.data)
|
||||
inputNode.value = event.data
|
||||
|
||||
# If we prevent the insertion of a space character, then the browser
|
||||
# interprets the spacebar keypress as a page-down command.
|
||||
event.preventDefault() unless event.data is ' '
|
||||
|
||||
onInputFocused: ->
|
||||
@setState(focused: true)
|
||||
|
||||
@@ -405,10 +513,10 @@ EditorComponent = React.createClass
|
||||
{wheelDeltaX, wheelDeltaY} = event
|
||||
if Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY)
|
||||
# Scrolling horizontally
|
||||
@pendingHorizontalScrollDelta -= wheelDeltaX
|
||||
@pendingHorizontalScrollDelta -= Math.round(wheelDeltaX * @scrollSensitivity)
|
||||
else
|
||||
# Scrolling vertically
|
||||
@pendingVerticalScrollDelta -= wheelDeltaY
|
||||
@pendingVerticalScrollDelta -= Math.round(wheelDeltaY * @scrollSensitivity)
|
||||
@mouseWheelScreenRow = @screenRowForNode(event.target)
|
||||
@clearMouseWheelScreenRowAfterDelay ?= debounce(@clearMouseWheelScreenRow, @mouseWheelScreenRowClearDelay)
|
||||
@clearMouseWheelScreenRowAfterDelay()
|
||||
@@ -421,36 +529,25 @@ EditorComponent = React.createClass
|
||||
@pendingVerticalScrollDelta = 0
|
||||
@pendingHorizontalScrollDelta = 0
|
||||
|
||||
onScrollViewOverflowChanged: ->
|
||||
if @overflowChangedEventsPaused
|
||||
@overflowChangedWhilePaused = true
|
||||
else
|
||||
@requestScrollViewMeasurement()
|
||||
|
||||
onWindowResize: ->
|
||||
@requestScrollViewMeasurement()
|
||||
|
||||
onScrollViewScroll: ->
|
||||
console.warn "EditorScrollView scroll position changed, and it shouldn't have. If you can reproduce this, please report it."
|
||||
scrollViewNode = @refs.scrollView.getDOMNode()
|
||||
scrollViewNode.scrollTop = 0
|
||||
scrollViewNode.scrollLeft = 0
|
||||
|
||||
onInput: (char, replaceLastCharacter) ->
|
||||
{editor} = @props
|
||||
|
||||
if replaceLastCharacter
|
||||
editor.transact ->
|
||||
editor.selectLeft()
|
||||
editor.insertText(char)
|
||||
else
|
||||
editor.insertText(char)
|
||||
if @isMounted()
|
||||
console.warn "EditorScrollView scrolled when it shouldn't have."
|
||||
scrollViewNode = @refs.scrollView.getDOMNode()
|
||||
scrollViewNode.scrollTop = 0
|
||||
scrollViewNode.scrollLeft = 0
|
||||
|
||||
onMouseDown: (event) ->
|
||||
return unless event.button is 0 # only handle the left mouse button
|
||||
|
||||
{editor} = @props
|
||||
{detail, shiftKey, metaKey} = event
|
||||
screenPosition = @screenPositionForMouseEvent(event)
|
||||
|
||||
if event.target?.classList.contains('fold-marker')
|
||||
bufferRow = editor.bufferRowForScreenRow(screenPosition.row)
|
||||
editor.unfoldBufferRow(bufferRow)
|
||||
return
|
||||
|
||||
if shiftKey
|
||||
editor.selectToScreenPosition(screenPosition)
|
||||
else if metaKey
|
||||
@@ -461,23 +558,50 @@ EditorComponent = React.createClass
|
||||
when 2 then editor.selectWord()
|
||||
when 3 then editor.selectLine()
|
||||
|
||||
@selectToMousePositionUntilMouseUp(event)
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
editor.selectToScreenPosition(screenPosition)
|
||||
|
||||
onGutterMouseDown: (event) ->
|
||||
return unless event.button is 0 # only handle the left mouse button
|
||||
|
||||
if event.shiftKey
|
||||
@onGutterShiftClick(event)
|
||||
else
|
||||
@onGutterClick(event)
|
||||
|
||||
onGutterClick: (event) ->
|
||||
{editor} = @props
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
|
||||
editor.setCursorScreenPosition([clickedRow, 0])
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
dragRow = screenPosition.row
|
||||
if dragRow < clickedRow # dragging up
|
||||
editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]])
|
||||
else
|
||||
editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]])
|
||||
|
||||
onGutterShiftClick: (event) ->
|
||||
{editor} = @props
|
||||
clickedRow = @screenPositionForMouseEvent(event).row
|
||||
tailPosition = editor.getSelection().getTailScreenPosition()
|
||||
|
||||
if clickedRow < tailPosition.row
|
||||
editor.selectToScreenPosition([clickedRow, 0])
|
||||
else
|
||||
editor.selectToScreenPosition([clickedRow + 1, 0])
|
||||
|
||||
@handleDragUntilMouseUp event, (screenPosition) ->
|
||||
dragRow = screenPosition.row
|
||||
if dragRow < tailPosition.row # dragging up
|
||||
editor.setSelectedScreenRange([[dragRow, 0], tailPosition])
|
||||
else
|
||||
editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]])
|
||||
|
||||
onStylesheetsChanged: (stylesheet) ->
|
||||
@refreshScrollbars() if @containsScrollbarSelector(stylesheet)
|
||||
|
||||
onBatchedUpdatesStarted: ->
|
||||
@batchingUpdates = true
|
||||
|
||||
onBatchedUpdatesEnded: ->
|
||||
updateRequested = @updateRequested
|
||||
@updateRequested = false
|
||||
@batchingUpdates = false
|
||||
if updateRequested
|
||||
@requestUpdate()
|
||||
|
||||
onScreenLinesChanged: (change) ->
|
||||
{editor} = @props
|
||||
@pendingChanges.push(change)
|
||||
@@ -503,6 +627,8 @@ EditorComponent = React.createClass
|
||||
@onStoppedScrollingAfterDelay()
|
||||
|
||||
onStoppedScrolling: ->
|
||||
return unless @isMounted()
|
||||
|
||||
@scrollingVertically = false
|
||||
@mouseWheelScreenRow = null
|
||||
@requestUpdate()
|
||||
@@ -511,16 +637,23 @@ EditorComponent = React.createClass
|
||||
|
||||
onCursorsMoved: ->
|
||||
@cursorsMoved = true
|
||||
@requestUpdate()
|
||||
|
||||
selectToMousePositionUntilMouseUp: (event) ->
|
||||
onDecorationChanged: ->
|
||||
@requestUpdate()
|
||||
|
||||
onCharacterWidthsChanged: (@scopedCharacterWidthsChangeCount) ->
|
||||
@requestUpdate()
|
||||
|
||||
handleDragUntilMouseUp: (event, dragHandler) ->
|
||||
{editor} = @props
|
||||
dragging = false
|
||||
lastMousePosition = {}
|
||||
|
||||
animationLoop = =>
|
||||
requestAnimationFrame =>
|
||||
if dragging
|
||||
@selectToMousePosition(lastMousePosition)
|
||||
screenPosition = @screenPositionForMouseEvent(lastMousePosition)
|
||||
dragHandler(screenPosition)
|
||||
animationLoop()
|
||||
|
||||
onMouseMove = (event) ->
|
||||
@@ -544,9 +677,6 @@ EditorComponent = React.createClass
|
||||
window.addEventListener('mousemove', onMouseMove)
|
||||
window.addEventListener('mouseup', onMouseUp)
|
||||
|
||||
selectToMousePosition: (event) ->
|
||||
@props.editor.selectToScreenPosition(@screenPositionForMouseEvent(event))
|
||||
|
||||
requestScrollViewMeasurement: ->
|
||||
return if @measurementPending
|
||||
|
||||
@@ -578,20 +708,10 @@ EditorComponent = React.createClass
|
||||
|
||||
measureLineHeightAndCharWidthsIfNeeded: (prevState) ->
|
||||
if not isEqualForProperties(prevState, @state, 'lineHeight', 'fontSize', 'fontFamily')
|
||||
{editor} = @props
|
||||
|
||||
editor.batchUpdates =>
|
||||
oldDefaultCharWidth = editor.getDefaultCharWidth()
|
||||
|
||||
if @state.visible
|
||||
@measureLineHeightAndDefaultCharWidth()
|
||||
else
|
||||
@measureLineHeightAndDefaultCharWidthWhenShown = true
|
||||
|
||||
unless oldDefaultCharWidth is editor.getDefaultCharWidth()
|
||||
@remeasureCharacterWidths()
|
||||
@measureGutter()
|
||||
|
||||
if @state.visible
|
||||
@measureLineHeightAndDefaultCharWidth()
|
||||
else
|
||||
@measureLineHeightAndDefaultCharWidthWhenShown = true
|
||||
else if @measureLineHeightAndDefaultCharWidthWhenShown and @state.visible and not prevState.visible
|
||||
@measureLineHeightAndDefaultCharWidth()
|
||||
|
||||
@@ -599,13 +719,21 @@ EditorComponent = React.createClass
|
||||
@measureLineHeightAndDefaultCharWidthWhenShown = false
|
||||
@refs.lines.measureLineHeightAndDefaultCharWidth()
|
||||
|
||||
remeasureCharacterWidthsIfNeeded: (prevState) ->
|
||||
if not isEqualForProperties(prevState, @state, 'fontSize', 'fontFamily')
|
||||
if @state.visible
|
||||
@remeasureCharacterWidths()
|
||||
else
|
||||
@remeasureCharacterWidthsWhenShown = true
|
||||
else if @remeasureCharacterWidthsWhenShown and @state.visible and not prevState.visible
|
||||
@remeasureCharacterWidths()
|
||||
|
||||
remeasureCharacterWidths: ->
|
||||
@remeasureCharacterWidthsWhenShown = false
|
||||
@refs.lines.remeasureCharacterWidths()
|
||||
|
||||
measureGutter: ->
|
||||
oldGutterWidth = @gutterWidth
|
||||
@gutterWidth = @refs.gutter.getDOMNode().offsetWidth
|
||||
@requestUpdate() if @gutterWidth isnt oldGutterWidth
|
||||
onGutterWidthChanged: (@gutterWidth) ->
|
||||
@requestUpdate()
|
||||
|
||||
measureScrollbars: ->
|
||||
@measuringScrollbars = false
|
||||
@@ -631,29 +759,17 @@ EditorComponent = React.createClass
|
||||
# visible, so first we need to hide scrollbars so we can redisplay them and
|
||||
# force Chromium to apply updates.
|
||||
@refreshingScrollbars = true
|
||||
@requestUpdate()
|
||||
@forceUpdate()
|
||||
|
||||
# Next, we display only the scrollbar corner so we can measure the new
|
||||
# scrollbar dimensions. The ::measuringScrollbars property will be set back
|
||||
# to false after the scrollbars are measured.
|
||||
@measuringScrollbars = true
|
||||
@requestUpdate()
|
||||
@forceUpdate()
|
||||
|
||||
# Finally, we restore the scrollbars based on the newly-measured dimensions
|
||||
# if the editor's content and dimensions require them to be visible.
|
||||
@requestUpdate()
|
||||
|
||||
pauseOverflowChangedEvents: ->
|
||||
@overflowChangedEventsPaused = true
|
||||
@resumeOverflowChangedEventsAfterDelay ?= debounce(@resumeOverflowChangedEvents, 500)
|
||||
@resumeOverflowChangedEventsAfterDelay()
|
||||
|
||||
resumeOverflowChangedEvents: ->
|
||||
if @overflowChangedWhilePaused
|
||||
@overflowChangedWhilePaused = false
|
||||
@requestScrollViewMeasurement()
|
||||
|
||||
resumeOverflowChangedEventsAfterDelay: null
|
||||
@forceUpdate()
|
||||
|
||||
clearMouseWheelScreenRow: ->
|
||||
if @mouseWheelScreenRow?
|
||||
@@ -682,6 +798,71 @@ EditorComponent = React.createClass
|
||||
show: ->
|
||||
@setState(visible: true)
|
||||
|
||||
getFontSize: ->
|
||||
@state.fontSize
|
||||
|
||||
setFontSize: (fontSize) ->
|
||||
@setState({fontSize})
|
||||
|
||||
getFontFamily: ->
|
||||
@state.fontFamily
|
||||
|
||||
setFontFamily: (fontFamily) ->
|
||||
@setState({fontFamily})
|
||||
|
||||
setLineHeight: (lineHeight) ->
|
||||
@setState({lineHeight})
|
||||
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
@setState({showIndentGuide})
|
||||
|
||||
# Public: Defines which characters are invisible.
|
||||
#
|
||||
# invisibles - An {Object} defining the invisible characters:
|
||||
# :eol - The end of line invisible {String} (default: `\u00ac`).
|
||||
# :space - The space invisible {String} (default: `\u00b7`).
|
||||
# :tab - The tab invisible {String} (default: `\u00bb`).
|
||||
# :cr - The carriage return invisible {String} (default: `\u00a4`).
|
||||
setInvisibles: (invisibles={}) ->
|
||||
defaults invisibles,
|
||||
eol: '\u00ac'
|
||||
space: '\u00b7'
|
||||
tab: '\u00bb'
|
||||
cr: '\u00a4'
|
||||
|
||||
@setState({invisibles})
|
||||
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
@setState({showInvisibles})
|
||||
|
||||
setScrollSensitivity: (scrollSensitivity) ->
|
||||
if scrollSensitivity = parseInt(scrollSensitivity)
|
||||
@scrollSensitivity = Math.abs(scrollSensitivity) / 100
|
||||
|
||||
screenPositionForMouseEvent: (event) ->
|
||||
pixelPosition = @pixelPositionForMouseEvent(event)
|
||||
@props.editor.screenPositionForPixelPosition(pixelPosition)
|
||||
|
||||
pixelPositionForMouseEvent: (event) ->
|
||||
{editor} = @props
|
||||
{clientX, clientY} = event
|
||||
|
||||
scrollViewClientRect = @refs.scrollView.getDOMNode().getBoundingClientRect()
|
||||
top = clientY - scrollViewClientRect.top + editor.getScrollTop()
|
||||
left = clientX - scrollViewClientRect.left + editor.getScrollLeft()
|
||||
{top, left}
|
||||
|
||||
getModel: ->
|
||||
@props.editor
|
||||
|
||||
isInputEnabled: -> @inputEnabled
|
||||
|
||||
setInputEnabled: (@inputEnabled) -> @inputEnabled
|
||||
|
||||
updateParentViewFocusedClassIfNeeded: (prevState) ->
|
||||
if prevState.focused isnt @state.focused
|
||||
@props.parentView.toggleClass('is-focused', @props.focused)
|
||||
|
||||
runScrollBenchmark: ->
|
||||
unless process.env.NODE_ENV is 'production'
|
||||
ReactPerf = require 'react-atom-fork/lib/ReactDefaultPerf'
|
||||
@@ -716,47 +897,3 @@ EditorComponent = React.createClass
|
||||
ReactPerf.printExclusive()
|
||||
console.log "Wasted"
|
||||
ReactPerf.printWasted()
|
||||
|
||||
setFontSize: (fontSize) ->
|
||||
@setState({fontSize})
|
||||
|
||||
setLineHeight: (lineHeight) ->
|
||||
@setState({lineHeight})
|
||||
|
||||
setFontFamily: (fontFamily) ->
|
||||
@setState({fontFamily})
|
||||
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
@setState({showIndentGuide})
|
||||
|
||||
# Public: Defines which characters are invisible.
|
||||
#
|
||||
# invisibles - An {Object} defining the invisible characters:
|
||||
# :eol - The end of line invisible {String} (default: `\u00ac`).
|
||||
# :space - The space invisible {String} (default: `\u00b7`).
|
||||
# :tab - The tab invisible {String} (default: `\u00bb`).
|
||||
# :cr - The carriage return invisible {String} (default: `\u00a4`).
|
||||
setInvisibles: (invisibles={}) ->
|
||||
defaults invisibles,
|
||||
eol: '\u00ac'
|
||||
space: '\u00b7'
|
||||
tab: '\u00bb'
|
||||
cr: '\u00a4'
|
||||
|
||||
@setState({invisibles})
|
||||
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
@setState({showInvisibles})
|
||||
|
||||
screenPositionForMouseEvent: (event) ->
|
||||
pixelPosition = @pixelPositionForMouseEvent(event)
|
||||
@props.editor.screenPositionForPixelPosition(pixelPosition)
|
||||
|
||||
pixelPositionForMouseEvent: (event) ->
|
||||
{editor} = @props
|
||||
{clientX, clientY} = event
|
||||
|
||||
scrollViewClientRect = @refs.scrollView.getDOMNode().getBoundingClientRect()
|
||||
top = clientY - scrollViewClientRect.top + editor.getScrollTop()
|
||||
left = clientX - scrollViewClientRect.left + editor.getScrollLeft()
|
||||
{top, left}
|
||||
|
||||
@@ -56,6 +56,7 @@ class EditorView extends View
|
||||
softWrap: false
|
||||
softTabs: true
|
||||
softWrapAtPreferredLineLength: false
|
||||
scrollSensitivity: 40
|
||||
|
||||
@nextEditorId: 1
|
||||
|
||||
@@ -156,6 +157,7 @@ class EditorView extends View
|
||||
'editor:consolidate-selections': (event) => @consolidateSelections(event)
|
||||
'editor:delete-to-beginning-of-word': => @editor.deleteToBeginningOfWord()
|
||||
'editor:delete-to-beginning-of-line': => @editor.deleteToBeginningOfLine()
|
||||
'editor:delete-to-end-of-line': => @editor.deleteToEndOfLine()
|
||||
'editor:delete-to-end-of-word': => @editor.deleteToEndOfWord()
|
||||
'editor:delete-line': => @editor.deleteLine()
|
||||
'editor:cut-to-end-of-line': => @editor.cutToEndOfLine()
|
||||
@@ -171,6 +173,8 @@ class EditorView extends View
|
||||
'editor:move-to-beginning-of-next-word': => @editor.moveCursorToBeginningOfNextWord()
|
||||
'editor:move-to-previous-word-boundary': => @editor.moveCursorToPreviousWordBoundary()
|
||||
'editor:move-to-next-word-boundary': => @editor.moveCursorToNextWordBoundary()
|
||||
'editor:select-to-beginning-of-next-paragraph': => @editor.selectToBeginningOfNextParagraph()
|
||||
'editor:select-to-beginning-of-previous-paragraph': => @editor.selectToBeginningOfPreviousParagraph()
|
||||
'editor:select-to-end-of-line': => @editor.selectToEndOfLine()
|
||||
'editor:select-to-beginning-of-line': => @editor.selectToBeginningOfLine()
|
||||
'editor:select-to-end-of-word': => @editor.selectToEndOfWord()
|
||||
@@ -190,12 +194,14 @@ class EditorView extends View
|
||||
'core:move-down': => @editor.moveCursorDown()
|
||||
'core:move-to-top': => @editor.moveCursorToTop()
|
||||
'core:move-to-bottom': => @editor.moveCursorToBottom()
|
||||
'core:page-down': => @pageDown()
|
||||
'core:page-up': => @pageUp()
|
||||
'core:page-down': => @pageDown()
|
||||
'core:select-up': => @editor.selectUp()
|
||||
'core:select-down': => @editor.selectDown()
|
||||
'core:select-to-top': => @editor.selectToTop()
|
||||
'core:select-to-bottom': => @editor.selectToBottom()
|
||||
'core:select-page-up': => @editor.selectUp(@getPageRows())
|
||||
'core:select-page-down': => @editor.selectDown(@getPageRows())
|
||||
'editor:indent': => @editor.indent()
|
||||
'editor:auto-indent': => @editor.autoIndentSelectedRows()
|
||||
'editor:indent-selected-rows': => @editor.indentSelectedRows()
|
||||
@@ -280,7 +286,7 @@ class EditorView extends View
|
||||
@editor.moveCursorUp(@getPageRows())
|
||||
@scrollTop(newScrollTop, adjustVerticalScrollbar: true)
|
||||
|
||||
# Public: Gets the number of actual page rows existing in an editor.
|
||||
# Gets the number of actual page rows existing in an editor.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getPageRows: ->
|
||||
@@ -365,6 +371,8 @@ class EditorView extends View
|
||||
false if @isFocused
|
||||
|
||||
@overlayer.on 'mousedown', (e) =>
|
||||
return unless e.which is 1 # only handle the left mouse button
|
||||
|
||||
@overlayer.hide()
|
||||
clickedElement = document.elementFromPoint(e.pageX, e.pageY)
|
||||
@overlayer.show()
|
||||
@@ -564,7 +572,7 @@ class EditorView extends View
|
||||
@scrollTop(scrollTop)
|
||||
|
||||
@subscribe @editor, 'scroll-left-changed', (scrollLeft) =>
|
||||
@scrollLeft(scrollLeft)
|
||||
@scrollView.scrollLeft(scrollLeft)
|
||||
|
||||
@subscribe @editor, 'soft-wrap-changed', (softWrap) =>
|
||||
@setSoftWrap(softWrap)
|
||||
|
||||
+118
-22
@@ -112,6 +112,7 @@ TextMateScopeSelector = require('first-mate').ScopeSelector
|
||||
# - {::deleteToBeginningOfWord}
|
||||
# - {::deleteToBeginningOfLine}
|
||||
# - {::delete}
|
||||
# - {::deleteToEndOfLine}
|
||||
# - {::deleteToEndOfWord}
|
||||
# - {::deleteLine}
|
||||
# - {::cutSelectedText}
|
||||
@@ -214,6 +215,9 @@ class Editor extends Model
|
||||
@subscribe @displayBuffer, 'grammar-changed', => @handleGrammarChange()
|
||||
@subscribe @displayBuffer, 'tokenized', => @handleTokenization()
|
||||
@subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args...
|
||||
@subscribe @displayBuffer, "decoration-added", (args...) => @emit 'decoration-added', args...
|
||||
@subscribe @displayBuffer, "decoration-removed", (args...) => @emit 'decoration-removed', args...
|
||||
@subscribe @displayBuffer, "character-widths-changed", (changeCount) => @emit 'character-widths-changed', changeCount
|
||||
|
||||
getViewClass: ->
|
||||
if atom.config.get('core.useReactEditor')
|
||||
@@ -688,6 +692,13 @@ class Editor extends Model
|
||||
delete: ->
|
||||
@mutateSelectedText (selection) -> selection.delete()
|
||||
|
||||
# Public: For each selection, if the selection is not empty, deletes the
|
||||
# selection; otherwise, deletes all characters of the containing line
|
||||
# following the cursor. If the cursor is already at the end of the line,
|
||||
# deletes the following newline.
|
||||
deleteToEndOfLine: ->
|
||||
@mutateSelectedText (selection) -> selection.deleteToEndOfLine()
|
||||
|
||||
# Public: For each selection, if the selection is empty, delete all characters
|
||||
# of the containing word following the cursor. Otherwise delete the selected
|
||||
# text.
|
||||
@@ -842,6 +853,10 @@ class Editor extends Model
|
||||
isFoldableAtBufferRow: (bufferRow) ->
|
||||
@languageMode.isFoldableAtBufferRow(bufferRow)
|
||||
|
||||
isFoldableAtScreenRow: (screenRow) ->
|
||||
bufferRow = @displayBuffer.bufferRowForScreenRow(screenRow)
|
||||
@isFoldableAtBufferRow(bufferRow)
|
||||
|
||||
# TODO: Rename to foldRowRange?
|
||||
createFold: (startRow, endRow) ->
|
||||
@displayBuffer.createFold(startRow, endRow)
|
||||
@@ -1057,6 +1072,66 @@ class Editor extends Model
|
||||
selection.insertText(fn(text))
|
||||
selection.setBufferRange(range)
|
||||
|
||||
# Public: Get all the decorations within a screen row range.
|
||||
#
|
||||
# startScreenRow - the {Number} beginning screen row
|
||||
# endScreenRow - the {Number} end screen row (inclusive)
|
||||
#
|
||||
# Returns an {Object} of decorations in the form `{1: [{type: 'gutter', class: 'someclass'}], 2: ...}`
|
||||
# where the keys are markerIds, and the values are an array of decoration objects attached to the marker.
|
||||
# Returns an empty object when no decorations are found
|
||||
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
@displayBuffer.decorationsForScreenRowRange(startScreenRow, endScreenRow)
|
||||
|
||||
# Public: Adds a decoration that tracks a {Marker}. When the marker moves,
|
||||
# is invalidated, or is destroyed, the decoration will be updated to reflect the marker's state.
|
||||
#
|
||||
# There are a few supported decoration types:
|
||||
# * `gutter`: `{type: 'gutter', class: 'linter-error'}` Will add a class to the gutter rows associated with the marker.
|
||||
# * `line`: `{type: 'line', class: 'linter-error'}` Will add a class to the editor lines associated with the marker.
|
||||
# * `highlight`: `{type: 'highlight', class: 'linter-error'}` Will highlight the region of the buffer associated with the marker. Your specified class will be added to the highlight.
|
||||
#
|
||||
# marker - the {Marker} you want this decoration to follow
|
||||
# decoration - the {Object} decoration eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# Returns nothing
|
||||
addDecorationForMarker: (marker, decoration) ->
|
||||
@displayBuffer.addDecorationForMarker(marker, decoration)
|
||||
|
||||
# Public: Removes all decorations associated with a {Marker} that match a
|
||||
# `decorationPattern` and stop tracking the {Marker}.
|
||||
#
|
||||
# ```coffee
|
||||
# marker = editor.markBufferRange([[4, 13], [5, 17]])
|
||||
# editor.removeDecorationForMarker(marker, {type: 'gutter', class: 'linter-error'})
|
||||
# ```
|
||||
#
|
||||
# All decorations matching a pattern will be removed. For example, you might
|
||||
# have decorations with a namespace like this attached to a row:
|
||||
#
|
||||
# ```coffee
|
||||
# [
|
||||
# {type: 'gutter', namespace: 'myns', class: 'something'},
|
||||
# {type: 'gutter', namespace: 'myns', class: 'something-else'}
|
||||
# ]
|
||||
# ```
|
||||
#
|
||||
# You can remove both with:
|
||||
#
|
||||
# ```coffee
|
||||
# editor.removeDecorationForMarker(marker, {namespace: 'myns'})
|
||||
# ```
|
||||
#
|
||||
# marker - the {Marker} to detach from
|
||||
# decorationPattern - the {Object} decoration type to filter by eg. `{type: 'gutter', class: 'linter-error'}`
|
||||
#
|
||||
# Returns nothing
|
||||
removeDecorationForMarker: (marker, decorationPattern) ->
|
||||
@displayBuffer.removeDecorationForMarker(marker, decorationPattern)
|
||||
|
||||
decorationMatchesType: (decoration, type) ->
|
||||
@displayBuffer.decorationMatchesType(decoration, type)
|
||||
|
||||
# Public: Get the {DisplayBufferMarker} for the given marker id.
|
||||
getMarker: (id) ->
|
||||
@displayBuffer.getMarker(id)
|
||||
@@ -1162,6 +1237,7 @@ class Editor extends Model
|
||||
addCursor: (marker) ->
|
||||
cursor = new Cursor(editor: this, marker: marker)
|
||||
@cursors.push(cursor)
|
||||
@addDecorationForMarker(marker, type: ['gutter', 'line'], class: 'cursor-line')
|
||||
@emit 'cursor-added', cursor
|
||||
cursor
|
||||
|
||||
@@ -1188,6 +1264,7 @@ class Editor extends Model
|
||||
if selection.intersectsBufferRange(selectionBufferRange)
|
||||
return selection
|
||||
else
|
||||
@addDecorationForMarker(marker, type: 'highlight', class: 'selection')
|
||||
@emit 'selection-added', selection
|
||||
selection
|
||||
|
||||
@@ -1201,7 +1278,9 @@ class Editor extends Model
|
||||
# Returns the added {Selection}.
|
||||
addSelectionForBufferRange: (bufferRange, options={}) ->
|
||||
@markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options))
|
||||
@getLastSelection()
|
||||
selection = @getLastSelection()
|
||||
selection.autoscroll()
|
||||
selection
|
||||
|
||||
# Public: Set the selected range in buffer coordinates. If there are multiple
|
||||
# selections, they are reduced to a single selection with the given range.
|
||||
@@ -1503,18 +1582,31 @@ class Editor extends Model
|
||||
@getCursor().autoscroll()
|
||||
|
||||
pageUp: ->
|
||||
@setScrollTop(@getScrollTop() - @getHeight())
|
||||
newScrollTop = @getScrollTop() - @getHeight()
|
||||
@moveCursorUp(@getRowsPerPage())
|
||||
@setScrollTop(newScrollTop)
|
||||
|
||||
pageDown: ->
|
||||
@setScrollTop(@getScrollTop() + @getHeight())
|
||||
newScrollTop = @getScrollTop() + @getHeight()
|
||||
@moveCursorDown(@getRowsPerPage())
|
||||
@setScrollTop(newScrollTop)
|
||||
|
||||
selectPageUp: ->
|
||||
@selectUp(@getRowsPerPage())
|
||||
|
||||
selectPageDown: ->
|
||||
@selectDown(@getRowsPerPage())
|
||||
|
||||
# Returns the number of rows per page
|
||||
getRowsPerPage: ->
|
||||
Math.max(1, Math.ceil(@getHeight() / @getLineHeightInPixels()))
|
||||
|
||||
moveCursors: (fn) ->
|
||||
@movingCursors = true
|
||||
@batchUpdates =>
|
||||
fn(cursor) for cursor in @getCursors()
|
||||
@mergeCursors()
|
||||
@movingCursors = false
|
||||
@emit 'cursors-moved'
|
||||
fn(cursor) for cursor in @getCursors()
|
||||
@mergeCursors()
|
||||
@movingCursors = false
|
||||
@emit 'cursors-moved'
|
||||
|
||||
cursorMoved: (event) ->
|
||||
@emit 'cursor-moved', event
|
||||
@@ -1727,6 +1819,20 @@ class Editor extends Model
|
||||
selectWord: ->
|
||||
@expandSelectionsForward (selection) => selection.selectWord()
|
||||
|
||||
# Public: Expand selections to the beginning of the next paragraph.
|
||||
#
|
||||
# Operates on all selections. Moves the cursor to the beginning of the next
|
||||
# paragraph while preserving the selection's tail position.
|
||||
selectToBeginningOfNextParagraph: ->
|
||||
@expandSelectionsForward (selection) => selection.selectToBeginningOfNextParagraph()
|
||||
|
||||
# Public: Expand selections to the beginning of the next paragraph.
|
||||
#
|
||||
# Operates on all selections. Moves the cursor to the beginning of the next
|
||||
# paragraph while preserving the selection's tail position.
|
||||
selectToBeginningOfPreviousParagraph: ->
|
||||
@expandSelectionsBackward (selection) => selection.selectToBeginningOfPreviousParagraph()
|
||||
|
||||
# Public: Select the range of the given marker if it is valid.
|
||||
#
|
||||
# marker - A {DisplayBufferMarker}
|
||||
@@ -1820,9 +1926,7 @@ class Editor extends Model
|
||||
# execution and revert any changes performed up to the abortion.
|
||||
#
|
||||
# fn - A {Function} to call inside the transaction.
|
||||
transact: (fn) ->
|
||||
@batchUpdates =>
|
||||
@buffer.transact(fn)
|
||||
transact: (fn) -> @buffer.transact(fn)
|
||||
|
||||
# Public: Start an open-ended transaction.
|
||||
#
|
||||
@@ -1842,14 +1946,6 @@ class Editor extends Model
|
||||
# within the transaction.
|
||||
abortTransaction: -> @buffer.abortTransaction()
|
||||
|
||||
batchUpdates: (fn) ->
|
||||
@emit 'batched-updates-started' if @updateBatchDepth is 0
|
||||
@updateBatchDepth++
|
||||
result = fn()
|
||||
@updateBatchDepth--
|
||||
@emit 'batched-updates-ended' if @updateBatchDepth is 0
|
||||
result
|
||||
|
||||
inspect: ->
|
||||
"<Editor #{@id}>"
|
||||
|
||||
@@ -1925,11 +2021,11 @@ class Editor extends Model
|
||||
|
||||
pixelRectForScreenRange: (screenRange) -> @displayBuffer.pixelRectForScreenRange(screenRange)
|
||||
|
||||
scrollToScreenRange: (screenRange) -> @displayBuffer.scrollToScreenRange(screenRange)
|
||||
scrollToScreenRange: (screenRange, options) -> @displayBuffer.scrollToScreenRange(screenRange, options)
|
||||
|
||||
scrollToScreenPosition: (screenPosition) -> @displayBuffer.scrollToScreenPosition(screenPosition)
|
||||
scrollToScreenPosition: (screenPosition, options) -> @displayBuffer.scrollToScreenPosition(screenPosition, options)
|
||||
|
||||
scrollToBufferPosition: (bufferPosition) -> @displayBuffer.scrollToBufferPosition(bufferPosition)
|
||||
scrollToBufferPosition: (bufferPosition, options) -> @displayBuffer.scrollToBufferPosition(bufferPosition, options)
|
||||
|
||||
horizontallyScrollable: -> @displayBuffer.horizontallyScrollable()
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
_ = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
|
||||
@@ -11,19 +12,23 @@ GutterComponent = React.createClass
|
||||
mixins: [SubscriberMixin]
|
||||
|
||||
dummyLineNumberNode: null
|
||||
measuredWidth: null
|
||||
|
||||
render: ->
|
||||
{scrollHeight, scrollTop} = @props
|
||||
{scrollHeight, scrollViewHeight, scrollTop, onMouseDown} = @props
|
||||
|
||||
div className: 'gutter',
|
||||
div className: 'line-numbers', ref: 'lineNumbers', style:
|
||||
height: scrollHeight
|
||||
div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown,
|
||||
# The line-numbers div must have the 'editor-colors' class so it has an
|
||||
# opaque background to avoid sub-pixel anti-aliasing problems on the GPU
|
||||
div className: 'gutter line-numbers editor-colors', ref: 'lineNumbers', style:
|
||||
height: Math.max(scrollHeight, scrollViewHeight)
|
||||
WebkitTransform: "translate3d(0px, #{-scrollTop}px, 0px)"
|
||||
|
||||
componentWillMount: ->
|
||||
@lineNumberNodesById = {}
|
||||
@lineNumberIdsByScreenRow = {}
|
||||
@screenRowsByLineNumberId = {}
|
||||
@renderedDecorationsByLineNumberId = {}
|
||||
|
||||
componentDidMount: ->
|
||||
@appendDummyLineNumber()
|
||||
@@ -33,20 +38,24 @@ GutterComponent = React.createClass
|
||||
# visible row range.
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
return true unless isEqualForProperties(newProps, @props,
|
||||
'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow'
|
||||
'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'mouseWheelScreenRow', 'lineDecorations',
|
||||
'scrollViewHeight'
|
||||
)
|
||||
|
||||
{renderedRowRange, pendingChanges} = newProps
|
||||
{renderedRowRange, pendingChanges, lineDecorations} = newProps
|
||||
for change in pendingChanges when Math.abs(change.screenDelta) > 0 or Math.abs(change.bufferDelta) > 0
|
||||
return true unless change.end <= renderedRowRange.start or renderedRowRange.end <= change.start
|
||||
|
||||
false
|
||||
|
||||
componentDidUpdate: (oldProps) ->
|
||||
unless oldProps.maxLineNumberDigits is @props.maxLineNumberDigits
|
||||
unless isEqualForProperties(oldProps, @props, 'maxLineNumberDigits')
|
||||
@updateDummyLineNumber()
|
||||
@removeLineNumberNodes()
|
||||
|
||||
unless isEqualForProperties(oldProps, @props, 'maxLineNumberDigits', 'defaultCharWidth')
|
||||
@measureWidth()
|
||||
|
||||
@clearScreenRowCaches() unless oldProps.lineHeightInPixels is @props.lineHeightInPixels
|
||||
@updateLineNumbers()
|
||||
|
||||
@@ -58,7 +67,7 @@ GutterComponent = React.createClass
|
||||
# since the real line numbers are absolutely positioned for performance reasons.
|
||||
appendDummyLineNumber: ->
|
||||
{maxLineNumberDigits} = @props
|
||||
WrapperDiv.innerHTML = @buildLineNumberHTML(0, false, maxLineNumberDigits)
|
||||
WrapperDiv.innerHTML = @buildLineNumberHTML(-1, false, maxLineNumberDigits)
|
||||
@dummyLineNumberNode = WrapperDiv.children[0]
|
||||
@refs.lineNumbers.getDOMNode().appendChild(@dummyLineNumberNode)
|
||||
|
||||
@@ -70,7 +79,7 @@ GutterComponent = React.createClass
|
||||
@removeLineNumberNodes(lineNumberIdsToPreserve)
|
||||
|
||||
appendOrUpdateVisibleLineNumberNodes: ->
|
||||
{editor, renderedRowRange, scrollTop, maxLineNumberDigits} = @props
|
||||
{editor, renderedRowRange, scrollTop, maxLineNumberDigits, lineDecorations} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
|
||||
newLineNumberIds = null
|
||||
@@ -91,7 +100,7 @@ GutterComponent = React.createClass
|
||||
visibleLineNumberIds.add(id)
|
||||
|
||||
if @hasLineNumberNode(id)
|
||||
@updateLineNumberNode(id, screenRow)
|
||||
@updateLineNumberNode(id, bufferRow, screenRow, wrapCount > 0)
|
||||
else
|
||||
newLineNumberIds ?= []
|
||||
newLineNumbersHTML ?= ""
|
||||
@@ -100,6 +109,8 @@ GutterComponent = React.createClass
|
||||
@screenRowsByLineNumberId[id] = screenRow
|
||||
@lineNumberIdsByScreenRow[screenRow] = id
|
||||
|
||||
@renderedDecorationsByLineNumberId[id] = lineDecorations[screenRow]
|
||||
|
||||
if newLineNumberIds?
|
||||
WrapperDiv.innerHTML = newLineNumbersHTML
|
||||
newLineNumberNodes = toArray(WrapperDiv.children)
|
||||
@@ -121,17 +132,27 @@ GutterComponent = React.createClass
|
||||
delete @lineNumberNodesById[lineNumberId]
|
||||
delete @lineNumberIdsByScreenRow[screenRow] if @lineNumberIdsByScreenRow[screenRow] is lineNumberId
|
||||
delete @screenRowsByLineNumberId[lineNumberId]
|
||||
delete @renderedDecorationsByLineNumberId[lineNumberId]
|
||||
node.removeChild(lineNumberNode)
|
||||
|
||||
buildLineNumberHTML: (bufferRow, softWrapped, maxLineNumberDigits, screenRow) ->
|
||||
{editor, lineHeightInPixels, lineDecorations} = @props
|
||||
if screenRow?
|
||||
{lineHeightInPixels} = @props
|
||||
style = "position: absolute; top: #{screenRow * lineHeightInPixels}px;"
|
||||
else
|
||||
style = "visibility: hidden;"
|
||||
innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped, maxLineNumberDigits)
|
||||
|
||||
"<div class=\"line-number\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
|
||||
classes = ''
|
||||
if lineDecorations? and decorations = lineDecorations[screenRow]
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'gutter')
|
||||
classes += decoration.class + ' '
|
||||
|
||||
classes += "foldable " if bufferRow >= 0 and editor.isFoldableAtBufferRow(bufferRow)
|
||||
classes += "line-number line-number-#{bufferRow}"
|
||||
|
||||
"<div class=\"#{classes}\" style=\"#{style}\" data-buffer-row=\"#{bufferRow}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
|
||||
|
||||
buildLineNumberInnerHTML: (bufferRow, softWrapped, maxLineNumberDigits) ->
|
||||
if softWrapped
|
||||
@@ -143,11 +164,31 @@ GutterComponent = React.createClass
|
||||
iconHTML = '<div class="icon-right"></div>'
|
||||
padding + lineNumber + iconHTML
|
||||
|
||||
updateLineNumberNode: (lineNumberId, screenRow) ->
|
||||
updateLineNumberNode: (lineNumberId, bufferRow, screenRow, softWrapped) ->
|
||||
{editor, lineDecorations} = @props
|
||||
node = @lineNumberNodesById[lineNumberId]
|
||||
|
||||
if editor.isFoldableAtBufferRow(bufferRow)
|
||||
node.classList.add('foldable')
|
||||
else
|
||||
node.classList.remove('foldable')
|
||||
|
||||
decorations = lineDecorations[screenRow]
|
||||
previousDecorations = @renderedDecorationsByLineNumberId[lineNumberId]
|
||||
|
||||
if previousDecorations?
|
||||
for decoration in previousDecorations
|
||||
node.classList.remove(decoration.class) if editor.decorationMatchesType(decoration, 'gutter') and not _.deepContains(decorations, decoration)
|
||||
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'gutter') and not _.deepContains(previousDecorations, decoration)
|
||||
node.classList.add(decoration.class)
|
||||
|
||||
unless @screenRowsByLineNumberId[lineNumberId] is screenRow
|
||||
{lineHeightInPixels} = @props
|
||||
@lineNumberNodesById[lineNumberId].style.top = screenRow * lineHeightInPixels + 'px'
|
||||
@lineNumberNodesById[lineNumberId].dataset.screenRow = screenRow
|
||||
node.style.top = screenRow * lineHeightInPixels + 'px'
|
||||
node.dataset.screenRow = screenRow
|
||||
@screenRowsByLineNumberId[lineNumberId] = screenRow
|
||||
@lineNumberIdsByScreenRow[screenRow] = lineNumberId
|
||||
|
||||
@@ -156,3 +197,21 @@ GutterComponent = React.createClass
|
||||
|
||||
lineNumberNodeForScreenRow: (screenRow) ->
|
||||
@lineNumberNodesById[@lineNumberIdsByScreenRow[screenRow]]
|
||||
|
||||
onClick: (event) ->
|
||||
{editor} = @props
|
||||
{target} = event
|
||||
lineNumber = target.parentNode
|
||||
|
||||
if target.classList.contains('icon-right') and lineNumber.classList.contains('foldable')
|
||||
bufferRow = parseInt(lineNumber.getAttribute('data-buffer-row'))
|
||||
if lineNumber.classList.contains('folded')
|
||||
editor.unfoldBufferRow(bufferRow)
|
||||
else
|
||||
editor.foldBufferRow(bufferRow)
|
||||
|
||||
measureWidth: ->
|
||||
width = @getDOMNode().offsetWidth
|
||||
unless width is @measuredWidth
|
||||
@measuredWidth = width
|
||||
@props.onWidthChanged?(width)
|
||||
|
||||
@@ -2,17 +2,19 @@ React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
|
||||
module.exports =
|
||||
SelectionComponent = React.createClass
|
||||
displayName: 'SelectionComponent'
|
||||
HighlightComponent = React.createClass
|
||||
displayName: 'HighlightComponent'
|
||||
|
||||
render: ->
|
||||
{editor, screenRange, lineHeightInPixels} = @props
|
||||
{editor, screenRange, decoration} = @props
|
||||
{start, end} = screenRange
|
||||
rowCount = end.row - start.row + 1
|
||||
startPixelPosition = editor.pixelPositionForScreenPosition(start)
|
||||
endPixelPosition = editor.pixelPositionForScreenPosition(end)
|
||||
|
||||
div className: 'selection',
|
||||
className = 'highlight'
|
||||
className += " #{decoration.class}" if decoration.class?
|
||||
div {className},
|
||||
if rowCount is 1
|
||||
@renderSingleLineRegions(startPixelPosition, endPixelPosition)
|
||||
else
|
||||
@@ -0,0 +1,24 @@
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{isEqualForProperties} = require 'underscore-plus'
|
||||
HighlightComponent = require './highlight-component'
|
||||
|
||||
module.exports =
|
||||
HighlightsComponent = React.createClass
|
||||
displayName: 'HighlightsComponent'
|
||||
|
||||
render: ->
|
||||
div className: 'highlights', @renderHighlights()
|
||||
|
||||
renderHighlights: ->
|
||||
{editor, highlightDecorations, lineHeightInPixels} = @props
|
||||
|
||||
highlightComponents = []
|
||||
for markerId, {screenRange, decorations} of highlightDecorations
|
||||
for decoration in decorations
|
||||
highlightComponents.push(HighlightComponent({key: "#{markerId}-#{decoration.class}", screenRange, decoration, editor, lineHeightInPixels}))
|
||||
|
||||
highlightComponents
|
||||
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scopedCharacterWidthsChangeCount')
|
||||
@@ -1,4 +1,3 @@
|
||||
punycode = require 'punycode'
|
||||
{last, isEqual} = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{input} = require 'reactionary-atom-fork'
|
||||
@@ -17,7 +16,6 @@ InputComponent = React.createClass
|
||||
|
||||
componentDidMount: ->
|
||||
@getDOMNode().addEventListener 'paste', @onPaste
|
||||
@getDOMNode().addEventListener 'input', @onInput
|
||||
@getDOMNode().addEventListener 'compositionupdate', @onCompositionUpdate
|
||||
|
||||
# Don't let text accumulate in the input forever, but avoid excessive reflows
|
||||
@@ -36,15 +34,6 @@ InputComponent = React.createClass
|
||||
onPaste: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
onInput: (e) ->
|
||||
e.stopPropagation()
|
||||
valueCharCodes = punycode.ucs2.decode(@getDOMNode().value)
|
||||
valueLength = valueCharCodes.length
|
||||
replaceLastChar = valueLength is @lastValueLength
|
||||
@lastValueLength = valueLength
|
||||
lastChar = String.fromCharCode(last(valueCharCodes))
|
||||
@props.onInput?(lastChar, replaceLastChar)
|
||||
|
||||
onFocus: ->
|
||||
@props.onFocus?()
|
||||
|
||||
|
||||
@@ -41,9 +41,9 @@ class LanguageMode
|
||||
buffer = @editor.buffer
|
||||
commentStartRegexString = _.escapeRegExp(commentStartString).replace(/(\s+)$/, '(?:$1)?')
|
||||
commentStartRegex = new OnigRegExp("^(\\s*)(#{commentStartRegexString})")
|
||||
shouldUncomment = commentStartRegex.test(buffer.lineForRow(start))
|
||||
|
||||
if commentEndString
|
||||
shouldUncomment = commentStartRegex.test(buffer.lineForRow(start))
|
||||
if shouldUncomment
|
||||
commentEndRegexString = _.escapeRegExp(commentEndString).replace(/^(\s+)/, '(?:$1)?')
|
||||
commentEndRegex = new OnigRegExp("(#{commentEndRegexString})(\\s*)$")
|
||||
@@ -64,10 +64,18 @@ class LanguageMode
|
||||
buffer.insert([start, indentLength], commentStartString)
|
||||
buffer.insert([end, buffer.lineLengthForRow(end)], commentEndString)
|
||||
else
|
||||
if shouldUncomment and start isnt end
|
||||
shouldUncomment = [start+1..end].every (row) ->
|
||||
line = buffer.lineForRow(row)
|
||||
not line or commentStartRegex.test(line)
|
||||
allBlank = true
|
||||
allBlankOrCommented = true
|
||||
|
||||
for row in [start..end]
|
||||
line = buffer.lineForRow(row)
|
||||
blank = line?.match(/^\s*$/)
|
||||
|
||||
allBlank = false unless blank
|
||||
allBlankOrCommented = false unless blank or commentStartRegex.test(line)
|
||||
|
||||
shouldUncomment = allBlankOrCommented and not allBlank
|
||||
|
||||
if shouldUncomment
|
||||
for row in [start..end]
|
||||
if match = commentStartRegex.search(buffer.lineForRow(row))
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
_ = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div, span} = require 'reactionary-atom-fork'
|
||||
{debounce, isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
SelectionsComponent = require './selections-component'
|
||||
HighlightsComponent = require './highlights-component'
|
||||
|
||||
DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0]
|
||||
AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT}
|
||||
@@ -15,26 +16,30 @@ LinesComponent = React.createClass
|
||||
|
||||
render: ->
|
||||
if @isMounted()
|
||||
{editor, selectionScreenRanges, scrollTop, scrollLeft, scrollHeight, scrollWidth, lineHeightInPixels, defaultCharWidth, scrollViewHeight} = @props
|
||||
{editor, highlightDecorations, scrollTop, scrollLeft, scrollHeight, scrollWidth} = @props
|
||||
{lineHeightInPixels, defaultCharWidth, scrollViewHeight, scopedCharacterWidthsChangeCount} = @props
|
||||
style =
|
||||
height: Math.max(scrollHeight, scrollViewHeight)
|
||||
width: scrollWidth
|
||||
WebkitTransform: "translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)"
|
||||
|
||||
div {className: 'lines', style},
|
||||
SelectionsComponent({editor, selectionScreenRanges, lineHeightInPixels, defaultCharWidth}) if @isMounted()
|
||||
# The lines div must have the 'editor-colors' class so it has an opaque
|
||||
# background to avoid sub-pixel anti-aliasing problems on the GPU
|
||||
div {className: 'lines editor-colors', style},
|
||||
HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth, scopedCharacterWidthsChangeCount}) if @isMounted()
|
||||
|
||||
componentWillMount: ->
|
||||
@measuredLines = new WeakSet
|
||||
@lineNodesByLineId = {}
|
||||
@screenRowsByLineId = {}
|
||||
@lineIdsByScreenRow = {}
|
||||
@renderedDecorationsByLineId = {}
|
||||
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
return true unless isEqualForProperties(newProps, @props,
|
||||
'renderedRowRange', 'selectionScreenRanges', 'lineHeightInPixels', 'defaultCharWidth',
|
||||
'renderedRowRange', 'lineDecorations', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth',
|
||||
'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible',
|
||||
'scrollViewHeight', 'mouseWheelScreenRow'
|
||||
'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount'
|
||||
)
|
||||
|
||||
{renderedRowRange, pendingChanges} = newProps
|
||||
@@ -57,7 +62,7 @@ LinesComponent = React.createClass
|
||||
@lineIdsByScreenRow = {}
|
||||
|
||||
updateLines: ->
|
||||
{editor, renderedRowRange, showIndentGuide, selectionChanged} = @props
|
||||
{editor, renderedRowRange, showIndentGuide, selectionChanged, lineDecorations} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
|
||||
visibleLines = editor.linesForScreenRows(startRow, endRow - 1)
|
||||
@@ -75,9 +80,12 @@ LinesComponent = React.createClass
|
||||
delete @lineNodesByLineId[lineId]
|
||||
delete @lineIdsByScreenRow[screenRow] if @lineIdsByScreenRow[screenRow] is lineId
|
||||
delete @screenRowsByLineId[lineId]
|
||||
delete @renderedDecorationsByLineId[lineId]
|
||||
node.removeChild(lineNode)
|
||||
|
||||
appendOrUpdateVisibleLineNodes: (visibleLines, startRow) ->
|
||||
{lineDecorations} = @props
|
||||
|
||||
newLines = null
|
||||
newLinesHTML = null
|
||||
|
||||
@@ -94,6 +102,8 @@ LinesComponent = React.createClass
|
||||
@screenRowsByLineId[line.id] = screenRow
|
||||
@lineIdsByScreenRow[screenRow] = line.id
|
||||
|
||||
@renderedDecorationsByLineId[line.id] = lineDecorations[screenRow]
|
||||
|
||||
return unless newLines?
|
||||
|
||||
WrapperDiv.innerHTML = newLinesHTML
|
||||
@@ -108,17 +118,25 @@ LinesComponent = React.createClass
|
||||
@lineNodesByLineId.hasOwnProperty(lineId)
|
||||
|
||||
buildLineHTML: (line, screenRow) ->
|
||||
{editor, mini, showIndentGuide, lineHeightInPixels} = @props
|
||||
{editor, mini, showIndentGuide, lineHeightInPixels, lineDecorations} = @props
|
||||
{tokens, text, lineEnding, fold, isSoftWrapped, indentLevel} = line
|
||||
|
||||
classes = ''
|
||||
if decorations = lineDecorations[screenRow]
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'line')
|
||||
classes += decoration.class + ' '
|
||||
classes += 'line'
|
||||
|
||||
top = screenRow * lineHeightInPixels
|
||||
lineHTML = "<div class=\"line\" style=\"position: absolute; top: #{top}px;\" data-screen-row=\"#{screenRow}\">"
|
||||
lineHTML = "<div class=\"#{classes}\" style=\"position: absolute; top: #{top}px;\" data-screen-row=\"#{screenRow}\">"
|
||||
|
||||
if text is ""
|
||||
lineHTML += @buildEmptyLineInnerHTML(line)
|
||||
else
|
||||
lineHTML += @buildLineInnerHTML(line)
|
||||
|
||||
lineHTML += '<span class="fold-marker"></span>' if fold
|
||||
lineHTML += "</div>"
|
||||
lineHTML
|
||||
|
||||
@@ -186,9 +204,22 @@ LinesComponent = React.createClass
|
||||
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
|
||||
|
||||
updateLineNode: (line, screenRow) ->
|
||||
{editor, lineHeightInPixels, lineDecorations} = @props
|
||||
lineNode = @lineNodesByLineId[line.id]
|
||||
|
||||
decorations = lineDecorations[screenRow]
|
||||
previousDecorations = @renderedDecorationsByLineId[line.id]
|
||||
|
||||
if previousDecorations?
|
||||
for decoration in previousDecorations
|
||||
lineNode.classList.remove(decoration.class) if editor.decorationMatchesType(decoration, 'line') and not _.deepContains(decorations, decoration)
|
||||
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
if editor.decorationMatchesType(decoration, 'line') and not _.deepContains(previousDecorations, decoration)
|
||||
lineNode.classList.add(decoration.class)
|
||||
|
||||
unless @screenRowsByLineId[line.id] is screenRow
|
||||
{lineHeightInPixels} = @props
|
||||
lineNode = @lineNodesByLineId[line.id]
|
||||
lineNode.style.top = screenRow * lineHeightInPixels + 'px'
|
||||
lineNode.dataset.screenRow = screenRow
|
||||
@screenRowsByLineId[line.id] = screenRow
|
||||
@@ -205,19 +236,19 @@ LinesComponent = React.createClass
|
||||
node.removeChild(DummyLineNode)
|
||||
|
||||
{editor} = @props
|
||||
editor.batchUpdates ->
|
||||
editor.setLineHeightInPixels(lineHeightInPixels)
|
||||
editor.setDefaultCharWidth(charWidth)
|
||||
editor.setLineHeightInPixels(lineHeightInPixels)
|
||||
editor.setDefaultCharWidth(charWidth)
|
||||
|
||||
remeasureCharacterWidths: ->
|
||||
@clearScopedCharWidths()
|
||||
@measureCharactersInNewLines()
|
||||
|
||||
measureCharactersInNewLines: ->
|
||||
{editor} = @props
|
||||
[visibleStartRow, visibleEndRow] = @props.renderedRowRange
|
||||
node = @getDOMNode()
|
||||
|
||||
for tokenizedLine in @props.editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
|
||||
for tokenizedLine in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1)
|
||||
unless @measuredLines.has(tokenizedLine)
|
||||
lineNode = @lineNodesByLineId[tokenizedLine.id]
|
||||
@measureCharactersInLine(tokenizedLine, lineNode)
|
||||
@@ -232,6 +263,8 @@ LinesComponent = React.createClass
|
||||
charWidths = editor.getScopedCharWidths(scopes)
|
||||
|
||||
for char in value
|
||||
continue if char is '\0'
|
||||
|
||||
unless charWidths[char]?
|
||||
unless textNode?
|
||||
rangeForMeasurement ?= document.createRange()
|
||||
|
||||
+5
-2
@@ -212,7 +212,10 @@ class Pane extends Model
|
||||
@destroyItem(item) for item in @getItems() when item isnt @activeItem
|
||||
|
||||
destroy: ->
|
||||
super unless @container?.isAlive() and @container?.getPanes().length is 1
|
||||
if @container?.isAlive() and @container.getPanes().length is 1
|
||||
@destroyItems()
|
||||
else
|
||||
super
|
||||
|
||||
# Called by model superclass.
|
||||
destroyed: ->
|
||||
@@ -331,7 +334,7 @@ class Pane extends Model
|
||||
if @parent.orientation isnt orientation
|
||||
@parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this]}))
|
||||
|
||||
newPane = new @constructor(extend({focused: true}, params))
|
||||
newPane = new @constructor(params)
|
||||
switch side
|
||||
when 'before' then @parent.insertChildBefore(this, newPane)
|
||||
when 'after' then @parent.insertChildAfter(this, newPane)
|
||||
|
||||
+139
-32
@@ -5,9 +5,7 @@ EditorComponent = require './editor-component'
|
||||
|
||||
module.exports =
|
||||
class ReactEditorView extends View
|
||||
# The `overlayer` class is included for backwards compatibility with
|
||||
# context menus. It should be removed in v1.0.0
|
||||
@content: -> @div class: 'editor react overlayer'
|
||||
@content: -> @div class: 'editor react'
|
||||
|
||||
focusOnAttach: false
|
||||
|
||||
@@ -16,10 +14,45 @@ class ReactEditorView extends View
|
||||
|
||||
getEditor: -> @editor
|
||||
|
||||
getModel: -> @editor
|
||||
|
||||
Object.defineProperty @::, 'lineHeight', get: -> @editor.getLineHeightInPixels()
|
||||
Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth()
|
||||
Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0]
|
||||
Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1]
|
||||
Object.defineProperty @::, 'active', get: -> @is(@getPane().activeView)
|
||||
Object.defineProperty @::, 'isFocused', get: -> @component?.state.focused
|
||||
|
||||
afterAttach: (onDom) ->
|
||||
return unless onDom
|
||||
return if @attached
|
||||
|
||||
@attached = true
|
||||
props = defaults({@editor, parentView: this}, @props)
|
||||
@component = React.renderComponent(EditorComponent(props), @element)
|
||||
|
||||
node = @component.getDOMNode()
|
||||
|
||||
@scrollView = $(node).find('.scroll-view')
|
||||
@underlayer = $(node).find('.highlights').addClass('underlayer')
|
||||
@overlayer = $(node).find('.lines').addClass('overlayer')
|
||||
@hiddenInput = $(node).find('.hidden-input')
|
||||
|
||||
@gutter = $(node).find('.gutter')
|
||||
@gutter.removeClassFromAllLines = (klass) =>
|
||||
@gutter.find('.line-number').removeClass(klass)
|
||||
|
||||
@gutter.getLineNumberElement = (bufferRow) =>
|
||||
@gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
|
||||
@gutter.addClassToLine = (bufferRow, klass) =>
|
||||
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
lines.addClass(klass)
|
||||
lines.length > 0
|
||||
|
||||
@focus() if @focusOnAttach
|
||||
|
||||
@trigger 'editor:attached', [this]
|
||||
|
||||
scrollTop: (scrollTop) ->
|
||||
if scrollTop?
|
||||
@@ -33,35 +66,22 @@ class ReactEditorView extends View
|
||||
else
|
||||
@editor.getScrollLeft()
|
||||
|
||||
scrollToScreenPosition: (screenPosition) ->
|
||||
scrollToBottom: ->
|
||||
@editor.setScrollBottom(Infinity)
|
||||
|
||||
scrollToScreenPosition: (screenPosition, options) ->
|
||||
@editor.scrollToScreenPosition(screenPosition, options)
|
||||
|
||||
scrollToBufferPosition: (bufferPosition, options) ->
|
||||
@editor.scrollToBufferPosition(bufferPosition, options)
|
||||
|
||||
scrollToCursorPosition: ->
|
||||
@editor.scrollToCursorPosition()
|
||||
|
||||
scrollToPixelPosition: (pixelPosition) ->
|
||||
screenPosition = screenPositionForPixelPosition(pixelPosition)
|
||||
@editor.scrollToScreenPosition(screenPosition)
|
||||
|
||||
scrollToBufferPosition: (bufferPosition) ->
|
||||
@editor.scrollToBufferPosition(bufferPosition)
|
||||
|
||||
afterAttach: (onDom) ->
|
||||
return unless onDom
|
||||
@attached = true
|
||||
props = defaults({@editor, parentView: this}, @props)
|
||||
@component = React.renderComponent(EditorComponent(props), @element)
|
||||
|
||||
node = @component.getDOMNode()
|
||||
|
||||
@underlayer = $(node).find('.selections')
|
||||
|
||||
@gutter = $(node).find('.gutter')
|
||||
@gutter.removeClassFromAllLines = (klass) =>
|
||||
@gutter.find('.line-number').removeClass(klass)
|
||||
|
||||
@gutter.addClassToLine = (bufferRow, klass) =>
|
||||
lines = @gutter.find("[data-buffer-row='#{bufferRow}']")
|
||||
lines.addClass(klass)
|
||||
lines.length > 0
|
||||
|
||||
@focus() if @focusOnAttach
|
||||
|
||||
@trigger 'editor:attached', [this]
|
||||
|
||||
pixelPositionForBufferPosition: (bufferPosition) ->
|
||||
@editor.pixelPositionForBufferPosition(bufferPosition)
|
||||
|
||||
@@ -78,6 +98,26 @@ class ReactEditorView extends View
|
||||
@attached = false
|
||||
@trigger 'editor:detached', this
|
||||
|
||||
# Public: Split the editor view left.
|
||||
splitLeft: ->
|
||||
pane = @getPane()
|
||||
pane?.splitLeft(pane?.copyActiveItem()).activeView
|
||||
|
||||
# Public: Split the editor view right.
|
||||
splitRight: ->
|
||||
pane = @getPane()
|
||||
pane?.splitRight(pane?.copyActiveItem()).activeView
|
||||
|
||||
# Public: Split the editor view up.
|
||||
splitUp: ->
|
||||
pane = @getPane()
|
||||
pane?.splitUp(pane?.copyActiveItem()).activeView
|
||||
|
||||
# Public: Split the editor view down.
|
||||
splitDown: ->
|
||||
pane = @getPane()
|
||||
pane?.splitDown(pane?.copyActiveItem()).activeView
|
||||
|
||||
getPane: ->
|
||||
@closest('.pane').view()
|
||||
|
||||
@@ -89,10 +129,77 @@ class ReactEditorView extends View
|
||||
|
||||
hide: ->
|
||||
super
|
||||
@component.hide()
|
||||
@component?.hide()
|
||||
|
||||
show: ->
|
||||
super
|
||||
@component.show()
|
||||
@component?.show()
|
||||
|
||||
pageDown: ->
|
||||
@editor.pageDown()
|
||||
|
||||
pageUp: ->
|
||||
@editor.pageUp()
|
||||
|
||||
getModel: ->
|
||||
@component?.getModel()
|
||||
|
||||
getFirstVisibleScreenRow: ->
|
||||
@editor.getVisibleRowRange()[0]
|
||||
|
||||
getLastVisibleScreenRow: ->
|
||||
@editor.getVisibleRowRange()[1]
|
||||
|
||||
getFontFamily: ->
|
||||
@component?.getFontFamily()
|
||||
|
||||
setFontFamily: (fontFamily)->
|
||||
@component?.setFontFamily(fontFamily)
|
||||
|
||||
getFontSize: ->
|
||||
@component?.getFontSize()
|
||||
|
||||
setFontSize: (fontSize)->
|
||||
@component?.setFontSize(fontSize)
|
||||
|
||||
setWidthInChars: (widthInChars) ->
|
||||
@component.getDOMNode().style.width = (@editor.getDefaultCharWidth() * widthInChars) + 'px'
|
||||
|
||||
setLineHeight: (lineHeight) ->
|
||||
@component.setLineHeight(lineHeight)
|
||||
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
@component.setShowIndentGuide(showIndentGuide)
|
||||
|
||||
setSoftWrap: (softWrap) ->
|
||||
@editor.setSoftWrap(softWrap)
|
||||
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
@component.setShowInvisibles(showInvisibles)
|
||||
|
||||
toggleSoftWrap: ->
|
||||
@editor.toggleSoftWrap()
|
||||
|
||||
toggleSoftTabs: ->
|
||||
@editor.toggleSoftTabs()
|
||||
|
||||
getText: ->
|
||||
@editor.getText()
|
||||
|
||||
setText: (text) ->
|
||||
@editor.setText(text)
|
||||
|
||||
insertText: (text) ->
|
||||
@editor.insertText(text)
|
||||
|
||||
isInputEnabled: ->
|
||||
@component.isInputEnabled()
|
||||
|
||||
setInputEnabled: (inputEnabled) ->
|
||||
@component.setInputEnabled(inputEnabled)
|
||||
|
||||
requestDisplayUpdate: -> # No-op shim for find-and-replace
|
||||
|
||||
updateDisplay: -> # No-op shim for package specs
|
||||
|
||||
redraw: -> # No-op shim
|
||||
|
||||
+34
-2
@@ -91,6 +91,18 @@ class Selection extends Model
|
||||
end = Math.max(start, end - 1) if range.end.column == 0
|
||||
[start, end]
|
||||
|
||||
getTailScreenPosition: ->
|
||||
@marker.getTailScreenPosition()
|
||||
|
||||
getTailBufferPosition: ->
|
||||
@marker.getTailBufferPosition()
|
||||
|
||||
getHeadScreenPosition: ->
|
||||
@marker.getHeadScreenPosition()
|
||||
|
||||
getHeadBufferPosition: ->
|
||||
@marker.getHeadBufferPosition()
|
||||
|
||||
autoscroll: ->
|
||||
@editor.scrollToScreenRange(@getScreenRange())
|
||||
|
||||
@@ -236,6 +248,16 @@ class Selection extends Model
|
||||
selectToNextWordBoundary: ->
|
||||
@modifySelection => @cursor.moveToNextWordBoundary()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the
|
||||
# beginning of the next paragraph.
|
||||
selectToBeginningOfNextParagraph: ->
|
||||
@modifySelection => @cursor.moveToBeginningOfNextParagraph()
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the
|
||||
# beginning of the previous paragraph.
|
||||
selectToBeginningOfPreviousParagraph: ->
|
||||
@modifySelection => @cursor.moveToBeginningOfPreviousParagraph()
|
||||
|
||||
# Public: Moves the selection down one row.
|
||||
addSelectionBelow: ->
|
||||
range = (@getGoalBufferRange() ? @getBufferRange()).copy()
|
||||
@@ -256,7 +278,8 @@ class Selection extends Model
|
||||
|
||||
# FIXME: I have no idea what this does.
|
||||
getGoalBufferRange: ->
|
||||
@marker.getAttributes().goalBufferRange
|
||||
if goalBufferRange = @marker.getAttributes().goalBufferRange
|
||||
Range.fromObject(goalBufferRange)
|
||||
|
||||
# Public: Moves the selection up one row.
|
||||
addSelectionAbove: ->
|
||||
@@ -420,6 +443,15 @@ class Selection extends Model
|
||||
@selectRight()
|
||||
@deleteSelectedText()
|
||||
|
||||
# Public: If the selection is empty, removes all text from the cursor to the
|
||||
# end of the line. If the cursor is already at the end of the line, it
|
||||
# removes the following newline. If the selection isn't empty, only deletes
|
||||
# the contents of the selection.
|
||||
deleteToEndOfLine: ->
|
||||
return @delete() if @isEmpty() and @cursor.isAtEndOfLine()
|
||||
@selectToEndOfLine() if @isEmpty()
|
||||
@deleteSelectedText()
|
||||
|
||||
# Public: Removes the selection or all characters from the start of the
|
||||
# selection to the end of the current word if nothing is selected.
|
||||
deleteToEndOfWord: ->
|
||||
@@ -485,7 +517,7 @@ class Selection extends Model
|
||||
outdentSelectedRows: ->
|
||||
[start, end] = @getBufferRowRange()
|
||||
buffer = @editor.buffer
|
||||
leadingTabRegex = new RegExp("^ {1,#{@editor.getTabLength()}}|\t")
|
||||
leadingTabRegex = new RegExp("^( {1,#{@editor.getTabLength()}}|\t)")
|
||||
for row in [start..end]
|
||||
if matchLength = buffer.lineForRow(row).match(leadingTabRegex)?[0].length
|
||||
buffer.delete [[row, 0], [row, matchLength]]
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{isEqualForProperties} = require 'underscore-plus'
|
||||
SelectionComponent = require './selection-component'
|
||||
|
||||
module.exports =
|
||||
SelectionsComponent = React.createClass
|
||||
displayName: 'SelectionsComponent'
|
||||
|
||||
render: ->
|
||||
div className: 'selections', @renderSelections()
|
||||
|
||||
renderSelections: ->
|
||||
{editor, selectionScreenRanges, lineHeightInPixels} = @props
|
||||
|
||||
selectionComponents = []
|
||||
for selectionId, screenRange of selectionScreenRanges
|
||||
selectionComponents.push(SelectionComponent({key: selectionId, screenRange, editor, lineHeightInPixels}))
|
||||
selectionComponents
|
||||
|
||||
componentWillMount: ->
|
||||
@selectionRanges = {}
|
||||
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
not isEqualForProperties(newProps, @props, 'selectionScreenRanges', 'lineHeightInPixels', 'defaultCharWidth')
|
||||
+18
-11
@@ -42,16 +42,18 @@ class Token
|
||||
whitespaceRegexForTabLength: (tabLength) ->
|
||||
WhitespaceRegexesByTabLength[tabLength] ?= new RegExp("([ ]{#{tabLength}})|(\t)|([^\t]+)", "g")
|
||||
|
||||
breakOutAtomicTokens: (tabLength, breakOutLeadingSoftTabs) ->
|
||||
breakOutAtomicTokens: (tabLength, breakOutLeadingSoftTabs, startColumn) ->
|
||||
if @hasSurrogatePair
|
||||
outputTokens = []
|
||||
column = startColumn
|
||||
|
||||
for token in @breakOutSurrogatePairs()
|
||||
if token.isAtomic
|
||||
outputTokens.push(token)
|
||||
else
|
||||
outputTokens.push(token.breakOutAtomicTokens(tabLength, breakOutLeadingSoftTabs)...)
|
||||
outputTokens.push(token.breakOutAtomicTokens(tabLength, breakOutLeadingSoftTabs, column)...)
|
||||
breakOutLeadingSoftTabs = token.isOnlyWhitespace() if breakOutLeadingSoftTabs
|
||||
column += token.value.length
|
||||
|
||||
outputTokens
|
||||
else
|
||||
@@ -64,17 +66,21 @@ class Token
|
||||
|
||||
outputTokens = []
|
||||
regex = @whitespaceRegexForTabLength(tabLength)
|
||||
column = startColumn
|
||||
while match = regex.exec(@value)
|
||||
[fullMatch, softTab, hardTab] = match
|
||||
token = null
|
||||
if softTab and breakOutLeadingSoftTabs
|
||||
outputTokens.push(@buildSoftTabToken(tabLength))
|
||||
token = @buildSoftTabToken(tabLength)
|
||||
else if hardTab
|
||||
breakOutLeadingSoftTabs = false
|
||||
outputTokens.push(@buildHardTabToken(tabLength))
|
||||
token = @buildHardTabToken(tabLength, column)
|
||||
else
|
||||
breakOutLeadingSoftTabs = false
|
||||
value = match[0]
|
||||
outputTokens.push(new Token({value, @scopes}))
|
||||
token = new Token({value, @scopes})
|
||||
column += token.value.length
|
||||
outputTokens.push(token)
|
||||
|
||||
outputTokens
|
||||
|
||||
@@ -105,17 +111,18 @@ class Token
|
||||
isAtomic: true
|
||||
)
|
||||
|
||||
buildHardTabToken: (tabLength) ->
|
||||
@buildTabToken(tabLength, true)
|
||||
buildHardTabToken: (tabLength, column) ->
|
||||
@buildTabToken(tabLength, true, column)
|
||||
|
||||
buildSoftTabToken: (tabLength) ->
|
||||
@buildTabToken(tabLength, false)
|
||||
@buildTabToken(tabLength, false, 0)
|
||||
|
||||
buildTabToken: (tabLength, isHardTab) ->
|
||||
buildTabToken: (tabLength, isHardTab, column=0) ->
|
||||
tabStop = tabLength - (column % tabLength)
|
||||
new Token(
|
||||
value: _.multiplyString(" ", tabLength)
|
||||
value: _.multiplyString(" ", tabStop)
|
||||
scopes: @scopes
|
||||
bufferDelta: if isHardTab then 1 else tabLength
|
||||
bufferDelta: if isHardTab then 1 else tabStop
|
||||
isAtomic: true
|
||||
isHardTab: isHardTab
|
||||
)
|
||||
|
||||
@@ -5,8 +5,8 @@ idCounter = 1
|
||||
module.exports =
|
||||
class TokenizedLine
|
||||
constructor: ({tokens, @lineEnding, @ruleStack, @startBufferColumn, @fold, @tabLength, @indentLevel}) ->
|
||||
@tokens = @breakOutAtomicTokens(tokens)
|
||||
@startBufferColumn ?= 0
|
||||
@tokens = @breakOutAtomicTokens(tokens)
|
||||
@text = @buildText()
|
||||
@bufferDelta = @buildBufferDelta()
|
||||
|
||||
@@ -124,8 +124,11 @@ class TokenizedLine
|
||||
breakOutAtomicTokens: (inputTokens) ->
|
||||
outputTokens = []
|
||||
breakOutLeadingSoftTabs = true
|
||||
column = @startBufferColumn
|
||||
for token in inputTokens
|
||||
outputTokens.push(token.breakOutAtomicTokens(@tabLength, breakOutLeadingSoftTabs)...)
|
||||
newTokens = token.breakOutAtomicTokens(@tabLength, breakOutLeadingSoftTabs, column)
|
||||
column += newToken.value.length for newToken in newTokens
|
||||
outputTokens.push(newTokens...)
|
||||
breakOutLeadingSoftTabs = token.isOnlyWhitespace() if breakOutLeadingSoftTabs
|
||||
outputTokens
|
||||
|
||||
|
||||
+18
-5
@@ -17,7 +17,8 @@
|
||||
}
|
||||
|
||||
.cursor {
|
||||
z-index: 2;
|
||||
z-index: 4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.editor-contents.is-focused .cursor {
|
||||
@@ -68,11 +69,13 @@
|
||||
.gutter {
|
||||
.line-number {
|
||||
white-space: nowrap;
|
||||
padding: 0 .5em;
|
||||
padding-left: .5em;
|
||||
|
||||
.icon-right {
|
||||
padding: 0;
|
||||
padding-left: .1em;
|
||||
padding: 0 .4em;
|
||||
&:before {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,7 +123,7 @@
|
||||
visibility: hidden;
|
||||
padding-left: .1em;
|
||||
padding-right: .5em;
|
||||
opacity: .7;
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.editor .gutter:hover .line-number.foldable .icon-right {
|
||||
@@ -148,6 +151,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.editor .fold-marker {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.editor .fold-marker:after {
|
||||
.icon(0.8em, inline);
|
||||
content: @ellipsis;
|
||||
@@ -248,6 +255,12 @@
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.editor .highlight {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.editor .highlight .region,
|
||||
.editor .selection .region {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
|
||||
@@ -135,6 +135,10 @@ body {
|
||||
padding: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
.result-message.deprecation-message {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
.stack-trace {
|
||||
font-size: 12px;
|
||||
margin: 5px 0 0 0;
|
||||
@@ -174,4 +178,24 @@ body {
|
||||
// overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.deprecation-toggle {
|
||||
.octicon(fold);
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
color: #999;
|
||||
|
||||
&.folded {
|
||||
.octicon(unfold);
|
||||
}
|
||||
}
|
||||
|
||||
.deprecation-toggle:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
&:hover .deprecation-toggle {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário