mirror of
https://github.com/atom/atom.git
synced 2026-02-14 00:25:08 -05:00
Merge remote-tracking branch 'refs/remotes/origin/master' into wl-update-language-javascript
This commit is contained in:
@@ -50,7 +50,7 @@ To get a sense for the packages that are bundled with Atom, you can go to Settin
|
||||
|
||||
Here's a list of the big ones:
|
||||
|
||||
* [atom/atom](https://github.com/atom/atom) - Atom Core! The core editor component is responsible for basic text editing (e.g. cursors, selections, scrolling), text indentation, wrapping, and folding, text rendering, editor rendering, file system operations (e.g. saving), and installation and auto-updating. You should also use this repository for feedback related to the [core API](https://atom.io/docs/api/latest/Notification) and for large, overarching design proposals.
|
||||
* [atom/atom](https://github.com/atom/atom) - Atom Core! The core editor component is responsible for basic text editing (e.g. cursors, selections, scrolling), text indentation, wrapping, and folding, text rendering, editor rendering, file system operations (e.g. saving), and installation and auto-updating. You should also use this repository for feedback related to the [core API](https://atom.io/docs/api/latest) and for large, overarching design proposals.
|
||||
* [tree-view](https://github.com/atom/tree-view) - file and directory listing on the left of the UI.
|
||||
* [fuzzy-finder](https://github.com/atom/fuzzy-finder) - the quick file opener.
|
||||
* [find-and-replace](https://github.com/atom/find-and-replace) - all search and replace functionality.
|
||||
@@ -82,7 +82,7 @@ Before creating bug reports, please check [this list](#before-submitting-a-bug-r
|
||||
|
||||
#### Before Submitting A Bug Report
|
||||
|
||||
* **Check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging).** You might be able to find the cause of the problem and fix things yourself. Most importantly, check if you can reproduce the problem [in the latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version), if the problem happens when you run Atom in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode), and if you can get the desired behavior by changing [Atom's or packages' config settings](https://atom.io/docs/latest/hacking-atom-debugging#check-atom-and-package-settings).
|
||||
* **Check the [debugging guide](http://flight-manual.atom.io/hacking-atom/sections/debugging/).** You might be able to find the cause of the problem and fix things yourself. Most importantly, check if you can reproduce the problem [in the latest version of Atom](http://flight-manual.atom.io/hacking-atom/sections/debugging/#update-to-the-latest-version), if the problem happens when you run Atom in [safe mode](http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-if-the-problem-shows-up-in-safe-mode), and if you can get the desired behavior by changing [Atom's or packages' config settings](http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-atom-and-package-settings).
|
||||
* **Check the [FAQs on the forum](https://discuss.atom.io/c/faq)** for a list of common questions and problems.
|
||||
* **Determine [which repository the problem should be reported in](#atom-and-packages)**.
|
||||
* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom)** to see if the problem has already been reported. If it has, add a comment to the existing issue instead of opening a new one.
|
||||
@@ -100,13 +100,13 @@ Explain the problem and include additional details to help maintainers reproduce
|
||||
* **Explain which behavior you expected to see instead and why.**
|
||||
* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. If you use the keyboard while following the steps, **record the GIF with the [Keybinding Resolver](https://github.com/atom/keybinding-resolver) shown**. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on OSX and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
|
||||
* **If you're reporting that Atom crashed**, include a crash report with a stack trace from the operating system. On OSX, the crash report will be available in `Console.app` under "Diagnostic and usage information" > "User diagnostic reports". Include the crash report in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines), a [file attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/), or put it in a [gist](https://gist.github.com/) and provide link to that gist.
|
||||
* **If the problem is related to performance**, include a [CPU profile capture and a screenshot](https://atom.io/docs/latest/hacking-atom-debugging#diagnose-performance-problems-with-the-dev-tools-cpu-profiler) with your report.
|
||||
* **If the problem is related to performance**, include a [CPU profile capture and a screenshot](http://flight-manual.atom.io/hacking-atom/sections/debugging/#diagnose-performance-problems-with-the-dev-tools-cpu-profiler) with your report.
|
||||
* **If the Chrome's developer tools pane is shown without you triggering it**, that normally means that an exception was thrown. The Console tab will include an entry for the exception. Expand the exception so that the stack trace is visible, and provide the full exception and stack trace in a [code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines) and as a screenshot.
|
||||
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
|
||||
|
||||
Provide more context by answering these questions:
|
||||
|
||||
* **Can you reproduce the problem in [safe mode](https://atom.io/docs/latest/hacking-atom-debugging#check-if-the-problem-shows-up-in-safe-mode)?**
|
||||
* **Can you reproduce the problem in [safe mode](http://flight-manual.atom.io/hacking-atom/sections/debugging/#diagnose-runtime-performance-problems-with-the-dev-tools-cpu-profiler)?**
|
||||
* **Did the problem start happening recently** (e.g. after updating to a new version of Atom) or was this always a problem?
|
||||
* If the problem started happening recently, **can you reproduce the problem in an older version of Atom?** What's the most recent version in which the problem doesn't happen? You can download older versions of Atom from [the releases page](https://github.com/atom/atom/releases).
|
||||
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
|
||||
@@ -118,7 +118,7 @@ Include details about your configuration and environment:
|
||||
* **What's the name and version of the OS you're using**?
|
||||
* **Are you running Atom in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest?
|
||||
* **Which [packages](#atom-and-packages) do you have installed?** You can get that list by running `apm list --installed`.
|
||||
* **Are you using [local configuration files](https://atom.io/docs/latest/using-atom-basic-customization)** `config.cson`, `keymap.cson`, `snippets.cson`, `styles.less` and `init.coffee` to customize Atom? If so, provide the contents of those files, preferably in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines) or with a link to a [gist](https://gist.github.com/).
|
||||
* **Are you using [local configuration files](http://flight-manual.atom.io/using-atom/sections/basic-customization/)** `config.cson`, `keymap.cson`, `snippets.cson`, `styles.less` and `init.coffee` to customize Atom? If so, provide the contents of those files, preferably in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines) or with a link to a [gist](https://gist.github.com/).
|
||||
* **Are you using Atom with multiple monitors?** If so, can you reproduce the problem when you use a single monitor?
|
||||
* **Which keyboard layout are you using?** Are you using a US layout or some other layout?
|
||||
|
||||
@@ -166,7 +166,7 @@ Before creating enhancement suggestions, please check [this list](#before-submit
|
||||
|
||||
#### Before Submitting An Enhancement Suggestion
|
||||
|
||||
* **Check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging)** for tips — you might discover that the enhancement is already available. Most importantly, check if you're using [the latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version) and if you can get the desired behavior by changing [Atom's or packages' config settings](https://atom.io/docs/latest/hacking-atom-debugging#check-atom-and-package-settings).
|
||||
* **Check the [debugging guide](http://flight-manual.atom.io/hacking-atom/sections/debugging/)** for tips — you might discover that the enhancement is already available. Most importantly, check if you're using [the latest version of Atom](http://flight-manual.atom.io/hacking-atom/sections/debugging/#update-to-the-latest-version) and if you can get the desired behavior by changing [Atom's or packages' config settings](http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-atom-and-package-settings).
|
||||
* **Check if there's already [a package](https://atom.io/packages) which provides that enhancement.**
|
||||
* **Determine [which repository the enhancement should be suggested in](#atom-and-packages).**
|
||||
* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
|
||||
@@ -376,7 +376,7 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and
|
||||
| `windows` | [search][search-atom-repo-label-windows] | [search][search-atom-org-label-windows] | Related to Atom running on Windows. |
|
||||
| `linux` | [search][search-atom-repo-label-linux] | [search][search-atom-org-label-linux] | Related to Atom running on Linux. |
|
||||
| `mac` | [search][search-atom-repo-label-mac] | [search][search-atom-org-label-mac] | Related to Atom running on OSX. |
|
||||
| `documentation` | [search][search-atom-repo-label-documentation] | [search][search-atom-org-label-documentation] | Related to any type of documentation (e.g. [API documentation](https://atom.io/docs/api/latest/Atom) and the [flight manual](https://atom.io/docs/latest/)). |
|
||||
| `documentation` | [search][search-atom-repo-label-documentation] | [search][search-atom-org-label-documentation] | Related to any type of documentation (e.g. [API documentation](https://atom.io/docs/api/latest/) and the [flight manual](http://flight-manual.atom.io/)). |
|
||||
| `performance` | [search][search-atom-repo-label-performance] | [search][search-atom-org-label-performance] | Related to performance. |
|
||||
| `security` | [search][search-atom-repo-label-security] | [search][search-atom-org-label-security] | Related to security. |
|
||||
| `ui` | [search][search-atom-repo-label-ui] | [search][search-atom-org-label-ui] | Related to visual design. |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
* [ ] Can you reproduce the problem in [safe mode](http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-if-the-problem-shows-up-in-safe-mode)?
|
||||
* [ ] Are you running the [latest version of Atom](http://flight-manual.atom.io/hacking-atom/sections/debugging/#update-to-the-latest-version)?
|
||||
* [ ] Did you check the [debugging guide](flight-manual.atom.io/hacking-atom/sections/debugging/)?
|
||||
* [ ] Did you check the [debugging guide](http://flight-manual.atom.io/hacking-atom/sections/debugging/)?
|
||||
* [ ] Did you check the [FAQs on Discuss](https://discuss.atom.io/c/faq)?
|
||||
* [ ] Are you reporting to the [correct repository](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages)?
|
||||
* [ ] Did you [perform a cursory search](https://github.com/issues?q=is%3Aissue+user%3Aatom+-repo%3Aatom%2Felectron) to see if your bug or enhancement is already reported?
|
||||
@@ -25,4 +25,4 @@ For more information on how to write a good [bug report](https://github.com/atom
|
||||
|
||||
### Versions
|
||||
|
||||
You can get this information from executing `atom --version` and `apm --version` at the command line.
|
||||
You can get this information from executing `atom --version` and `apm --version` at the command line. Also, please include the OS and what version of the OS you're running.
|
||||
|
||||
16
README.md
16
README.md
@@ -76,6 +76,22 @@ Currently only a 64-bit version is available.
|
||||
The Linux version does not currently automatically update so you will need to
|
||||
repeat these steps to upgrade to future releases.
|
||||
|
||||
### Archive extraction
|
||||
|
||||
An archive is available for people who don't want to install `atom` as root.
|
||||
|
||||
This version enables you to install multiple Atom versions in parallel. It has been built on Ubuntu 64-bit,
|
||||
but should be compatible with other Linux distributions.
|
||||
|
||||
1. Install dependencies (on Ubuntu): `sudo apt install git gconf2 gconf-service libgtk2.0-0 libudev1 libgcrypt20
|
||||
libnotify4 libxtst6 libnss3 python gvfs-bin xdg-utils libcap2`
|
||||
2. Download `atom-amd64.tar.gz` from the [Atom releases page](https://github.com/atom/atom/releases/latest).
|
||||
3. Run `tar xf atom-amd64.tar.gz` in the directory where you want to extract the Atom folder.
|
||||
4. Launch Atom using the installed `atom` command from the newly extracted directory.
|
||||
|
||||
The Linux version does not currently automatically update so you will need to
|
||||
repeat these steps to upgrade to future releases.
|
||||
|
||||
## Building
|
||||
|
||||
* [Linux](docs/build-instructions/linux.md)
|
||||
|
||||
@@ -285,6 +285,7 @@ module.exports = (grunt) ->
|
||||
ciTasks.push('dump-symbols') if process.platform is 'darwin'
|
||||
ciTasks.push('set-version', 'check-licenses', 'lint', 'generate-asar')
|
||||
ciTasks.push('mkdeb') if process.platform is 'linux'
|
||||
ciTasks.push('mktar') if process.platform is 'linux'
|
||||
ciTasks.push('codesign:exe') if process.platform is 'win32' and not process.env.CI
|
||||
ciTasks.push('create-windows-installer:installer') if process.platform is 'win32'
|
||||
ciTasks.push('test') if process.platform is 'darwin'
|
||||
|
||||
BIN
build/certs/AtomDevTestSignKey.p12
Normal file
BIN
build/certs/AtomDevTestSignKey.p12
Normal file
Binary file not shown.
@@ -1,24 +1,46 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
request = require 'request'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
signUsingWindowsSDK = (exeToSign, callback) ->
|
||||
{WIN_P12KEY_PASSWORD, WIN_P12KEY_URL} = process.env
|
||||
if WIN_P12KEY_URL?
|
||||
grunt.log.ok("Obtaining signing key")
|
||||
downloadedKeyFile = path.resolve(__dirname, 'DownloadedSignKey.p12')
|
||||
downloadFile WIN_P12KEY_URL, downloadedKeyFile, (done) ->
|
||||
signUsingWindowsSDKTool exeToSign, downloadedKeyFile, WIN_P12KEY_PASSWORD, (done) ->
|
||||
fs.unlinkSync(downloadedKeyFile)
|
||||
callback()
|
||||
else
|
||||
signUsingWindowsSDKTool exeToSign, path.resolve(__dirname, '..', 'certs', 'AtomDevTestSignKey.p12'), 'password', callback
|
||||
|
||||
signUsingWindowsSDKTool = (exeToSign, keyFilePath, password, callback) ->
|
||||
grunt.log.ok("Signing #{exeToSign}")
|
||||
args = ['sign', '/v', '/p', password, '/f', keyFilePath, exeToSign]
|
||||
spawn {cmd: 'C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.1A\\bin\\signtool.exe', args: args}, callback
|
||||
|
||||
signUsingJanky = (exeToSign, callback) ->
|
||||
spawn {cmd: process.env.JANKY_SIGNTOOL, args: [exeToSign]}, callback
|
||||
|
||||
signWindowsExecutable = if process.env.JANKY_SIGNTOOL then signUsingJanky else signUsingWindowsSDK
|
||||
|
||||
grunt.registerTask 'codesign:exe', 'CodeSign Atom.exe and Update.exe', ->
|
||||
done = @async()
|
||||
spawn {cmd: 'taskkill', args: ['/F', '/IM', 'atom.exe']}, ->
|
||||
cmd = process.env.JANKY_SIGNTOOL ? 'signtool'
|
||||
atomExePath = path.join(grunt.config.get('atom.shellAppDir'), 'atom.exe')
|
||||
spawn {cmd, args: [atomExePath]}, (error) ->
|
||||
signWindowsExecutable atomExePath, (error) ->
|
||||
return done(error) if error?
|
||||
|
||||
updateExePath = path.resolve(__dirname, '..', 'node_modules', 'grunt-electron-installer', 'vendor', 'Update.exe')
|
||||
spawn {cmd, args: [updateExePath]}, (error) -> done(error)
|
||||
signWindowsExecutable updateExePath, (error) -> done(error)
|
||||
|
||||
grunt.registerTask 'codesign:installer', 'CodeSign AtomSetup.exe', ->
|
||||
done = @async()
|
||||
cmd = process.env.JANKY_SIGNTOOL ? 'signtool'
|
||||
atomSetupExePath = path.resolve(grunt.config.get('atom.buildDir'), 'installer', 'AtomSetup.exe')
|
||||
spawn {cmd, args: [atomSetupExePath]}, (error) -> done(error)
|
||||
signWindowsExecutable atomSetupExePath, (error) -> done(error)
|
||||
|
||||
grunt.registerTask 'codesign:app', 'CodeSign Atom.app', ->
|
||||
done = @async()
|
||||
@@ -26,14 +48,24 @@ module.exports = (grunt) ->
|
||||
unlockKeychain (error) ->
|
||||
return done(error) if error?
|
||||
|
||||
cmd = 'codesign'
|
||||
args = ['--deep', '--force', '--verbose', '--sign', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')]
|
||||
spawn {cmd, args}, (error) -> done(error)
|
||||
spawn {cmd: 'codesign', args: args}, (error) -> done(error)
|
||||
|
||||
unlockKeychain = (callback) ->
|
||||
return callback() unless process.env.XCODE_KEYCHAIN
|
||||
|
||||
cmd = 'security'
|
||||
{XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN} = process.env
|
||||
args = ['unlock-keychain', '-p', XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN]
|
||||
spawn {cmd, args}, (error) -> callback(error)
|
||||
spawn {cmd: 'security', args: args}, (error) -> callback(error)
|
||||
|
||||
downloadFile = (sourceUrl, targetPath, callback) ->
|
||||
options = {
|
||||
url: sourceUrl
|
||||
headers: {
|
||||
'User-Agent': 'Atom Signing Key build task',
|
||||
'Accept': 'application/vnd.github.VERSION.raw'
|
||||
}
|
||||
}
|
||||
request(options)
|
||||
.pipe(fs.createWriteStream(targetPath))
|
||||
.on('finish', callback)
|
||||
|
||||
@@ -123,10 +123,10 @@ module.exports =
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
'tweetnacl@0.13.2':
|
||||
'tweetnacl@0.14.3':
|
||||
repository: 'https://github.com/dchest/tweetnacl-js'
|
||||
license: 'Public Domain'
|
||||
source: 'https://github.com/dchest/tweetnacl-js/blob/2f328394f74d83564634fb89ea2798caa3a4edb9/README.md says public domain.'
|
||||
source: 'https://github.com/dchest/tweetnacl-js/blob/1042c9c65dc8f1dcb9e981d962d7dbbcf58f1fdc/COPYING.txt says public domain.'
|
||||
'json-schema@0.2.2':
|
||||
repository: 'https://github.com/kriszyp/json-schema'
|
||||
license: 'BSD'
|
||||
|
||||
30
build/tasks/mktar-task.coffee
Normal file
30
build/tasks/mktar-task.coffee
Normal file
@@ -0,0 +1,30 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{spawn, fillTemplate} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'mktar', 'Create an archive', ->
|
||||
done = @async()
|
||||
|
||||
appFileName = grunt.config.get('atom.appFileName')
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
{version, description} = grunt.config.get('atom.metadata')
|
||||
|
||||
if process.arch is 'ia32'
|
||||
arch = 'i386'
|
||||
else if process.arch is 'x64'
|
||||
arch = 'amd64'
|
||||
else
|
||||
return done("Unsupported arch #{process.arch}")
|
||||
|
||||
iconPath = path.join(shellAppDir, 'resources', 'app.asar.unpacked', 'resources', 'atom.png')
|
||||
|
||||
cmd = path.join('script', 'mktar')
|
||||
args = [appFileName, version, arch, iconPath, buildDir]
|
||||
spawn {cmd, args}, (error) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
grunt.log.ok "Created " + path.join(buildDir, "#{appFileName}-#{version}-#{arch}.tar.gz")
|
||||
done()
|
||||
@@ -85,13 +85,13 @@ getAssets = ->
|
||||
arch = 'amd64'
|
||||
|
||||
# Check for a Debian build
|
||||
sourcePath = "#{buildDir}/#{appFileName}-#{version}-#{arch}.deb"
|
||||
sourcePath = path.join(buildDir, "#{appFileName}-#{version}-#{arch}.deb")
|
||||
assetName = "atom-#{arch}.deb"
|
||||
|
||||
# Check for a Fedora build
|
||||
unless fs.isFileSync(sourcePath)
|
||||
rpmName = fs.readdirSync("#{buildDir}/rpm")[0]
|
||||
sourcePath = "#{buildDir}/rpm/#{rpmName}"
|
||||
sourcePath = path.join(buildDir, "rpm", rpmName)
|
||||
if process.arch is 'ia32'
|
||||
arch = 'i386'
|
||||
else
|
||||
@@ -99,10 +99,17 @@ getAssets = ->
|
||||
assetName = "atom.#{arch}.rpm"
|
||||
|
||||
cp sourcePath, path.join(buildDir, assetName)
|
||||
assets = [{assetName, sourcePath}]
|
||||
|
||||
[
|
||||
{assetName, sourcePath}
|
||||
]
|
||||
# Check for an archive build on a debian build machine.
|
||||
# We could provide a Fedora version if some libraries are not compatible
|
||||
sourcePath = path.join(buildDir, "#{appFileName}-#{version}-#{arch}.tar.gz")
|
||||
if fs.isFileSync(sourcePath)
|
||||
assetName = "atom-#{arch}.tar.gz"
|
||||
cp sourcePath, path.join(buildDir, assetName)
|
||||
assets.push({assetName, sourcePath})
|
||||
|
||||
assets
|
||||
|
||||
logError = (message, error, details) ->
|
||||
grunt.log.error(message)
|
||||
|
||||
@@ -17,20 +17,6 @@ module.exports = (grunt) ->
|
||||
|
||||
packageSpecQueue = null
|
||||
|
||||
logDeprecations = (label, {stderr}={}) ->
|
||||
return unless process.env.JANKY_SHA1 or process.env.CI
|
||||
stderr ?= ''
|
||||
deprecatedStart = stderr.indexOf('Calls to deprecated functions')
|
||||
return if deprecatedStart is -1
|
||||
|
||||
grunt.log.error(label)
|
||||
stderr = stderr.substring(deprecatedStart)
|
||||
stderr = stderr.replace(/^\s*\[[^\]]+\]\s+/gm, '')
|
||||
stderr = stderr.replace(/source: .*$/gm, '')
|
||||
stderr = stderr.replace(/^"/gm, '')
|
||||
stderr = stderr.replace(/",\s*$/gm, '')
|
||||
grunt.log.error(stderr)
|
||||
|
||||
getAppPath = ->
|
||||
contentsDir = grunt.config.get('atom.contentsDir')
|
||||
switch process.platform
|
||||
@@ -57,14 +43,14 @@ module.exports = (grunt) ->
|
||||
args: ['--test', "--resource-path=#{resourcePath}", path.join(packagePath, 'spec')]
|
||||
opts:
|
||||
cwd: packagePath
|
||||
env: _.extend({}, process.env, ATOM_PATH: rootDir)
|
||||
env: _.extend({}, process.env, ELECTRON_ENABLE_LOGGING: true, ATOM_PATH: rootDir)
|
||||
else if process.platform is 'win32'
|
||||
options =
|
||||
cmd: process.env.comspec
|
||||
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--log-file=ci.log", path.join(packagePath, 'spec')]
|
||||
opts:
|
||||
cwd: packagePath
|
||||
env: _.extend({}, process.env, ATOM_PATH: rootDir)
|
||||
env: _.extend({}, process.env, ELECTRON_ENABLE_LOGGING: true, ATOM_PATH: rootDir)
|
||||
|
||||
grunt.log.ok "Launching #{path.basename(packagePath)} specs."
|
||||
spawn options, (error, results, code) ->
|
||||
@@ -74,7 +60,6 @@ module.exports = (grunt) ->
|
||||
fs.unlinkSync(path.join(packagePath, 'ci.log'))
|
||||
|
||||
failedPackages.push path.basename(packagePath) if error
|
||||
logDeprecations("#{path.basename(packagePath)} Specs", results)
|
||||
callback()
|
||||
|
||||
modulesDirectory = path.resolve('node_modules')
|
||||
@@ -87,7 +72,7 @@ module.exports = (grunt) ->
|
||||
packageSpecQueue.concurrency = Math.max(1, concurrency - 1)
|
||||
packageSpecQueue.drain = -> callback(null, failedPackages)
|
||||
|
||||
runCoreSpecs = (callback, logOutput = false) ->
|
||||
runCoreSpecs = (callback) ->
|
||||
appPath = getAppPath()
|
||||
resourcePath = process.cwd()
|
||||
coreSpecsPath = path.resolve('spec')
|
||||
@@ -97,21 +82,16 @@ module.exports = (grunt) ->
|
||||
cmd: appPath
|
||||
args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath, "--user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}"]
|
||||
opts:
|
||||
env: _.extend({}, process.env,
|
||||
ATOM_INTEGRATION_TESTS_ENABLED: true
|
||||
)
|
||||
env: _.extend({}, process.env, {ELECTRON_ENABLE_LOGGING: true, ATOM_INTEGRATION_TESTS_ENABLED: true})
|
||||
stdio: 'inherit'
|
||||
|
||||
else if process.platform is 'win32'
|
||||
options =
|
||||
cmd: process.env.comspec
|
||||
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", '--log-file=ci.log', coreSpecsPath]
|
||||
opts:
|
||||
env: _.extend({}, process.env,
|
||||
ATOM_INTEGRATION_TESTS_ENABLED: true
|
||||
)
|
||||
|
||||
if logOutput
|
||||
options.opts.stdio = 'inherit'
|
||||
env: _.extend({}, process.env, {ELECTRON_ENABLE_LOGGING: true, ATOM_INTEGRATION_TESTS_ENABLED: true})
|
||||
stdio: 'inherit'
|
||||
|
||||
grunt.log.ok "Launching core specs."
|
||||
spawn options, (error, results, code) ->
|
||||
@@ -121,7 +101,6 @@ module.exports = (grunt) ->
|
||||
else
|
||||
# TODO: Restore concurrency on Windows
|
||||
packageSpecQueue?.concurrency = concurrency
|
||||
logDeprecations('Core Specs', results)
|
||||
|
||||
callback(null, error)
|
||||
|
||||
@@ -134,17 +113,11 @@ module.exports = (grunt) ->
|
||||
else
|
||||
async.parallel
|
||||
|
||||
# If we're just running the core specs then we won't have any output to
|
||||
# indicate the tests actually *are* running. This upsets Travis:
|
||||
# https://github.com/atom/atom/issues/10837. So pass the test output
|
||||
# through.
|
||||
runCoreSpecsWithLogging = (callback) -> runCoreSpecs(callback, true)
|
||||
|
||||
specs =
|
||||
if process.env.ATOM_SPECS_TASK is 'packages'
|
||||
[runPackageSpecs]
|
||||
else if process.env.ATOM_SPECS_TASK is 'core'
|
||||
[runCoreSpecsWithLogging]
|
||||
[runCoreSpecs]
|
||||
else
|
||||
[runCoreSpecs, runPackageSpecs]
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
|
||||
* OS with 64-bit or 32-bit architecture
|
||||
* C++ toolchain
|
||||
* [Git](http://git-scm.com/)
|
||||
* [Node.js](http://nodejs.org/download/) (0.10.x or above)
|
||||
* [Git](https://git-scm.com/)
|
||||
* [Node.js](https://nodejs.org/en/download/) (0.10.x or above)
|
||||
* [npm](https://www.npmjs.com/) v1.4.x or above (automatically bundled with Node.js)
|
||||
* `npm -v` to check the version.
|
||||
* `npm config set python /usr/bin/python2 -g` to ensure that gyp uses python2.
|
||||
@@ -74,18 +74,24 @@ If you have problems with permissions don't forget to prefix with `sudo`
|
||||
|
||||
To use the newly installed Atom, quit and restart all running Atom instances.
|
||||
|
||||
5. *Optionally*, you may generate distributable packages of Atom at `out`. Currently, `.deb` and `.rpm` package types are supported. To create a `.deb` package run:
|
||||
5. *Optionally*, you may generate distributable packages of Atom at `out`. Currently, `.deb` and `.rpm` package types are supported, as well as a `.tar.gz` archive. To create a `.deb` package run:
|
||||
|
||||
```sh
|
||||
script/grunt mkdeb
|
||||
```
|
||||
|
||||
To create an `.rpm` package run
|
||||
To create a `.rpm` package run
|
||||
|
||||
```sh
|
||||
script/grunt mkrpm
|
||||
```
|
||||
|
||||
To create a `.tar.gz` archive run
|
||||
|
||||
```sh
|
||||
script/grunt mktar
|
||||
```
|
||||
|
||||
## Advanced Options
|
||||
|
||||
### Custom build directory
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Requirements
|
||||
|
||||
* OS X 10.8 or later
|
||||
* [Node.js](http://nodejs.org/download/) (0.10.x or above)
|
||||
* [Node.js](https://nodejs.org/en/download/) (0.10.x or above)
|
||||
* Command Line Tools for [Xcode](https://developer.apple.com/xcode/downloads/) (run `xcode-select --install` to install)
|
||||
|
||||
## Instructions
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Requirements
|
||||
|
||||
### General
|
||||
* [Node.js](http://nodejs.org/en/download/) v4.x
|
||||
* [Node.js](https://nodejs.org/en/download/) v4.x
|
||||
* [Python](https://www.python.org/downloads/) v2.7.x
|
||||
* The python.exe must be available at `%SystemDrive%\Python27\python.exe`.
|
||||
If it is installed elsewhere, you can create a symbolic link to the
|
||||
@@ -14,29 +14,27 @@
|
||||
|
||||
You can use either:
|
||||
|
||||
* [Visual Studio 2013 Update 5](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Express or better) on Windows 7, 8 or 10
|
||||
* [Visual Studio 2015](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Community or better) with Windows 8 or 10
|
||||
* [Visual Studio 2013 Update 5](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Express or better) on Windows 7, 8 or 10
|
||||
* [Visual Studio 2015](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Community or better) with Windows 8 or 10
|
||||
|
||||
Whichever version you use, ensure that:
|
||||
|
||||
* The default installation folder is chosen so the build tools can find it
|
||||
* Visual C++ support is installed
|
||||
* You set the `GYP_MSVS_VERSION` environment variable to the Visual Studio version (`2013` or `2015`), e.g. , e.g. ``[Environment]::SetEnvironmentVariable("GYP_MSVS_VERSION", "2015", "User")`` in PowerShell or set it in Windows advanced system settings control panel.
|
||||
* The git command is in your path
|
||||
* A `git` command is in your path
|
||||
* If you have both VS2013 and VS2015 installed set the `GYP_MSVS_VERSION` environment variable to the Visual Studio version (`2013` or `2015`) you wish to use, e.g. ``[Environment]::SetEnvironmentVariable("GYP_MSVS_VERSION", "2015", "User")`` in PowerShell or set it in Windows advanced system settings control panel.
|
||||
|
||||
## Instructions
|
||||
|
||||
You can run these commands using Command Prompt, PowerShell or Git Shell via [GitHub Desktop](https://desktop.github.com/). These instructions will assume the use of Bash from Git Shell - if you are using Command Prompt use a backslash instead: i.e. `script\build`.
|
||||
|
||||
**VS2015 + Git Shell users** should note that the default path supplied with Git Shell includes reference to an older version of msbuild that will fail. It is recommended you use a PowerShell window that has git in the path at this time.
|
||||
|
||||
```bash
|
||||
cd C:\
|
||||
git clone https://github.com/atom/atom/
|
||||
cd atom
|
||||
script/build
|
||||
```
|
||||
This will create the Atom application in the `Program Files` folder.
|
||||
This will create the Atom application in the `out\Atom` folder as well as copy it to a folder named `Atom` within `Program Files`.
|
||||
|
||||
### `script/build` Options
|
||||
* `--install-dir` - Creates the final built application in this directory. Example (trailing slash is optional):
|
||||
@@ -47,6 +45,7 @@ This will create the Atom application in the `Program Files` folder.
|
||||
```bash
|
||||
./script/build --build-dir Z:\Some\Temporary\Directory\
|
||||
```
|
||||
* `--no-install` - Skips the installation task after building.
|
||||
* `--verbose` - Verbose mode. A lot more information output.
|
||||
|
||||
## Do I have to use GitHub Desktop?
|
||||
@@ -63,37 +62,34 @@ If none of this works, do install Github Desktop and use its Git Shell as it mak
|
||||
|
||||
### Common Errors
|
||||
* `node is not recognized`
|
||||
|
||||
* If you just installed Node.js, you'll need to restart your PowerShell/Command Prompt/Git Shell before the node
|
||||
command is available on your Path.
|
||||
|
||||
* `script/build` outputs only the Node.js and Python versions before returning
|
||||
* `msbuild.exe failed with exit code: 1`
|
||||
* Ensure you have Visual C++ support installed. Go into Add/Remove Programs, select Visual Studio and press Modify and then check the Visual C++ box.
|
||||
|
||||
* `script/build` stops with no error or warning shortly after displaying the versions of node, npm and Python
|
||||
* Make sure that the path where you have checked out Atom does not include a space. e.g. use `c:\atom` and not `c:\my stuff\atom`
|
||||
|
||||
* `script/build` outputs only the Node.js 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.`
|
||||
|
||||
* This 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.js 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
|
||||
|
||||
* `script/build` stops at installing runas with `Failed at the runas@x.y.z install script.`
|
||||
* `'node_modules\.bin\npm' is not recognized as an internal or external command, operable program or batch file.`
|
||||
* This occurs if the previous build left things in a bad state. Run `script\clean` and then `script\build` again.
|
||||
|
||||
* `script/build` stops at installing runas with `Failed at the runas@x.y.z install script.`
|
||||
* See the next item.
|
||||
|
||||
* `error MSB8020: The build tools for Visual Studio 201? (Platform Toolset = 'v1?0') cannot be found.`
|
||||
|
||||
* If you're building Atom with Visual Studio 2013 or above make sure the `GYP_MSVS_VERSION` environment variable is set, and then re-run `script/build` after a clean:
|
||||
|
||||
```bash
|
||||
$env:GYP_MSVS_VERSION='2013' # '2015' if using Visual Studio 2015, and so on
|
||||
script/clean
|
||||
script/build
|
||||
```
|
||||
* If you are using Visual Studio 2013 or above and the build fails with some other error message this environment variable might still be required and ensure you have Visual C++ language support installed.
|
||||
* If you're building Atom with Visual Studio 2013 try setting the `GYP_MSVS_VERSION` environment variable to 2013 and then `script/clean` followed by `script/build` (re-open your command prompt or Powershell window if you set it using the GUI)
|
||||
|
||||
* Other `node-gyp` errors on first build attempt, even though the right Node.js and Python versions are installed.
|
||||
* Do try the build command one more time, as experience shows it often works on second try in many of these cases.
|
||||
|
||||
18
docs/native-profiling.md
Normal file
18
docs/native-profiling.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Profiling the Atom Render Process on OS X with Instruments
|
||||
|
||||

|
||||
|
||||
* Determine the version of Electron for your version of Atom.
|
||||
* Open the dev tools with `alt-cmd-i`
|
||||
* Evaluate `process.versions.electron` in the console.
|
||||
* Based on this version, download the appropriate Electron symbols from the [releases](https://github.com/atom/electron/releases) page.
|
||||
* The file name should look like `electron-v0.X.Y-darwin-x64-dsym.zip`.
|
||||
* Decompress these symbols in your `~/Downloads` directory.
|
||||
* Now create a time profile in Instruments.
|
||||
* Open `Instruments.app`.
|
||||
* Select `Time Profiler`
|
||||
* In Atom, determine the pid to attach to by evaluating `process.pid` in the dev tools console.
|
||||
* Attach to this pid via the menu at the upper left corner of the Instruments profiler.
|
||||
* Click record, do your thing.
|
||||
* Click stop.
|
||||
* The symbols should have been automatically located by Instruments (via Spotlight or something?), giving you a readable profile.
|
||||
@@ -18,15 +18,15 @@
|
||||
# 'ctrl-p': 'core:move-down'
|
||||
#
|
||||
# You can find more information about keymaps in these guides:
|
||||
# * https://atom.io/docs/latest/using-atom-basic-customization#customizing-key-bindings
|
||||
# * https://atom.io/docs/latest/behind-atom-keymaps-in-depth
|
||||
# * http://flight-manual.atom.io/using-atom/sections/basic-customization/#_customizing_keybindings
|
||||
# * http://flight-manual.atom.io/behind-atom/sections/keymaps-in-depth/
|
||||
#
|
||||
# If you're having trouble with your keybindings not working, try the
|
||||
# Keybinding Resolver: `Cmd+.` on OS X and `Ctrl+.` on other platforms. See the
|
||||
# Debugging Guide for more information:
|
||||
# * https://atom.io/docs/latest/hacking-atom-debugging#check-the-keybindings
|
||||
# * http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-the-keybindings
|
||||
#
|
||||
# This file uses CoffeeScript Object Notation (CSON).
|
||||
# If you are unfamiliar with CSON, you can read more about it in the
|
||||
# Atom Flight Manual:
|
||||
# https://atom.io/docs/latest/using-atom-basic-customization#cson
|
||||
# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson
|
||||
|
||||
@@ -130,6 +130,8 @@
|
||||
|
||||
# Atom Specific
|
||||
'ctrl-W': 'editor:select-word'
|
||||
'cmd-ctrl-left': 'editor:move-selection-left'
|
||||
'cmd-ctrl-right': 'editor:move-selection-right'
|
||||
|
||||
# Sublime Parity
|
||||
'cmd-a': 'core:select-all'
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
'ctrl-shift-pageup': 'pane:move-item-left'
|
||||
'ctrl-shift-pagedown': 'pane:move-item-right'
|
||||
'f11': 'window:toggle-full-screen'
|
||||
'alt-shift-left': 'editor:move-selection-left'
|
||||
'alt-shift-right': 'editor:move-selection-right'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-,': 'application:show-settings'
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
'ctrl-shift-left': 'pane:move-item-left'
|
||||
'ctrl-shift-right': 'pane:move-item-right'
|
||||
'f11': 'window:toggle-full-screen'
|
||||
'alt-shift-left': 'editor:move-selection-left'
|
||||
'alt-shift-right': 'editor:move-selection-right'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-,': 'application:show-settings'
|
||||
|
||||
@@ -75,6 +75,13 @@
|
||||
{ label: 'Join Lines', command: 'editor:join-lines' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Columns',
|
||||
submenu: [
|
||||
{ label: 'Move Selection Left', command: 'editor:move-selection-left' }
|
||||
{ label: 'Move Selection Right', command: 'editor:move-selection-right' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Text',
|
||||
submenu: [
|
||||
@@ -221,10 +228,10 @@
|
||||
{label: 'Delete', command: 'core:delete'}
|
||||
{label: 'Select All', command: 'core:select-all'}
|
||||
{type: 'separator'}
|
||||
{label: 'Split Up', command: 'pane:split-up'}
|
||||
{label: 'Split Down', command: 'pane:split-down'}
|
||||
{label: 'Split Left', command: 'pane:split-left'}
|
||||
{label: 'Split Right', command: 'pane:split-right'}
|
||||
{label: 'Split Up', command: 'pane:split-up-and-copy-active-item'}
|
||||
{label: 'Split Down', command: 'pane:split-down-and-copy-active-item'}
|
||||
{label: 'Split Left', command: 'pane:split-left-and-copy-active-item'}
|
||||
{label: 'Split Right', command: 'pane:split-right-and-copy-active-item'}
|
||||
{label: 'Close Pane', command: 'pane:close'}
|
||||
{type: 'separator'}
|
||||
]
|
||||
|
||||
@@ -48,6 +48,13 @@
|
||||
{ label: '&Join Lines', command: 'editor:join-lines' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Columns',
|
||||
submenu: [
|
||||
{ label: 'Move Selection &Left', command: 'editor:move-selection-left' }
|
||||
{ label: 'Move Selection &Right', command: 'editor:move-selection-right' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Text',
|
||||
submenu: [
|
||||
@@ -197,10 +204,10 @@
|
||||
{label: 'Delete', command: 'core:delete'}
|
||||
{label: 'Select All', command: 'core:select-all'}
|
||||
{type: 'separator'}
|
||||
{label: 'Split Up', command: 'pane:split-up'}
|
||||
{label: 'Split Down', command: 'pane:split-down'}
|
||||
{label: 'Split Left', command: 'pane:split-left'}
|
||||
{label: 'Split Right', command: 'pane:split-right'}
|
||||
{label: 'Split Up', command: 'pane:split-up-and-copy-active-item'}
|
||||
{label: 'Split Down', command: 'pane:split-down-and-copy-active-item'}
|
||||
{label: 'Split Left', command: 'pane:split-left-and-copy-active-item'}
|
||||
{label: 'Split Right', command: 'pane:split-right-and-copy-active-item'}
|
||||
{label: 'Close Pane', command: 'pane:close'}
|
||||
{type: 'separator'}
|
||||
]
|
||||
|
||||
@@ -56,6 +56,13 @@
|
||||
{ label: '&Join Lines', command: 'editor:join-lines' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Columns',
|
||||
submenu: [
|
||||
{ label: 'Move Selection &Left', command: 'editor:move-selection-left' }
|
||||
{ label: 'Move Selection &Right', command: 'editor:move-selection-right' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Text',
|
||||
submenu: [
|
||||
@@ -200,10 +207,10 @@
|
||||
{label: 'Delete', command: 'core:delete'}
|
||||
{label: 'Select All', command: 'core:select-all'}
|
||||
{type: 'separator'}
|
||||
{label: 'Split Up', command: 'pane:split-up'}
|
||||
{label: 'Split Down', command: 'pane:split-down'}
|
||||
{label: 'Split Left', command: 'pane:split-left'}
|
||||
{label: 'Split Right', command: 'pane:split-right'}
|
||||
{label: 'Split Up', command: 'pane:split-up-and-copy-active-item'}
|
||||
{label: 'Split Down', command: 'pane:split-down-and-copy-active-item'}
|
||||
{label: 'Split Left', command: 'pane:split-left-and-copy-active-item'}
|
||||
{label: 'Split Right', command: 'pane:split-right-and-copy-active-item'}
|
||||
{label: 'Close Pane', command: 'pane:close'}
|
||||
{type: 'separator'}
|
||||
]
|
||||
|
||||
64
package.json
64
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.8.0-dev",
|
||||
"version": "1.9.0-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -15,7 +15,7 @@
|
||||
"electronVersion": "0.36.8",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^6.3.1",
|
||||
"atom-keymap": "6.3.2",
|
||||
"babel-core": "^5.8.21",
|
||||
"bootstrap": "^3.3.4",
|
||||
"cached-run-in-this-context": "0.4.1",
|
||||
@@ -37,7 +37,7 @@
|
||||
"less-cache": "0.23",
|
||||
"line-top-index": "0.2.0",
|
||||
"marked": "^0.3.4",
|
||||
"nodegit": "0.12.0",
|
||||
"nodegit": "0.12.2",
|
||||
"normalize-package-data": "^2.0.0",
|
||||
"nslog": "^3",
|
||||
"oniguruma": "^5",
|
||||
@@ -54,7 +54,7 @@
|
||||
"service-hub": "^0.7.0",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "8.4.3",
|
||||
"text-buffer": "8.4.6",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"yargs": "^3.23.0"
|
||||
@@ -66,32 +66,32 @@
|
||||
"atom-light-ui": "0.43.0",
|
||||
"base16-tomorrow-dark-theme": "1.1.0",
|
||||
"base16-tomorrow-light-theme": "1.1.1",
|
||||
"one-dark-ui": "1.3.0",
|
||||
"one-light-ui": "1.3.0",
|
||||
"one-dark-ui": "1.3.1",
|
||||
"one-light-ui": "1.3.1",
|
||||
"one-dark-syntax": "1.2.0",
|
||||
"one-light-syntax": "1.2.0",
|
||||
"solarized-dark-syntax": "1.0.0",
|
||||
"solarized-light-syntax": "1.0.0",
|
||||
"about": "1.4.2",
|
||||
"solarized-dark-syntax": "1.0.2",
|
||||
"solarized-light-syntax": "1.0.2",
|
||||
"about": "1.5.0",
|
||||
"archive-view": "0.61.1",
|
||||
"autocomplete-atom-api": "0.10.0",
|
||||
"autocomplete-css": "0.11.0",
|
||||
"autocomplete-css": "0.11.1",
|
||||
"autocomplete-html": "0.7.2",
|
||||
"autocomplete-plus": "2.29.1",
|
||||
"autocomplete-plus": "2.29.2",
|
||||
"autocomplete-snippets": "1.10.0",
|
||||
"autoflow": "0.27.0",
|
||||
"autosave": "0.23.1",
|
||||
"background-tips": "0.26.0",
|
||||
"bookmarks": "0.38.2",
|
||||
"bracket-matcher": "0.81.0",
|
||||
"bookmarks": "0.39.0",
|
||||
"bracket-matcher": "0.82.0",
|
||||
"command-palette": "0.38.0",
|
||||
"deprecation-cop": "0.54.1",
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.21.0",
|
||||
"exception-reporting": "0.37.0",
|
||||
"find-and-replace": "0.197.4",
|
||||
"fuzzy-finder": "1.0.3",
|
||||
"exception-reporting": "0.38.1",
|
||||
"fuzzy-finder": "1.0.4",
|
||||
"git-diff": "1.0.1",
|
||||
"find-and-replace": "0.198.0",
|
||||
"go-to-line": "0.30.0",
|
||||
"grammar-selector": "0.48.1",
|
||||
"image-view": "0.57.0",
|
||||
@@ -101,28 +101,28 @@
|
||||
"link": "0.31.1",
|
||||
"markdown-preview": "0.158.0",
|
||||
"metrics": "0.53.1",
|
||||
"notifications": "0.63.1",
|
||||
"open-on-github": "1.0.1",
|
||||
"notifications": "0.63.2",
|
||||
"open-on-github": "1.1.0",
|
||||
"package-generator": "1.0.0",
|
||||
"settings-view": "0.235.1",
|
||||
"snippets": "1.0.2",
|
||||
"spell-check": "0.67.0",
|
||||
"status-bar": "1.2.0",
|
||||
"spell-check": "0.67.1",
|
||||
"status-bar": "1.2.3",
|
||||
"styleguide": "0.45.2",
|
||||
"symbols-view": "0.112.0",
|
||||
"tabs": "0.92.0",
|
||||
"tabs": "0.92.2",
|
||||
"timecop": "0.33.1",
|
||||
"tree-view": "0.203.3",
|
||||
"tree-view": "0.206.0",
|
||||
"update-package-dependencies": "0.10.0",
|
||||
"welcome": "0.34.0",
|
||||
"whitespace": "0.32.2",
|
||||
"wrap-guide": "0.38.1",
|
||||
"language-c": "0.51.2",
|
||||
"language-c": "0.51.3",
|
||||
"language-clojure": "0.20.0",
|
||||
"language-coffee-script": "0.46.1",
|
||||
"language-csharp": "0.12.0",
|
||||
"language-css": "0.36.0",
|
||||
"language-gfm": "0.85.0",
|
||||
"language-csharp": "0.12.1",
|
||||
"language-css": "0.36.1",
|
||||
"language-gfm": "0.86.0",
|
||||
"language-git": "0.12.1",
|
||||
"language-go": "0.42.0",
|
||||
"language-html": "0.44.1",
|
||||
@@ -130,24 +130,24 @@
|
||||
"language-java": "0.17.0",
|
||||
"language-javascript": "0.116.0",
|
||||
"language-json": "0.18.0",
|
||||
"language-less": "0.29.1",
|
||||
"language-make": "0.21.0",
|
||||
"language-less": "0.29.3",
|
||||
"language-make": "0.21.1",
|
||||
"language-mustache": "0.13.0",
|
||||
"language-objective-c": "0.15.1",
|
||||
"language-perl": "0.32.0",
|
||||
"language-perl": "0.34.0",
|
||||
"language-php": "0.37.0",
|
||||
"language-property-list": "0.8.0",
|
||||
"language-python": "0.43.1",
|
||||
"language-ruby": "0.68.4",
|
||||
"language-ruby": "0.68.5",
|
||||
"language-ruby-on-rails": "0.25.0",
|
||||
"language-sass": "0.46.0",
|
||||
"language-shellscript": "0.21.1",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.20.0",
|
||||
"language-sql": "0.21.0",
|
||||
"language-text": "0.7.1",
|
||||
"language-todo": "0.27.0",
|
||||
"language-toml": "0.18.0",
|
||||
"language-xml": "0.34.4",
|
||||
"language-xml": "0.34.5",
|
||||
"language-yaml": "0.25.2"
|
||||
},
|
||||
"private": true,
|
||||
|
||||
39
script/mktar
Executable file
39
script/mktar
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# mktar name version arch icon-path build-root-path
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT=`readlink -f "$0"`
|
||||
ROOT=`readlink -f $(dirname $SCRIPT)/..`
|
||||
cd $ROOT
|
||||
|
||||
NAME="$1"
|
||||
VERSION="$2"
|
||||
ARCH="$3"
|
||||
ICON_FILE="$4"
|
||||
BUILD_ROOT_PATH="$5"
|
||||
FILE_MODE=755
|
||||
|
||||
TAR_PATH=$BUILD_ROOT_PATH
|
||||
ATOM_PATH="$BUILD_ROOT_PATH/Atom"
|
||||
|
||||
TARGET_ROOT="`mktemp -d`"
|
||||
chmod $FILE_MODE "$TARGET_ROOT"
|
||||
NAME_IN_TAR="$NAME-$VERSION-$ARCH"
|
||||
TARGET="$TARGET_ROOT/$NAME_IN_TAR"
|
||||
|
||||
# Copy executable and resources
|
||||
cp -a "$ATOM_PATH" "$TARGET"
|
||||
|
||||
# Copy icon file
|
||||
cp "$ICON_FILE" "$TARGET/$NAME.png"
|
||||
|
||||
# Remove executable bit from .node files
|
||||
find "$TARGET" -type f -name "*.node" -exec chmod a-x {} \;
|
||||
|
||||
# Create the archive
|
||||
pushd "$TARGET_ROOT"
|
||||
tar caf "$TAR_PATH/$NAME_IN_TAR.tar.gz" "$NAME_IN_TAR"
|
||||
popd
|
||||
|
||||
rm -rf "$TARGET_ROOT"
|
||||
@@ -17,7 +17,7 @@ exports.safeExec = function(command, options, callback) {
|
||||
var child = childProcess.exec(command, options, function(error, stdout, stderr) {
|
||||
if (error)
|
||||
process.exit(error.code || 1);
|
||||
else
|
||||
else if (callback)
|
||||
callback(null);
|
||||
});
|
||||
child.stderr.pipe(process.stderr);
|
||||
|
||||
@@ -64,6 +64,16 @@ describe('AutoUpdateManager (renderer)', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('::onUpdateError', () => {
|
||||
it('subscribes to "update-error" event', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
autoUpdateManager.onUpdateError(spy)
|
||||
electronAutoUpdater.emit('error', {}, 'an error message')
|
||||
waitsFor(() => spy.callCount === 1)
|
||||
runs(() => expect(autoUpdateManager.getErrorMessage()).toBe('an error message'))
|
||||
})
|
||||
})
|
||||
|
||||
describe('::platformSupportsUpdates', () => {
|
||||
let state, releaseChannel
|
||||
it('returns true on OS X and Windows when in stable', () => {
|
||||
|
||||
@@ -88,14 +88,15 @@ describe "BufferedProcess", ->
|
||||
describe "when the explorer command is spawned on Windows", ->
|
||||
it "doesn't quote arguments of the form /root,C...", ->
|
||||
new BufferedProcess({command: 'explorer.exe', args: ['/root,C:\\foo']})
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '"explorer.exe /root,C:\\foo"'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"explorer.exe /root,C:\\foo"'
|
||||
|
||||
it "spawns the command using a cmd.exe wrapper", ->
|
||||
new BufferedProcess({command: 'dir'})
|
||||
expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'cmd.exe'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe '/s'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/c'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '"dir"'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/d'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '/c'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"dir"'
|
||||
|
||||
it "calls the specified stdout, stderr, and exit callbacks", ->
|
||||
stdout = ''
|
||||
|
||||
@@ -230,9 +230,7 @@ describe('GitRepositoryAsync', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// @joshaber: Disabling for now. There seems to be some race with path
|
||||
// subscriptions leading to intermittent test failures, e.g.: https://travis-ci.org/atom/atom/jobs/102702554
|
||||
xdescribe('.checkoutHeadForEditor(editor)', () => {
|
||||
describe('.checkoutHeadForEditor(editor)', () => {
|
||||
let filePath
|
||||
let editor
|
||||
|
||||
|
||||
@@ -268,6 +268,36 @@ describe "Starting Atom", ->
|
||||
[otherTempDirPath]
|
||||
].sort()
|
||||
|
||||
it "doesn't reopen any previously opened windows if restorePreviousWindowsOnStart is disabled", ->
|
||||
runAtom [tempDirPath], {ATOM_HOME: atomHome}, (client) ->
|
||||
client
|
||||
.waitForExist("atom-workspace")
|
||||
.waitForNewWindow(->
|
||||
@startAnotherAtom([otherTempDirPath], ATOM_HOME: atomHome)
|
||||
, 5000)
|
||||
.waitForExist("atom-workspace")
|
||||
|
||||
configPath = path.join(atomHome, 'config.cson')
|
||||
config = CSON.readFileSync(configPath)
|
||||
config['*'].core = {restorePreviousWindowsOnStart: false}
|
||||
CSON.writeFileSync(configPath, config)
|
||||
|
||||
runAtom [], {ATOM_HOME: atomHome}, (client) ->
|
||||
windowProjectPaths = []
|
||||
|
||||
client
|
||||
.waitForWindowCount(1, 10000)
|
||||
.then ({value: windowHandles}) ->
|
||||
@window(windowHandles[0])
|
||||
.waitForExist("atom-workspace")
|
||||
.treeViewRootDirectories()
|
||||
.then ({value: directories}) -> windowProjectPaths.push(directories)
|
||||
|
||||
.call ->
|
||||
expect(windowProjectPaths).toEqual [
|
||||
[]
|
||||
]
|
||||
|
||||
describe "opening a remote directory", ->
|
||||
it "opens the parent directory and creates an empty text editor", ->
|
||||
remoteDirectory = 'remote://server:3437/some/directory/path'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
Grim = require 'grim'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
@@ -96,13 +97,10 @@ buildTerminalReporter = (logFile, resolveWithExitCode) ->
|
||||
log(str)
|
||||
onComplete: (runner) ->
|
||||
fs.closeSync(logStream) if logStream?
|
||||
if process.env.JANKY_SHA1 or process.env.CI
|
||||
grim = require 'grim'
|
||||
|
||||
if grim.getDeprecationsLength() > 0
|
||||
grim.logDeprecations()
|
||||
resolveWithExitCode(1)
|
||||
return
|
||||
if Grim.getDeprecationsLength() > 0
|
||||
Grim.logDeprecations()
|
||||
resolveWithExitCode(1)
|
||||
return
|
||||
|
||||
if runner.results().failedCount > 0
|
||||
resolveWithExitCode(1)
|
||||
|
||||
@@ -66,6 +66,9 @@ describe "PackageManager", ->
|
||||
expect(addErrorHandler.argsForCall[0][0].message).toContain("Failed to load the package-with-broken-package-json package")
|
||||
expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual "package-with-broken-package-json"
|
||||
|
||||
it "returns null if the package name or path starts with a dot", ->
|
||||
expect(atom.packages.loadPackage("/Users/user/.atom/packages/.git")).toBeNull()
|
||||
|
||||
it "normalizes short repository urls in package.json", ->
|
||||
{metadata} = atom.packages.loadPackage("package-with-short-url-package-json")
|
||||
expect(metadata.repository.type).toBe "git"
|
||||
|
||||
@@ -262,6 +262,26 @@ describe "Pane", ->
|
||||
pane.setPendingItem("fake item two")
|
||||
expect(callbackCalled).toBeTruthy()
|
||||
|
||||
it "isn't called when a pending item is replaced with a new one", ->
|
||||
pane = null
|
||||
pendingSpy = jasmine.createSpy("onItemDidTerminatePendingState")
|
||||
destroySpy = jasmine.createSpy("onWillDestroyItem")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt', pending: true).then ->
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
runs ->
|
||||
pane.onItemDidTerminatePendingState pendingSpy
|
||||
pane.onWillDestroyItem destroySpy
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', pending: true)
|
||||
|
||||
runs ->
|
||||
expect(destroySpy).toHaveBeenCalled()
|
||||
expect(pendingSpy).not.toHaveBeenCalled()
|
||||
|
||||
describe "::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()", ->
|
||||
it "sets the active item to the next/previous item in the itemStack, looping around at either end", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")]))
|
||||
|
||||
66
spec/resource-pool-spec.js
Normal file
66
spec/resource-pool-spec.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/** @babel */
|
||||
|
||||
import ResourcePool from '../src/resource-pool'
|
||||
|
||||
import {it} from './async-spec-helpers'
|
||||
|
||||
describe('ResourcePool', () => {
|
||||
let queue
|
||||
|
||||
beforeEach(() => {
|
||||
queue = new ResourcePool([{}])
|
||||
})
|
||||
|
||||
describe('.enqueue', () => {
|
||||
it('calls the enqueued function', async () => {
|
||||
let called = false
|
||||
await queue.enqueue(() => {
|
||||
called = true
|
||||
return Promise.resolve()
|
||||
})
|
||||
expect(called).toBe(true)
|
||||
})
|
||||
|
||||
it('forwards values from the inner promise', async () => {
|
||||
const result = await queue.enqueue(() => Promise.resolve(42))
|
||||
expect(result).toBe(42)
|
||||
})
|
||||
|
||||
it('forwards errors from the inner promise', async () => {
|
||||
let threw = false
|
||||
try {
|
||||
await queue.enqueue(() => Promise.reject(new Error('down with the sickness')))
|
||||
} catch (e) {
|
||||
threw = true
|
||||
}
|
||||
expect(threw).toBe(true)
|
||||
})
|
||||
|
||||
it('continues to dequeue work after a promise has been rejected', async () => {
|
||||
try {
|
||||
await queue.enqueue(() => Promise.reject(new Error('down with the sickness')))
|
||||
} catch (e) {}
|
||||
|
||||
const result = await queue.enqueue(() => Promise.resolve(42))
|
||||
expect(result).toBe(42)
|
||||
})
|
||||
|
||||
it('queues up work', async () => {
|
||||
let resolve = null
|
||||
queue.enqueue(() => {
|
||||
return new Promise((resolve_, reject) => {
|
||||
resolve = resolve_
|
||||
})
|
||||
})
|
||||
|
||||
expect(queue.getQueueDepth()).toBe(0)
|
||||
|
||||
queue.enqueue(() => new Promise((resolve, reject) => {}))
|
||||
|
||||
expect(queue.getQueueDepth()).toBe(1)
|
||||
resolve()
|
||||
|
||||
waitsFor(() => queue.getQueueDepth() === 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -91,3 +91,13 @@ describe "Selection", ->
|
||||
expect(buffer.lineForRow(0)).toBe " "
|
||||
expect(buffer.lineForRow(1)).toBe " "
|
||||
expect(buffer.lineForRow(2)).toBe ""
|
||||
|
||||
it "auto-indents if only a newline is inserted", ->
|
||||
selection.setBufferRange [[2, 0], [3, 0]]
|
||||
selection.insertText("\n", autoIndent: true)
|
||||
expect(buffer.lineForRow(2)).toBe " "
|
||||
|
||||
it "auto-indents if only a carriage return + newline is inserted", ->
|
||||
selection.setBufferRange [[2, 0], [3, 0]]
|
||||
selection.insertText("\r\n", autoIndent: true)
|
||||
expect(buffer.lineForRow(2)).toBe " "
|
||||
|
||||
@@ -112,14 +112,14 @@ afterEach ->
|
||||
|
||||
document.getElementById('jasmine-content').innerHTML = '' unless window.debugContent
|
||||
|
||||
ensureNoPathSubscriptions()
|
||||
warnIfLeakingPathSubscriptions()
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
|
||||
ensureNoPathSubscriptions = ->
|
||||
warnIfLeakingPathSubscriptions = ->
|
||||
watchedPaths = pathwatcher.getWatchedPaths()
|
||||
pathwatcher.closeAllWatchers()
|
||||
if watchedPaths.length > 0
|
||||
throw new Error("Leaking subscriptions for paths: " + watchedPaths.join(", "))
|
||||
console.error("WARNING: Leaking subscriptions for paths: " + watchedPaths.join(", "))
|
||||
pathwatcher.closeAllWatchers()
|
||||
|
||||
ensureNoDeprecatedFunctionsCalled = ->
|
||||
deprecations = Grim.getDeprecations()
|
||||
|
||||
@@ -3745,6 +3745,21 @@ describe('TextEditorComponent', function () {
|
||||
return event
|
||||
}
|
||||
|
||||
function buildKeydownEvent ({keyCode, target}) {
|
||||
let event = new KeyboardEvent('keydown')
|
||||
Object.defineProperty(event, 'keyCode', {
|
||||
get: function () {
|
||||
return keyCode
|
||||
}
|
||||
})
|
||||
Object.defineProperty(event, 'target', {
|
||||
get: function () {
|
||||
return target
|
||||
}
|
||||
})
|
||||
return event
|
||||
}
|
||||
|
||||
let inputNode
|
||||
|
||||
beforeEach(function () {
|
||||
@@ -3767,11 +3782,12 @@ describe('TextEditorComponent', function () {
|
||||
expect(editor.lineTextForBufferRow(0)).toBe('xyvar quicksort = function () {')
|
||||
})
|
||||
|
||||
it('replaces the last character if the length of the input\'s value does not increase, as occurs with the accented character menu', async function () {
|
||||
componentNode.dispatchEvent(buildTextInputEvent({
|
||||
data: 'u',
|
||||
target: inputNode
|
||||
}))
|
||||
it('replaces the last character if a keypress event is bracketed by keydown events with matching keyCodes, which occurs when the accented character menu is shown', async function () {
|
||||
componentNode.dispatchEvent(buildKeydownEvent({keyCode: 85, target: inputNode}))
|
||||
componentNode.dispatchEvent(buildTextInputEvent({data: 'u', target: inputNode}))
|
||||
componentNode.dispatchEvent(new KeyboardEvent('keypress'))
|
||||
componentNode.dispatchEvent(buildKeydownEvent({keyCode: 85, target: inputNode}))
|
||||
componentNode.dispatchEvent(new KeyboardEvent('keyup'))
|
||||
await nextViewUpdatePromise()
|
||||
|
||||
expect(editor.lineTextForBufferRow(0)).toBe('uvar quicksort = function () {')
|
||||
|
||||
@@ -4561,6 +4561,136 @@ describe "TextEditor", ->
|
||||
expect(cursor1.getBufferPosition()).toEqual [0, 0]
|
||||
expect(cursor3.getBufferPosition()).toEqual [1, 2]
|
||||
|
||||
describe ".moveSelectionLeft()", ->
|
||||
it "moves one active selection on one line one column to the left", ->
|
||||
editor.setSelectedBufferRange [[0, 4], [0, 13]]
|
||||
expect(editor.getSelectedText()).toBe 'quicksort'
|
||||
|
||||
editor.moveSelectionLeft()
|
||||
|
||||
expect(editor.getSelectedText()).toBe 'quicksort'
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 3], [0, 12]]
|
||||
|
||||
it "moves multiple active selections on one line one column to the left", ->
|
||||
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[0, 16], [0, 24]]])
|
||||
selections = editor.getSelections()
|
||||
|
||||
expect(selections[0].getText()).toBe 'quicksort'
|
||||
expect(selections[1].getText()).toBe 'function'
|
||||
|
||||
editor.moveSelectionLeft()
|
||||
|
||||
expect(selections[0].getText()).toBe 'quicksort'
|
||||
expect(selections[1].getText()).toBe 'function'
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[0, 15], [0, 23]]]
|
||||
|
||||
it "moves multiple active selections on multiple lines one column to the left", ->
|
||||
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]])
|
||||
selections = editor.getSelections()
|
||||
|
||||
expect(selections[0].getText()).toBe 'quicksort'
|
||||
expect(selections[1].getText()).toBe 'sort'
|
||||
|
||||
editor.moveSelectionLeft()
|
||||
|
||||
expect(selections[0].getText()).toBe 'quicksort'
|
||||
expect(selections[1].getText()).toBe 'sort'
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[1, 5], [1, 9]]]
|
||||
|
||||
describe "when a selection is at the first column of a line", ->
|
||||
it "does not change the selection", ->
|
||||
editor.setSelectedBufferRanges([[[0, 0], [0, 3]], [[1, 0], [1, 3]]])
|
||||
selections = editor.getSelections()
|
||||
|
||||
expect(selections[0].getText()).toBe 'var'
|
||||
expect(selections[1].getText()).toBe ' v'
|
||||
|
||||
editor.moveSelectionLeft()
|
||||
editor.moveSelectionLeft()
|
||||
|
||||
expect(selections[0].getText()).toBe 'var'
|
||||
expect(selections[1].getText()).toBe ' v'
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]], [[1, 0], [1, 3]]]
|
||||
|
||||
describe "when multiple selections are active on one line", ->
|
||||
it "does not change the selection", ->
|
||||
editor.setSelectedBufferRanges([[[0, 0], [0, 3]], [[0, 4], [0, 13]]])
|
||||
selections = editor.getSelections()
|
||||
|
||||
expect(selections[0].getText()).toBe 'var'
|
||||
expect(selections[1].getText()).toBe 'quicksort'
|
||||
|
||||
editor.moveSelectionLeft()
|
||||
|
||||
expect(selections[0].getText()).toBe 'var'
|
||||
expect(selections[1].getText()).toBe 'quicksort'
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]], [[0, 4], [0, 13]]]
|
||||
|
||||
describe ".moveSelectionRight()", ->
|
||||
it "moves one active selection on one line one column to the right", ->
|
||||
editor.setSelectedBufferRange [[0, 4], [0, 13]]
|
||||
expect(editor.getSelectedText()).toBe 'quicksort'
|
||||
|
||||
editor.moveSelectionRight()
|
||||
|
||||
expect(editor.getSelectedText()).toBe 'quicksort'
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 14]]
|
||||
|
||||
it "moves multiple active selections on one line one column to the right", ->
|
||||
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[0, 16], [0, 24]]])
|
||||
selections = editor.getSelections()
|
||||
|
||||
expect(selections[0].getText()).toBe 'quicksort'
|
||||
expect(selections[1].getText()).toBe 'function'
|
||||
|
||||
editor.moveSelectionRight()
|
||||
|
||||
expect(selections[0].getText()).toBe 'quicksort'
|
||||
expect(selections[1].getText()).toBe 'function'
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 5], [0, 14]], [[0, 17], [0, 25]]]
|
||||
|
||||
it "moves multiple active selections on multiple lines one column to the right", ->
|
||||
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]])
|
||||
selections = editor.getSelections()
|
||||
|
||||
expect(selections[0].getText()).toBe 'quicksort'
|
||||
expect(selections[1].getText()).toBe 'sort'
|
||||
|
||||
editor.moveSelectionRight()
|
||||
|
||||
expect(selections[0].getText()).toBe 'quicksort'
|
||||
expect(selections[1].getText()).toBe 'sort'
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 5], [0, 14]], [[1, 7], [1, 11]]]
|
||||
|
||||
describe "when a selection is at the last column of a line", ->
|
||||
it "does not change the selection", ->
|
||||
editor.setSelectedBufferRanges([[[2, 34], [2, 40]], [[5, 22], [5, 30]]])
|
||||
selections = editor.getSelections()
|
||||
|
||||
expect(selections[0].getText()).toBe 'items;'
|
||||
expect(selections[1].getText()).toBe 'shift();'
|
||||
|
||||
editor.moveSelectionRight()
|
||||
editor.moveSelectionRight()
|
||||
|
||||
expect(selections[0].getText()).toBe 'items;'
|
||||
expect(selections[1].getText()).toBe 'shift();'
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[2, 34], [2, 40]], [[5, 22], [5, 30]]]
|
||||
|
||||
describe "when multiple selections are active on one line", ->
|
||||
it "does not change the selection", ->
|
||||
editor.setSelectedBufferRanges([[[2, 27], [2, 33]], [[2, 34], [2, 40]]])
|
||||
selections = editor.getSelections()
|
||||
|
||||
expect(selections[0].getText()).toBe 'return'
|
||||
expect(selections[1].getText()).toBe 'items;'
|
||||
|
||||
editor.moveSelectionRight()
|
||||
|
||||
expect(selections[0].getText()).toBe 'return'
|
||||
expect(selections[1].getText()).toBe 'items;'
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[2, 27], [2, 33]], [[2, 34], [2, 40]]]
|
||||
|
||||
describe 'reading text', ->
|
||||
it '.lineTextForScreenRow(row)', ->
|
||||
editor.foldBufferRow(4)
|
||||
|
||||
@@ -47,9 +47,14 @@ describe "WorkspaceElement", ->
|
||||
|
||||
it "updates the font-family based on the 'editor.fontFamily' config value", ->
|
||||
initialCharWidth = editor.getDefaultCharWidth()
|
||||
expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily')
|
||||
fontFamily = atom.config.get('editor.fontFamily')
|
||||
fontFamily += ", 'Apple Color Emoji'" if process.platform is 'darwin'
|
||||
expect(getComputedStyle(editorElement).fontFamily).toBe fontFamily
|
||||
|
||||
atom.config.set('editor.fontFamily', 'sans-serif')
|
||||
expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily')
|
||||
fontFamily = atom.config.get('editor.fontFamily')
|
||||
fontFamily += ", 'Apple Color Emoji'" if process.platform is 'darwin'
|
||||
expect(getComputedStyle(editorElement).fontFamily).toBe fontFamily
|
||||
expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth
|
||||
|
||||
it "updates the line-height based on the 'editor.lineHeight' config value", ->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
_ = require 'underscore-plus'
|
||||
{ipcRenderer, remote, shell, webFrame} = require 'electron'
|
||||
{screen, ipcRenderer, remote, shell, webFrame} = require 'electron'
|
||||
ipcHelpers = require './ipc-helpers'
|
||||
{Disposable} = require 'event-kit'
|
||||
{getWindowLoadSettings, setWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
@@ -211,6 +211,14 @@ class ApplicationDelegate
|
||||
new Disposable ->
|
||||
ipcRenderer.removeListener('message', outerCallback)
|
||||
|
||||
onUpdateError: (callback) ->
|
||||
outerCallback = (event, message, detail) ->
|
||||
callback(detail) if message is 'update-error'
|
||||
|
||||
ipcRenderer.on('message', outerCallback)
|
||||
new Disposable ->
|
||||
ipcRenderer.removeListener('message', outerCallback)
|
||||
|
||||
onApplicationMenuCommand: (callback) ->
|
||||
outerCallback = (event, args...) ->
|
||||
callback(args...)
|
||||
@@ -233,14 +241,28 @@ class ApplicationDelegate
|
||||
openExternal: (url) ->
|
||||
shell.openExternal(url)
|
||||
|
||||
disablePinchToZoom: ->
|
||||
webFrame.setZoomLevelLimits(1, 1)
|
||||
disableZoom: ->
|
||||
outerCallback = ->
|
||||
webFrame.setZoomLevelLimits(1, 1)
|
||||
|
||||
outerCallback()
|
||||
# Set the limits every time a display is added or removed, otherwise the
|
||||
# configuration gets reset to the default, which allows zooming the
|
||||
# webframe.
|
||||
screen.on('display-added', outerCallback)
|
||||
screen.on('display-removed', outerCallback)
|
||||
new Disposable ->
|
||||
screen.removeListener('display-added', outerCallback)
|
||||
screen.removeListener('display-removed', outerCallback)
|
||||
|
||||
checkForUpdate: ->
|
||||
ipcRenderer.send('check-for-update')
|
||||
ipcRenderer.send('command', 'application:check-for-update')
|
||||
|
||||
restartAndInstallUpdate: ->
|
||||
ipcRenderer.send('install-update')
|
||||
ipcRenderer.send('command', 'application:install-update')
|
||||
|
||||
getAutoUpdateManagerState: ->
|
||||
ipcRenderer.sendSync('get-auto-update-manager-state')
|
||||
|
||||
getAutoUpdateManagerErrorMessage: ->
|
||||
ipcRenderer.sendSync('get-auto-update-manager-error')
|
||||
|
||||
@@ -208,7 +208,7 @@ class AtomEnvironment extends Model
|
||||
@stylesElement = @styles.buildStylesElement()
|
||||
@document.head.appendChild(@stylesElement)
|
||||
|
||||
@applicationDelegate.disablePinchToZoom()
|
||||
@disposables.add(@applicationDelegate.disableZoom())
|
||||
|
||||
@keymaps.subscribeToFileReadFailure()
|
||||
@keymaps.loadBundledKeymaps()
|
||||
|
||||
@@ -20,6 +20,9 @@ export default class AutoUpdateManager {
|
||||
}),
|
||||
applicationDelegate.onUpdateNotAvailable(() => {
|
||||
this.emitter.emit('update-not-available')
|
||||
}),
|
||||
applicationDelegate.onUpdateError(() => {
|
||||
this.emitter.emit('update-error')
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -41,6 +44,10 @@ export default class AutoUpdateManager {
|
||||
return this.applicationDelegate.getAutoUpdateManagerState()
|
||||
}
|
||||
|
||||
getErrorMessage () {
|
||||
return this.applicationDelegate.getAutoUpdateManagerErrorMessage()
|
||||
}
|
||||
|
||||
platformSupportsUpdates () {
|
||||
return atom.getReleaseChannel() !== 'dev' && this.getState() !== 'unsupported'
|
||||
}
|
||||
@@ -67,6 +74,10 @@ export default class AutoUpdateManager {
|
||||
return this.emitter.on('update-not-available', callback)
|
||||
}
|
||||
|
||||
onUpdateError (callback) {
|
||||
return this.emitter.on('update-error', callback)
|
||||
}
|
||||
|
||||
getPlatform () {
|
||||
return process.platform
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ ApplicationMenu = require './application-menu'
|
||||
AtomProtocolHandler = require './atom-protocol-handler'
|
||||
AutoUpdateManager = require './auto-update-manager'
|
||||
StorageFolder = require '../storage-folder'
|
||||
Config = require '../config'
|
||||
ipcHelpers = require '../ipc-helpers'
|
||||
{BrowserWindow, Menu, app, dialog, ipcMain, shell} = require 'electron'
|
||||
fs = require 'fs-plus'
|
||||
@@ -70,7 +71,11 @@ class AtomApplication
|
||||
@pidsToOpenWindows = {}
|
||||
@windows = []
|
||||
|
||||
@autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath)
|
||||
@config = new Config({configDirPath: process.env.ATOM_HOME, @resourcePath, enablePersistence: true})
|
||||
@config.setSchema null, {type: 'object', properties: _.clone(require('../config-schema'))}
|
||||
@config.load()
|
||||
|
||||
@autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath, @config)
|
||||
@applicationMenu = new ApplicationMenu(@version, @autoUpdateManager)
|
||||
@atomProtocolHandler = new AtomProtocolHandler(@resourcePath, @safeMode)
|
||||
|
||||
@@ -305,12 +310,12 @@ class AtomApplication
|
||||
ipcMain.on 'execute-javascript-in-dev-tools', (event, code) ->
|
||||
event.sender.devToolsWebContents?.executeJavaScript(code)
|
||||
|
||||
ipcMain.on 'check-for-update', =>
|
||||
@autoUpdateManager.check()
|
||||
|
||||
ipcMain.on 'get-auto-update-manager-state', (event) =>
|
||||
event.returnValue = @autoUpdateManager.getState()
|
||||
|
||||
ipcMain.on 'get-auto-update-manager-error', (event) =>
|
||||
event.returnValue = @autoUpdateManager.getErrorMessage()
|
||||
|
||||
ipcMain.on 'execute-javascript-in-dev-tools', (event, code) ->
|
||||
event.sender.devToolsWebContents?.executeJavaScript(code)
|
||||
|
||||
@@ -461,6 +466,7 @@ class AtomApplication
|
||||
openedWindow.restore()
|
||||
else
|
||||
openedWindow.focus()
|
||||
openedWindow.replaceEnvironment(env)
|
||||
else
|
||||
if devMode
|
||||
try
|
||||
@@ -510,7 +516,8 @@ class AtomApplication
|
||||
@storageFolder.storeSync('application.json', states)
|
||||
|
||||
loadState: (options) ->
|
||||
if (states = @storageFolder.load('application.json'))?.length > 0
|
||||
restorePreviousState = @config.get('core.restorePreviousWindowsOnStart') ? true
|
||||
if restorePreviousState and (states = @storageFolder.load('application.json'))?.length > 0
|
||||
for state in states
|
||||
@openWithOptions(_.extend(options, {
|
||||
initialPaths: state.initialPaths
|
||||
|
||||
@@ -68,6 +68,7 @@ class AtomWindow
|
||||
@loaded = true
|
||||
|
||||
@setLoadSettings(loadSettings)
|
||||
@env = loadSettings.env if loadSettings.env?
|
||||
@browserWindow.focusOnWebView() if @isSpec
|
||||
@browserWindow.temporaryState = {windowDimensions} if windowDimensions?
|
||||
|
||||
@@ -169,6 +170,9 @@ class AtomWindow
|
||||
else
|
||||
@browserWindow.once 'window:loaded', => @openLocations(locationsToOpen)
|
||||
|
||||
replaceEnvironment: (env) ->
|
||||
@browserWindow.webContents.send 'environment', env
|
||||
|
||||
sendMessage: (message, detail) ->
|
||||
@browserWindow.webContents.send 'message', message, detail
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
autoUpdater = null
|
||||
_ = require 'underscore-plus'
|
||||
Config = require '../config'
|
||||
{EventEmitter} = require 'events'
|
||||
path = require 'path'
|
||||
|
||||
@@ -16,13 +15,10 @@ module.exports =
|
||||
class AutoUpdateManager
|
||||
_.extend @prototype, EventEmitter.prototype
|
||||
|
||||
constructor: (@version, @testMode, resourcePath) ->
|
||||
constructor: (@version, @testMode, resourcePath, @config) ->
|
||||
@state = IdleState
|
||||
@iconPath = path.resolve(__dirname, '..', '..', 'resources', 'atom.png')
|
||||
@feedUrl = "https://atom.io/api/updates?version=#{@version}"
|
||||
@config = new Config({configDirPath: process.env.ATOM_HOME, resourcePath, enablePersistence: true})
|
||||
@config.setSchema null, {type: 'object', properties: _.clone(require('../config-schema'))}
|
||||
@config.load()
|
||||
process.nextTick => @setupAutoUpdater()
|
||||
|
||||
setupAutoUpdater: ->
|
||||
@@ -32,7 +28,8 @@ class AutoUpdateManager
|
||||
{autoUpdater} = require 'electron'
|
||||
|
||||
autoUpdater.on 'error', (event, message) =>
|
||||
@setState(ErrorState)
|
||||
@setState(ErrorState, message)
|
||||
@emitWindowEvent('update-error')
|
||||
console.error "Error Downloading Update: #{message}"
|
||||
|
||||
autoUpdater.setFeedURL @feedUrl
|
||||
@@ -82,14 +79,18 @@ class AutoUpdateManager
|
||||
atomWindow.sendMessage(eventName, payload)
|
||||
return
|
||||
|
||||
setState: (state) ->
|
||||
setState: (state, errorMessage) ->
|
||||
return if @state is state
|
||||
@state = state
|
||||
@errorMessage = errorMessage
|
||||
@emit 'state-changed', @state
|
||||
|
||||
getState: ->
|
||||
@state
|
||||
|
||||
getErrorMessage: ->
|
||||
@errorMessage
|
||||
|
||||
scheduleUpdateCheck: ->
|
||||
# Only schedule update check periodically if running in release version and
|
||||
# and there is no existing scheduled update check.
|
||||
|
||||
@@ -47,6 +47,7 @@ class BufferedNodeProcess extends BufferedProcess
|
||||
options ?= {}
|
||||
options.env ?= Object.create(process.env)
|
||||
options.env['ELECTRON_RUN_AS_NODE'] = 1
|
||||
options.env['ELECTRON_NO_ATTACH_CONSOLE'] = 1
|
||||
|
||||
args = args?.slice() ? []
|
||||
args.unshift(command)
|
||||
|
||||
@@ -67,7 +67,7 @@ class BufferedProcess
|
||||
cmdArgs.unshift("\"#{command}\"")
|
||||
else
|
||||
cmdArgs.unshift(command)
|
||||
cmdArgs = ['/s', '/c', "\"#{cmdArgs.join(' ')}\""]
|
||||
cmdArgs = ['/s', '/d', '/c', "\"#{cmdArgs.join(' ')}\""]
|
||||
cmdOptions = _.clone(options)
|
||||
cmdOptions.windowsVerbatimArguments = true
|
||||
@spawn(@getCmdPath(), cmdArgs, cmdOptions)
|
||||
|
||||
@@ -58,7 +58,7 @@ function getFromShell () {
|
||||
function needsPatching (options = { platform: process.platform, env: process.env }) {
|
||||
if (options.platform === 'darwin' && !options.env.PWD) {
|
||||
let shell = getUserShell()
|
||||
if (shell.endsWith('csh') || shell.endsWith('tcsh')) {
|
||||
if (shell.endsWith('csh') || shell.endsWith('tcsh') || shell.endsWith('fish')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -67,9 +67,20 @@ function needsPatching (options = { platform: process.platform, env: process.env
|
||||
return false
|
||||
}
|
||||
|
||||
// Fix for #11302 because `process.env` on Windows is a magic object that offers case-insensitive
|
||||
// environment variable matching. By always cloning to `process.env` we prevent breaking the
|
||||
// underlying functionality.
|
||||
function clone (to, from) {
|
||||
for (var key in to) {
|
||||
delete to[key]
|
||||
}
|
||||
|
||||
Object.assign(to, from)
|
||||
}
|
||||
|
||||
function normalize (options = {}) {
|
||||
if (options && options.env) {
|
||||
process.env = options.env
|
||||
clone(process.env, options.env)
|
||||
}
|
||||
|
||||
if (!options.env) {
|
||||
@@ -85,10 +96,18 @@ function normalize (options = {}) {
|
||||
// in #4126. Retain the original in case someone needs it.
|
||||
let shellEnv = getFromShell()
|
||||
if (shellEnv && shellEnv.PATH) {
|
||||
process._originalEnv = process.env
|
||||
process.env = shellEnv
|
||||
process._originalEnv = Object.assign({}, process.env)
|
||||
clone(process.env, shellEnv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default { getFromShell, needsPatching, normalize }
|
||||
function replace (env) {
|
||||
if (!env || !env.PATH) {
|
||||
return
|
||||
}
|
||||
|
||||
clone(process.env, env)
|
||||
}
|
||||
|
||||
export default { getFromShell, needsPatching, normalize, replace }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import fs from 'fs-plus'
|
||||
import path from 'path'
|
||||
import Git from 'nodegit'
|
||||
import ResourcePool from './resource-pool'
|
||||
import {Emitter, CompositeDisposable, Disposable} from 'event-kit'
|
||||
|
||||
const modifiedStatusFlags = Git.Status.STATUS.WT_MODIFIED | Git.Status.STATUS.INDEX_MODIFIED | Git.Status.STATUS.WT_DELETED | Git.Status.STATUS.INDEX_DELETED | Git.Status.STATUS.WT_TYPECHANGE | Git.Status.STATUS.INDEX_TYPECHANGE
|
||||
@@ -38,7 +39,8 @@ export default class GitRepositoryAsync {
|
||||
}
|
||||
|
||||
constructor (_path, options = {}) {
|
||||
Git.enableThreadSafety()
|
||||
// We'll serialize our access manually.
|
||||
Git.setThreadSafetyStatus(Git.THREAD_SAFETY.DISABLED)
|
||||
|
||||
this.emitter = new Emitter()
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
@@ -50,6 +52,11 @@ export default class GitRepositoryAsync {
|
||||
this._openExactPath = options.openExactPath || false
|
||||
|
||||
this.repoPromise = this.openRepository()
|
||||
// NB: We don't currently _use_ the pooled object. But by giving it one
|
||||
// thing, we're really just serializing all the work. Down the road, we
|
||||
// could open multiple connections to the repository.
|
||||
this.repoPool = new ResourcePool([this.repoPromise])
|
||||
|
||||
this.isCaseInsensitive = fs.isCaseInsensitive()
|
||||
this.upstream = {}
|
||||
this.submodules = {}
|
||||
@@ -81,6 +88,7 @@ export default class GitRepositoryAsync {
|
||||
this.emitter.dispose()
|
||||
this.emitter = null
|
||||
}
|
||||
|
||||
if (this.subscriptions) {
|
||||
this.subscriptions.dispose()
|
||||
this.subscriptions = null
|
||||
@@ -260,10 +268,12 @@ export default class GitRepositoryAsync {
|
||||
// Public: Returns a {Promise} which resolves to whether the given branch
|
||||
// exists.
|
||||
hasBranch (branch) {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.getBranch(branch))
|
||||
.then(branch => branch != null)
|
||||
.catch(_ => false)
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.getBranch(branch))
|
||||
.then(branch => branch != null)
|
||||
.catch(_ => false)
|
||||
})
|
||||
}
|
||||
|
||||
// Public: Retrieves a shortened version of the HEAD reference value.
|
||||
@@ -277,9 +287,11 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Promise} which resolves to a {String}.
|
||||
getShortHead (_path) {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.getCurrentBranch())
|
||||
.then(branch => branch.shorthand())
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.getCurrentBranch())
|
||||
.then(branch => branch.shorthand())
|
||||
})
|
||||
}
|
||||
|
||||
// Public: Is the given path a submodule in the repository?
|
||||
@@ -289,14 +301,18 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} that resolves true if the given path is a submodule in
|
||||
// the repository.
|
||||
isSubmodule (_path) {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.openIndex())
|
||||
.then(index => Promise.all([index, this.relativizeToWorkingDirectory(_path)]))
|
||||
.then(([index, relativePath]) => {
|
||||
const entry = index.getByPath(relativePath)
|
||||
if (!entry) return false
|
||||
return this.relativizeToWorkingDirectory(_path)
|
||||
.then(relativePath => {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.index())
|
||||
.then(index => {
|
||||
const entry = index.getByPath(relativePath)
|
||||
if (!entry) return false
|
||||
|
||||
return entry.mode === submoduleMode
|
||||
return entry.mode === submoduleMode
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -311,16 +327,18 @@ export default class GitRepositoryAsync {
|
||||
// * `ahead` The {Number} of commits ahead.
|
||||
// * `behind` The {Number} of commits behind.
|
||||
getAheadBehindCount (reference, _path) {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => Promise.all([repo, repo.getBranch(reference)]))
|
||||
.then(([repo, local]) => {
|
||||
const upstream = Git.Branch.upstream(local)
|
||||
return Promise.all([repo, local, upstream])
|
||||
})
|
||||
.then(([repo, local, upstream]) => {
|
||||
return Git.Graph.aheadBehind(repo, local.target(), upstream.target())
|
||||
})
|
||||
.catch(_ => ({ahead: 0, behind: 0}))
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => Promise.all([repo, repo.getBranch(reference)]))
|
||||
.then(([repo, local]) => {
|
||||
const upstream = Git.Branch.upstream(local)
|
||||
return Promise.all([repo, local, upstream])
|
||||
})
|
||||
.then(([repo, local, upstream]) => {
|
||||
return Git.Graph.aheadBehind(repo, local.target(), upstream.target())
|
||||
})
|
||||
.catch(_ => ({ahead: 0, behind: 0}))
|
||||
})
|
||||
}
|
||||
|
||||
// Public: Get the cached ahead/behind commit counts for the current branch's
|
||||
@@ -352,10 +370,12 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to the {String} git configuration value
|
||||
// specified by the key.
|
||||
getConfigValue (key, _path) {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.configSnapshot())
|
||||
.then(config => config.getStringBuf(key))
|
||||
.catch(_ => null)
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.configSnapshot())
|
||||
.then(config => config.getStringBuf(key))
|
||||
.catch(_ => null)
|
||||
})
|
||||
}
|
||||
|
||||
// Public: Get the URL for the 'origin' remote.
|
||||
@@ -378,9 +398,11 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a {String} branch name such as
|
||||
// `refs/remotes/origin/master`.
|
||||
getUpstreamBranch (_path) {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.getCurrentBranch())
|
||||
.then(branch => Git.Branch.upstream(branch))
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.getCurrentBranch())
|
||||
.then(branch => Git.Branch.upstream(branch))
|
||||
})
|
||||
}
|
||||
|
||||
// Public: Gets all the local and remote references.
|
||||
@@ -393,23 +415,25 @@ export default class GitRepositoryAsync {
|
||||
// * `remotes` An {Array} of remote reference names.
|
||||
// * `tags` An {Array} of tag reference names.
|
||||
getReferences (_path) {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.getReferences(Git.Reference.TYPE.LISTALL))
|
||||
.then(refs => {
|
||||
const heads = []
|
||||
const remotes = []
|
||||
const tags = []
|
||||
for (const ref of refs) {
|
||||
if (ref.isTag()) {
|
||||
tags.push(ref.name())
|
||||
} else if (ref.isRemote()) {
|
||||
remotes.push(ref.name())
|
||||
} else if (ref.isBranch()) {
|
||||
heads.push(ref.name())
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => repo.getReferences(Git.Reference.TYPE.LISTALL))
|
||||
.then(refs => {
|
||||
const heads = []
|
||||
const remotes = []
|
||||
const tags = []
|
||||
for (const ref of refs) {
|
||||
if (ref.isTag()) {
|
||||
tags.push(ref.name())
|
||||
} else if (ref.isRemote()) {
|
||||
remotes.push(ref.name())
|
||||
} else if (ref.isBranch()) {
|
||||
heads.push(ref.name())
|
||||
}
|
||||
}
|
||||
}
|
||||
return {heads, remotes, tags}
|
||||
})
|
||||
return {heads, remotes, tags}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Public: Get the SHA for the given reference.
|
||||
@@ -421,9 +445,11 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to the current {String} SHA for the
|
||||
// given reference.
|
||||
getReferenceTarget (reference, _path) {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => Git.Reference.nameToId(repo, reference))
|
||||
.then(oid => oid.tostrS())
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => Git.Reference.nameToId(repo, reference))
|
||||
.then(oid => oid.tostrS())
|
||||
})
|
||||
}
|
||||
|
||||
// Reading Status
|
||||
@@ -460,12 +486,17 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a {Boolean} that's true if the `path`
|
||||
// is ignored.
|
||||
isPathIgnored (_path) {
|
||||
return Promise.all([this.getRepo(), this.getWorkingDirectory()])
|
||||
.then(([repo, wd]) => {
|
||||
const relativePath = this.relativize(_path, wd)
|
||||
return Git.Ignore.pathIsIgnored(repo, relativePath)
|
||||
return this.getWorkingDirectory()
|
||||
.then(wd => {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => {
|
||||
const relativePath = this.relativize(_path, wd)
|
||||
return Git.Ignore.pathIsIgnored(repo, relativePath)
|
||||
})
|
||||
.then(ignored => Boolean(ignored))
|
||||
})
|
||||
})
|
||||
.then(ignored => Boolean(ignored))
|
||||
}
|
||||
|
||||
// Get the status of a directory in the repository's working directory.
|
||||
@@ -501,8 +532,8 @@ export default class GitRepositoryAsync {
|
||||
// status bit for the path.
|
||||
refreshStatusForPath (_path) {
|
||||
let relativePath
|
||||
return Promise.all([this.getRepo(), this.getWorkingDirectory()])
|
||||
.then(([repo, wd]) => {
|
||||
return this.getWorkingDirectory()
|
||||
.then(wd => {
|
||||
relativePath = this.relativize(_path, wd)
|
||||
return this._getStatus([relativePath])
|
||||
})
|
||||
@@ -609,34 +640,39 @@ export default class GitRepositoryAsync {
|
||||
// * `added` The {Number} of added lines.
|
||||
// * `deleted` The {Number} of deleted lines.
|
||||
getDiffStats (_path) {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => Promise.all([repo, repo.getHeadCommit()]))
|
||||
.then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree(), this.getWorkingDirectory(_path)]))
|
||||
.then(([repo, tree, wd]) => {
|
||||
const options = new Git.DiffOptions()
|
||||
options.contextLines = 0
|
||||
options.flags = Git.Diff.OPTION.DISABLE_PATHSPEC_MATCH
|
||||
options.pathspec = this.relativize(_path, wd)
|
||||
if (process.platform === 'win32') {
|
||||
// Ignore eol of line differences on windows so that files checked in
|
||||
// as LF don't report every line modified when the text contains CRLF
|
||||
// endings.
|
||||
options.flags |= Git.Diff.OPTION.IGNORE_WHITESPACE_EOL
|
||||
}
|
||||
return Git.Diff.treeToWorkdir(repo, tree, options)
|
||||
})
|
||||
.then(diff => this._getDiffLines(diff))
|
||||
.then(lines => {
|
||||
const stats = {added: 0, deleted: 0}
|
||||
for (const line of lines) {
|
||||
const origin = line.origin()
|
||||
if (origin === Git.Diff.LINE.ADDITION) {
|
||||
stats.added++
|
||||
} else if (origin === Git.Diff.LINE.DELETION) {
|
||||
stats.deleted++
|
||||
}
|
||||
}
|
||||
return stats
|
||||
return this.getWorkingDirectory(_path)
|
||||
.then(wd => {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => Promise.all([repo, repo.getHeadCommit()]))
|
||||
.then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree()]))
|
||||
.then(([repo, tree]) => {
|
||||
const options = new Git.DiffOptions()
|
||||
options.contextLines = 0
|
||||
options.flags = Git.Diff.OPTION.DISABLE_PATHSPEC_MATCH
|
||||
options.pathspec = this.relativize(_path, wd)
|
||||
if (process.platform === 'win32') {
|
||||
// Ignore eol of line differences on windows so that files checked in
|
||||
// as LF don't report every line modified when the text contains CRLF
|
||||
// endings.
|
||||
options.flags |= Git.Diff.OPTION.IGNORE_WHITESPACE_EOL
|
||||
}
|
||||
return Git.Diff.treeToWorkdir(repo, tree, options)
|
||||
})
|
||||
.then(diff => this._getDiffLines(diff))
|
||||
.then(lines => {
|
||||
const stats = {added: 0, deleted: 0}
|
||||
for (const line of lines) {
|
||||
const origin = line.origin()
|
||||
if (origin === Git.Diff.LINE.ADDITION) {
|
||||
stats.added++
|
||||
} else if (origin === Git.Diff.LINE.DELETION) {
|
||||
stats.deleted++
|
||||
}
|
||||
}
|
||||
return stats
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -652,24 +688,29 @@ export default class GitRepositoryAsync {
|
||||
// * `oldLines` The {Number} of lines in the old hunk.
|
||||
// * `newLines` The {Number} of lines in the new hunk
|
||||
getLineDiffs (_path, text) {
|
||||
let relativePath = null
|
||||
return Promise.all([this.getRepo(_path), this.getWorkingDirectory(_path)])
|
||||
.then(([repo, wd]) => {
|
||||
relativePath = this.relativize(_path, wd)
|
||||
return repo.getHeadCommit()
|
||||
})
|
||||
.then(commit => commit.getEntry(relativePath))
|
||||
.then(entry => entry.getBlob())
|
||||
.then(blob => {
|
||||
const options = new Git.DiffOptions()
|
||||
options.contextLines = 0
|
||||
if (process.platform === 'win32') {
|
||||
// Ignore eol of line differences on windows so that files checked in
|
||||
// as LF don't report every line modified when the text contains CRLF
|
||||
// endings.
|
||||
options.flags = Git.Diff.OPTION.IGNORE_WHITESPACE_EOL
|
||||
}
|
||||
return this._diffBlobToBuffer(blob, text, options)
|
||||
return this.getWorkingDirectory(_path)
|
||||
.then(wd => {
|
||||
let relativePath = null
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => {
|
||||
relativePath = this.relativize(_path, wd)
|
||||
return repo.getHeadCommit()
|
||||
})
|
||||
.then(commit => commit.getEntry(relativePath))
|
||||
.then(entry => entry.getBlob())
|
||||
.then(blob => {
|
||||
const options = new Git.DiffOptions()
|
||||
options.contextLines = 0
|
||||
if (process.platform === 'win32') {
|
||||
// Ignore eol of line differences on windows so that files checked in
|
||||
// as LF don't report every line modified when the text contains CRLF
|
||||
// endings.
|
||||
options.flags = Git.Diff.OPTION.IGNORE_WHITESPACE_EOL
|
||||
}
|
||||
return this._diffBlobToBuffer(blob, text, options)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -691,12 +732,17 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} that resolves or rejects depending on whether the
|
||||
// method was successful.
|
||||
checkoutHead (_path) {
|
||||
return Promise.all([this.getRepo(_path), this.getWorkingDirectory(_path)])
|
||||
.then(([repo, wd]) => {
|
||||
const checkoutOptions = new Git.CheckoutOptions()
|
||||
checkoutOptions.paths = [this.relativize(_path, wd)]
|
||||
checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH
|
||||
return Git.Checkout.head(repo, checkoutOptions)
|
||||
return this.getWorkingDirectory(_path)
|
||||
.then(wd => {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo(_path)
|
||||
.then(repo => {
|
||||
const checkoutOptions = new Git.CheckoutOptions()
|
||||
checkoutOptions.paths = [this.relativize(_path, wd)]
|
||||
checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH
|
||||
return Git.Checkout.head(repo, checkoutOptions)
|
||||
})
|
||||
})
|
||||
})
|
||||
.then(() => this.refreshStatusForPath(_path))
|
||||
}
|
||||
@@ -709,17 +755,19 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Promise} that resolves if the method was successful.
|
||||
checkoutReference (reference, create) {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.checkoutBranch(reference))
|
||||
.catch(error => {
|
||||
if (create) {
|
||||
return this._createBranch(reference)
|
||||
.then(_ => this.checkoutReference(reference, false))
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
.then(_ => null)
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.checkoutBranch(reference))
|
||||
})
|
||||
.catch(error => {
|
||||
if (create) {
|
||||
return this._createBranch(reference)
|
||||
.then(_ => this.checkoutReference(reference, false))
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
.then(_ => null)
|
||||
}
|
||||
|
||||
// Private
|
||||
@@ -745,9 +793,11 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a {NodeGit.Ref} reference to the
|
||||
// created branch.
|
||||
_createBranch (name) {
|
||||
return this.getRepo()
|
||||
.then(repo => Promise.all([repo, repo.getHeadCommit()]))
|
||||
.then(([repo, commit]) => repo.createBranch(name, commit))
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => Promise.all([repo, repo.getHeadCommit()]))
|
||||
.then(([repo, commit]) => repo.createBranch(name, commit))
|
||||
})
|
||||
}
|
||||
|
||||
// Get all the hunks in the diff.
|
||||
@@ -804,14 +854,16 @@ export default class GitRepositoryAsync {
|
||||
// Returns a {Promise} which resolves to a {boolean} indicating whether the
|
||||
// branch name changed.
|
||||
_refreshBranch () {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.getCurrentBranch())
|
||||
.then(ref => ref.name())
|
||||
.then(branchName => {
|
||||
const changed = branchName !== this.branch
|
||||
this.branch = branchName
|
||||
return changed
|
||||
})
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => repo.getCurrentBranch())
|
||||
.then(ref => ref.name())
|
||||
.then(branchName => {
|
||||
const changed = branchName !== this.branch
|
||||
this.branch = branchName
|
||||
return changed
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Refresh the cached ahead/behind count with the given branch.
|
||||
@@ -1077,18 +1129,20 @@ export default class GitRepositoryAsync {
|
||||
//
|
||||
// Returns a {Promise} which resolves to an {Array} of {NodeGit.StatusFile}
|
||||
// statuses for the paths.
|
||||
_getStatus (paths, repo) {
|
||||
return this.getRepo()
|
||||
.then(repo => {
|
||||
const opts = {
|
||||
flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS
|
||||
}
|
||||
_getStatus (paths) {
|
||||
return this.repoPool.enqueue(() => {
|
||||
return this.getRepo()
|
||||
.then(repo => {
|
||||
const opts = {
|
||||
flags: Git.Status.OPT.INCLUDE_UNTRACKED | Git.Status.OPT.RECURSE_UNTRACKED_DIRS
|
||||
}
|
||||
|
||||
if (paths) {
|
||||
opts.pathspec = paths
|
||||
}
|
||||
if (paths) {
|
||||
opts.pathspec = paths
|
||||
}
|
||||
|
||||
return repo.getStatusExt(opts)
|
||||
})
|
||||
return repo.getStatusExt(opts)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,12 @@ class GitRepository
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeStatuses: (callback) ->
|
||||
@async.onDidChangeStatuses callback
|
||||
@async.onDidChangeStatuses ->
|
||||
# Defer the callback to the next tick so that we've reset
|
||||
# `@statusesByPath` by the time it's called. Otherwise reads from within
|
||||
# the callback could be inconsistent.
|
||||
# See https://github.com/atom/atom/issues/11396
|
||||
process.nextTick callback
|
||||
|
||||
###
|
||||
Section: Repository Details
|
||||
|
||||
@@ -4,11 +4,12 @@ module.exports = ({blobStore}) ->
|
||||
path = require 'path'
|
||||
require './window'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
|
||||
{ipcRenderer} = require 'electron'
|
||||
{resourcePath, isSpec, devMode, env} = getWindowLoadSettings()
|
||||
|
||||
# Set baseline environment
|
||||
environmentHelpers.normalize({env: env})
|
||||
env = process.env
|
||||
|
||||
# Add application-specific exports to module search path.
|
||||
exportsPath = path.join(resourcePath, 'exports')
|
||||
@@ -25,7 +26,7 @@ module.exports = ({blobStore}) ->
|
||||
applicationDelegate: new ApplicationDelegate,
|
||||
configDirPath: process.env.ATOM_HOME
|
||||
enablePersistence: true
|
||||
env: env
|
||||
env: process.env
|
||||
})
|
||||
|
||||
atom.startEditorWindow().then ->
|
||||
@@ -35,3 +36,6 @@ module.exports = ({blobStore}) ->
|
||||
window.removeEventListener('focus', windowFocused)
|
||||
setTimeout (-> document.querySelector('atom-workspace').focus()), 0
|
||||
window.addEventListener('focus', windowFocused)
|
||||
ipcRenderer.on('environment', (event, env) ->
|
||||
environmentHelpers.replace(env)
|
||||
)
|
||||
|
||||
@@ -367,6 +367,8 @@ class PackageManager
|
||||
@emitter.emit 'did-load-initial-packages'
|
||||
|
||||
loadPackage: (nameOrPath) ->
|
||||
return null if path.basename(nameOrPath)[0].match /^\./ # primarily to skip .git folder
|
||||
|
||||
return pack if pack = @getLoadedPackage(nameOrPath)
|
||||
|
||||
if packagePath = @resolvePackagePath(nameOrPath)
|
||||
|
||||
@@ -442,15 +442,16 @@ class Pane extends Model
|
||||
if typeof item.onDidTerminatePendingState is "function"
|
||||
itemSubscriptions.add item.onDidTerminatePendingState =>
|
||||
@clearPendingItem() if @getPendingItem() is item
|
||||
itemSubscriptions.add item.onDidDestroy => @removeItem(item, false)
|
||||
@subscriptionsPerItem.set item, itemSubscriptions
|
||||
|
||||
@items.splice(index, 0, item)
|
||||
lastPendingItem = @getPendingItem()
|
||||
replacingPendingItem = lastPendingItem? and not moved
|
||||
@pendingItem = null if replacingPendingItem
|
||||
@setPendingItem(item) if pending
|
||||
|
||||
@emitter.emit 'did-add-item', {item, index, moved}
|
||||
@destroyItem(lastPendingItem) if lastPendingItem? and not moved
|
||||
@destroyItem(lastPendingItem) if replacingPendingItem
|
||||
@setActiveItem(item) unless @getActiveItem()?
|
||||
item
|
||||
|
||||
@@ -458,7 +459,8 @@ class Pane extends Model
|
||||
if @pendingItem isnt item
|
||||
mostRecentPendingItem = @pendingItem
|
||||
@pendingItem = item
|
||||
@emitter.emit 'item-did-terminate-pending-state', mostRecentPendingItem
|
||||
if mostRecentPendingItem?
|
||||
@emitter.emit 'item-did-terminate-pending-state', mostRecentPendingItem
|
||||
|
||||
getPendingItem: =>
|
||||
@pendingItem or null
|
||||
|
||||
@@ -207,6 +207,8 @@ module.exports = ({commandRegistry, commandInstaller, config}) ->
|
||||
'editor:checkout-head-revision': -> @checkoutHeadRevision()
|
||||
'editor:move-line-up': -> @moveLineUp()
|
||||
'editor:move-line-down': -> @moveLineDown()
|
||||
'editor:move-selection-left': -> @moveSelectionLeft()
|
||||
'editor:move-selection-right': -> @moveSelectionRight()
|
||||
'editor:duplicate-lines': -> @duplicateLines()
|
||||
'editor:join-lines': -> @joinLines()
|
||||
)
|
||||
|
||||
57
src/resource-pool.js
Normal file
57
src/resource-pool.js
Normal file
@@ -0,0 +1,57 @@
|
||||
/** @babel */
|
||||
|
||||
// Manages a pool of some resource.
|
||||
export default class ResourcePool {
|
||||
constructor (pool) {
|
||||
this.pool = pool
|
||||
|
||||
this.queue = []
|
||||
}
|
||||
|
||||
// Enqueue the given function. The function will be given an object from the
|
||||
// pool. The function must return a {Promise}.
|
||||
enqueue (fn) {
|
||||
let resolve = null
|
||||
let reject = null
|
||||
const wrapperPromise = new Promise((resolve_, reject_) => {
|
||||
resolve = resolve_
|
||||
reject = reject_
|
||||
})
|
||||
|
||||
this.queue.push(this.wrapFunction(fn, resolve, reject))
|
||||
|
||||
this.dequeueIfAble()
|
||||
|
||||
return wrapperPromise
|
||||
}
|
||||
|
||||
wrapFunction (fn, resolve, reject) {
|
||||
return (resource) => {
|
||||
const promise = fn(resource)
|
||||
promise
|
||||
.then(result => {
|
||||
resolve(result)
|
||||
this.taskDidComplete(resource)
|
||||
}, error => {
|
||||
reject(error)
|
||||
this.taskDidComplete(resource)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
taskDidComplete (resource) {
|
||||
this.pool.push(resource)
|
||||
|
||||
this.dequeueIfAble()
|
||||
}
|
||||
|
||||
dequeueIfAble () {
|
||||
if (!this.pool.length || !this.queue.length) return
|
||||
|
||||
const fn = this.queue.shift()
|
||||
const resource = this.pool.shift()
|
||||
fn(resource)
|
||||
}
|
||||
|
||||
getQueueDepth () { return this.queue.length }
|
||||
}
|
||||
@@ -378,7 +378,8 @@ class Selection extends Model
|
||||
indentAdjustment = @editor.indentLevelForLine(precedingText) - options.indentBasis
|
||||
@adjustIndent(remainingLines, indentAdjustment)
|
||||
|
||||
if options.autoIndent and NonWhitespaceRegExp.test(text) and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0
|
||||
textIsAutoIndentable = text is '\n' or text is '\r\n' or NonWhitespaceRegExp.test(text)
|
||||
if options.autoIndent and textIsAutoIndentable and not NonWhitespaceRegExp.test(precedingText) and remainingLines.length > 0
|
||||
autoIndentFirstLine = true
|
||||
firstLine = precedingText + firstInsertedLine
|
||||
desiredIndentLevel = @editor.languageMode.suggestedIndentForLineAtBufferRow(oldBufferRange.start.row, firstLine)
|
||||
|
||||
@@ -247,9 +247,50 @@ class TextEditorComponent
|
||||
@scrollViewNode.addEventListener 'mousedown', @onMouseDown
|
||||
@scrollViewNode.addEventListener 'scroll', @onScrollViewScroll
|
||||
|
||||
@detectAccentedCharacterMenu()
|
||||
@listenForIMEEvents()
|
||||
@trackSelectionClipboard() if process.platform is 'linux'
|
||||
|
||||
detectAccentedCharacterMenu: ->
|
||||
# We need to get clever to detect when the accented character menu is
|
||||
# opened on OS X. Usually, every keydown event that could cause input is
|
||||
# followed by a corresponding keypress. However, pressing and holding
|
||||
# long enough to open the accented character menu causes additional keydown
|
||||
# events to fire that aren't followed by their own keypress and textInput
|
||||
# events.
|
||||
#
|
||||
# Therefore, we assume the accented character menu has been deployed if,
|
||||
# before observing any keyup event, we observe events in the following
|
||||
# sequence:
|
||||
#
|
||||
# keydown(keyCode: X), keypress, keydown(keyCode: X)
|
||||
#
|
||||
# The keyCode X must be the same in the keydown events that bracket the
|
||||
# keypress, meaning we're *holding* the _same_ key we intially pressed.
|
||||
# Got that?
|
||||
lastKeydown = null
|
||||
lastKeydownBeforeKeypress = null
|
||||
|
||||
@domNode.addEventListener 'keydown', (event) =>
|
||||
if lastKeydownBeforeKeypress
|
||||
if lastKeydownBeforeKeypress.keyCode is event.keyCode
|
||||
@openedAccentedCharacterMenu = true
|
||||
lastKeydownBeforeKeypress = null
|
||||
else
|
||||
lastKeydown = event
|
||||
|
||||
@domNode.addEventListener 'keypress', =>
|
||||
lastKeydownBeforeKeypress = lastKeydown
|
||||
lastKeydown = null
|
||||
|
||||
# This cancels the accented character behavior if we type a key normally
|
||||
# with the menu open.
|
||||
@openedAccentedCharacterMenu = false
|
||||
|
||||
@domNode.addEventListener 'keyup', ->
|
||||
lastKeydownBeforeKeypress = null
|
||||
lastKeydown = null
|
||||
|
||||
listenForIMEEvents: ->
|
||||
# The IME composition events work like this:
|
||||
#
|
||||
@@ -266,6 +307,9 @@ class TextEditorComponent
|
||||
|
||||
checkpoint = null
|
||||
@domNode.addEventListener 'compositionstart', =>
|
||||
if @openedAccentedCharacterMenu
|
||||
@editor.selectLeft()
|
||||
@openedAccentedCharacterMenu = false
|
||||
checkpoint = @editor.createCheckpoint()
|
||||
@domNode.addEventListener 'compositionupdate', (event) =>
|
||||
@editor.insertText(event.data, select: true)
|
||||
@@ -321,24 +365,21 @@ class TextEditorComponent
|
||||
|
||||
onTextInput: (event) =>
|
||||
event.stopPropagation()
|
||||
|
||||
# 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 ' '
|
||||
event.preventDefault()
|
||||
|
||||
return unless @isInputEnabled()
|
||||
|
||||
inputNode = event.target
|
||||
# Workaround of the accented character suggestion feature in OS X.
|
||||
# This will only occur when the user is not composing in IME mode.
|
||||
# When the user selects a modified character from the OSX menu, `textInput`
|
||||
# will occur twice, once for the initial character, and once for the
|
||||
# modified character. However, only a single keypress will have fired. If
|
||||
# this is the case, select backward to replace the original character.
|
||||
if @openedAccentedCharacterMenu
|
||||
@editor.selectLeft()
|
||||
@openedAccentedCharacterMenu = false
|
||||
|
||||
# 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
|
||||
|
||||
insertedRange = @editor.insertText(event.data, groupUndo: true)
|
||||
inputNode.value = event.data if insertedRange
|
||||
@editor.insertText(event.data, groupUndo: true)
|
||||
|
||||
onVerticalScroll: (scrollTop) =>
|
||||
return if @updateRequested or scrollTop is @presenter.getScrollTop()
|
||||
|
||||
@@ -1074,6 +1074,50 @@ class TextEditor extends Model
|
||||
@autoIndentSelectedRows() if @shouldAutoIndent()
|
||||
@scrollToBufferPosition([newSelectionRanges[0].start.row - 1, 0])
|
||||
|
||||
# Move any active selections one column to the left.
|
||||
moveSelectionLeft: ->
|
||||
selections = @getSelectedBufferRanges()
|
||||
noSelectionAtStartOfLine = selections.every((selection) ->
|
||||
selection.start.column isnt 0
|
||||
)
|
||||
|
||||
translationDelta = [0, -1]
|
||||
translatedRanges = []
|
||||
|
||||
if noSelectionAtStartOfLine
|
||||
@transact =>
|
||||
for selection in selections
|
||||
charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start)
|
||||
charTextToLeftOfSelection = @buffer.getTextInRange(charToLeftOfSelection)
|
||||
|
||||
@buffer.insert(selection.end, charTextToLeftOfSelection)
|
||||
@buffer.delete(charToLeftOfSelection)
|
||||
translatedRanges.push(selection.translate(translationDelta))
|
||||
|
||||
@setSelectedBufferRanges(translatedRanges)
|
||||
|
||||
# Move any active selections one column to the right.
|
||||
moveSelectionRight: ->
|
||||
selections = @getSelectedBufferRanges()
|
||||
noSelectionAtEndOfLine = selections.every((selection) =>
|
||||
selection.end.column isnt @buffer.lineLengthForRow(selection.end.row)
|
||||
)
|
||||
|
||||
translationDelta = [0, 1]
|
||||
translatedRanges = []
|
||||
|
||||
if noSelectionAtEndOfLine
|
||||
@transact =>
|
||||
for selection in selections
|
||||
charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta))
|
||||
charTextToRightOfSelection = @buffer.getTextInRange(charToRightOfSelection)
|
||||
|
||||
@buffer.delete(charToRightOfSelection)
|
||||
@buffer.insert(selection.start, charTextToRightOfSelection)
|
||||
translatedRanges.push(selection.translate(translationDelta))
|
||||
|
||||
@setSelectedBufferRanges(translatedRanges)
|
||||
|
||||
# Duplicate the most recent cursor's current line.
|
||||
duplicateLines: ->
|
||||
@transact =>
|
||||
@@ -1507,7 +1551,7 @@ class TextEditor extends Model
|
||||
#
|
||||
# * `markerLayer` A {TextEditorMarkerLayer} or {MarkerLayer} to decorate.
|
||||
# * `decorationParams` The same parameters that are passed to
|
||||
# {decorateMarker}, except the `type` cannot be `overlay` or `gutter`.
|
||||
# {TextEditor::decorateMarker}, except the `type` cannot be `overlay` or `gutter`.
|
||||
#
|
||||
# This API is experimental and subject to change on any release.
|
||||
#
|
||||
|
||||
@@ -44,10 +44,15 @@ class WorkspaceElement extends HTMLElement
|
||||
@subscriptions.add @config.onDidChange 'editor.lineHeight', @updateGlobalTextEditorStyleSheet.bind(this)
|
||||
|
||||
updateGlobalTextEditorStyleSheet: ->
|
||||
fontFamily = @config.get('editor.fontFamily')
|
||||
# TODO: There is a bug in how some emojis (e.g. ❤️) are rendered on OSX.
|
||||
# This workaround should be removed once we update to Chromium 51, where the
|
||||
# problem was fixed.
|
||||
fontFamily += ', "Apple Color Emoji"' if process.platform is 'darwin'
|
||||
styleSheetSource = """
|
||||
atom-text-editor {
|
||||
font-size: #{@config.get('editor.fontSize')}px;
|
||||
font-family: #{@config.get('editor.fontFamily')};
|
||||
font-family: #{fontFamily};
|
||||
line-height: #{@config.get('editor.lineHeight')};
|
||||
}
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user