mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Merge branch 'master' into sm-hidden-all
This commit is contained in:
@@ -49,3 +49,5 @@ addons:
|
||||
- git
|
||||
- libgnome-keyring-dev
|
||||
- rpm
|
||||
- libx11-dev
|
||||
- libxkbfile-dev
|
||||
|
||||
@@ -84,7 +84,7 @@ When we make a significant decision in how we maintain the project and what we c
|
||||
|
||||
This section guides you through submitting a bug report for Atom. Following these guidelines helps maintainers and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:.
|
||||
|
||||
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). If you'd like, you can use [this template](#template-for-submitting-bug-reports) to structure the information.
|
||||
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](ISSUE_TEMPLATE.md), the information it asks for helps us resolve issues faster.
|
||||
|
||||
#### Before Submitting A Bug Report
|
||||
|
||||
@@ -95,7 +95,7 @@ Before creating bug reports, please check [this list](#before-submitting-a-bug-r
|
||||
|
||||
#### How Do I Submit A (Good) Bug Report?
|
||||
|
||||
Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined [which repository](#atom-and-packages) your bug is related to, create an issue on that repository and provide the following information.
|
||||
Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined [which repository](#atom-and-packages) your bug is related to, create an issue on that repository and provide the following information by filling in [the template](ISSUE_TEMPLATE.md).
|
||||
|
||||
Explain the problem and include additional details to help maintainers reproduce the problem:
|
||||
|
||||
@@ -128,47 +128,11 @@ Include details about your configuration and environment:
|
||||
* **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?
|
||||
|
||||
#### Template For Submitting Bug Reports
|
||||
|
||||
[Short description of problem here]
|
||||
|
||||
**Reproduction Steps:**
|
||||
|
||||
1. [First Step]
|
||||
2. [Second Step]
|
||||
3. [Other Steps...]
|
||||
|
||||
**Expected behavior:**
|
||||
|
||||
[Describe expected behavior here]
|
||||
|
||||
**Observed behavior:**
|
||||
|
||||
[Describe observed behavior here]
|
||||
|
||||
**Screenshots and GIFs**
|
||||
|
||||

|
||||
|
||||
**Atom version:** [Enter Atom version here]
|
||||
**OS and version:** [Enter OS name and version here]
|
||||
|
||||
**Installed packages:**
|
||||
|
||||
[List of installed packages here]
|
||||
|
||||
**Additional information:**
|
||||
|
||||
* Problem can be reproduced in safe mode: [Yes/No]
|
||||
* Problem started happening recently, didn't happen in an older version of Atom: [Yes/No]
|
||||
* Problem can be reliably reproduced, doesn't happen randomly: [Yes/No]
|
||||
* Problem happens with all files and projects, not only some files or projects: [Yes/No]
|
||||
|
||||
### Suggesting Enhancements
|
||||
|
||||
This section guides you through submitting an enhancement suggestion for Atom, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion :pencil: and find related suggestions :mag_right:.
|
||||
|
||||
Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). If you'd like, you can use [this template](#template-for-submitting-enhancement-suggestions) to structure the information.
|
||||
Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). Fill in [the template](ISSUE_TEMPLATE.md), including the steps that you imagine you would take if the feature you're requesting existed.
|
||||
|
||||
#### Before Submitting An Enhancement Suggestion
|
||||
|
||||
@@ -191,33 +155,6 @@ Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com
|
||||
* **Specify which version of Atom you're using.** You can get the exact version by running `atom -v` in your terminal, or by starting Atom and running the `Application: About` command from the [Command Palette](https://github.com/atom/command-palette).
|
||||
* **Specify the name and version of the OS you're using.**
|
||||
|
||||
#### Template For Submitting Enhancement Suggestions
|
||||
|
||||
[Short description of suggestion]
|
||||
|
||||
**Steps which explain the enhancement**
|
||||
|
||||
1. [First Step]
|
||||
2. [Second Step]
|
||||
3. [Other Steps...]
|
||||
|
||||
**Current and suggested behavior**
|
||||
|
||||
[Describe current and suggested behavior here]
|
||||
|
||||
**Why would the enhancement be useful to most users**
|
||||
|
||||
[Explain why the enhancement would be useful to most users]
|
||||
|
||||
[List some other text editors or applications where this enhancement exists]
|
||||
|
||||
**Screenshots and GIFs**
|
||||
|
||||

|
||||
|
||||
**Atom Version:** [Enter Atom version here]
|
||||
**OS and Version:** [Enter OS name and version here]
|
||||
|
||||
### Your First Code Contribution
|
||||
|
||||
Unsure where to begin contributing to Atom? You can start by looking through these `beginner` and `help-wanted` issues:
|
||||
@@ -231,6 +168,7 @@ If you want to read about using Atom or developing packages in Atom, the [Atom F
|
||||
|
||||
### Pull Requests
|
||||
|
||||
* Fill in [the required template](PULL_REQUEST_TEMPLATE.md)
|
||||
* Include screenshots and animated GIFs in your pull request whenever possible.
|
||||
* Follow the [JavaScript](#javascript-styleguide) and [CoffeeScript](#coffeescript-styleguide) styleguides.
|
||||
* Include thoughtfully-worded, well-structured
|
||||
@@ -240,16 +178,12 @@ If you want to read about using Atom or developing packages in Atom, the [Atom F
|
||||
* End files with a newline.
|
||||
* Place requires in the following order:
|
||||
* Built in Node Modules (such as `path`)
|
||||
* Built in Atom and Atom Shell Modules (such as `atom`, `shell`)
|
||||
* Built in Atom and Electron Modules (such as `atom`, `remote`)
|
||||
* Local Modules (using relative paths)
|
||||
* Place class properties in the following order:
|
||||
* Class methods and properties (methods starting with a `@`)
|
||||
* Instance methods and properties
|
||||
* Avoid platform-dependent code:
|
||||
* Use `require('fs-plus').getHomeDirectory()` to get the home directory.
|
||||
* Use `path.join()` to concatenate filenames.
|
||||
* Use `os.tmpdir()` rather than `/tmp` when you need to reference the
|
||||
temporary directory.
|
||||
* [Avoid platform-dependent code](http://flight-manual.atom.io/hacking-atom/sections/cross-platform-compatibility/)
|
||||
* Using a plain `return` when returning explicitly at the end of a function.
|
||||
* Not `return null`, `return undefined`, `null`, or `undefined`
|
||||
|
||||
@@ -427,12 +361,6 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and
|
||||
| `deprecation-help` | [search][search-atom-repo-label-deprecation-help] | [search][search-atom-org-label-deprecation-help] | Issues for helping package authors remove usage of deprecated APIs in packages. |
|
||||
| `electron` | [search][search-atom-repo-label-electron] | [search][search-atom-org-label-electron] | Issues that require changes to [Electron](https://electron.atom.io) to fix or implement. |
|
||||
|
||||
#### Core Team Project Management
|
||||
|
||||
| Label name | `atom/atom` :mag_right: | `atom`‑org :mag_right: | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `atom` | [search][search-atom-repo-label-atom] | [search][search-atom-org-label-atom] | Topics discussed for prioritization at the next meeting of Atom core team members. |
|
||||
|
||||
#### Pull Request Labels
|
||||
|
||||
| Label name | `atom/atom` :mag_right: | `atom`‑org :mag_right: | Description
|
||||
@@ -519,8 +447,6 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and
|
||||
[search-atom-org-label-deprecation-help]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Adeprecation-help
|
||||
[search-atom-repo-label-electron]: https://github.com/issues?q=is%3Aissue+repo%3Aatom%2Fatom+is%3Aopen+label%3Aelectron
|
||||
[search-atom-org-label-electron]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aelectron
|
||||
[search-atom-repo-label-atom]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aatom
|
||||
[search-atom-org-label-atom]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aatom
|
||||
[search-atom-repo-label-work-in-progress]: https://github.com/pulls?q=is%3Aopen+is%3Apr+repo%3Aatom%2Fatom+label%3Awork-in-progress
|
||||
[search-atom-org-label-work-in-progress]: https://github.com/pulls?q=is%3Aopen+is%3Apr+user%3Aatom+label%3Awork-in-progress
|
||||
[search-atom-repo-label-needs-review]: https://github.com/pulls?q=is%3Aopen+is%3Apr+repo%3Aatom%2Fatom+label%3Aneeds-review
|
||||
@@ -533,4 +459,4 @@ Please open an issue on `atom/atom` if you have suggestions for new labels, and
|
||||
[search-atom-org-label-needs-testing]: https://github.com/pulls?q=is%3Aopen+is%3Apr+user%3Aatom+label%3Aneeds-testing
|
||||
|
||||
[beginner]:https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Abeginner+label%3Ahelp-wanted+user%3Aatom+sort%3Acomments-desc
|
||||
[help-wanted]:https://github.com/issues?q=is%3Aopen+is%3Aissue+label%3Ahelp-wanted+user%3Aatom+sort%3Acomments-desc
|
||||
[help-wanted]:https://github.com/issues?q=is%3Aopen+is%3Aissue+label%3Ahelp-wanted+user%3Aatom+sort%3Acomments-desc+-label%3Abeginner
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
<!--
|
||||
|
||||
Have you read Atom's Code of Conduct? By filing an Issue, you are expected to comply with it, including treating everyone with respect: https://github.com/atom/atom/blob/master/CODE_OF_CONDUCT.md
|
||||
|
||||
Do you want to ask a question? Are you looking for support? The Atom message board is the best place for getting support: https://discuss.atom.io
|
||||
|
||||
-->
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* [ ] Can you reproduce the problem in [safe mode](http://flight-manual.atom.io/hacking-atom/sections/debugging/#using-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](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?
|
||||
|
||||
For more information on how to write a good [bug report](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-bug-report) or [enhancement request](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#how-do-i-submit-a-good-enhancement-suggestion), see the `CONTRIBUTING` guide.
|
||||
* [ ] Put an X between the brackets on this line if you have done all of the following:
|
||||
* Reproduced the problem in Safe Mode: http://flight-manual.atom.io/hacking-atom/sections/debugging/#using-safe-mode
|
||||
* Followed all applicable steps in the debugging guide: http://flight-manual.atom.io/hacking-atom/sections/debugging/
|
||||
* Checked the FAQs on the message board for common solutions: https://discuss.atom.io/c/faq
|
||||
* Checked that your issue isn't already filed: https://github.com/issues?utf8=✓&q=is%3Aissue+user%3Aatom
|
||||
* Checked that there is not already an Atom package that provides the described functionality: https://atom.io/packages
|
||||
|
||||
### Description
|
||||
|
||||
[Description of the bug or feature]
|
||||
[Description of the issue]
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
@@ -19,10 +25,16 @@ For more information on how to write a good [bug report](https://github.com/atom
|
||||
2. [Second Step]
|
||||
3. [and so on...]
|
||||
|
||||
**Expected behavior:** [What you expected to happen]
|
||||
**Expected behavior:** [What you expect to happen]
|
||||
|
||||
**Actual behavior:** [What actually happened]
|
||||
**Actual behavior:** [What actually happens]
|
||||
|
||||
**Reproduces how often:** [What percentage of the time does it reproduce?]
|
||||
|
||||
### Versions
|
||||
|
||||
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.
|
||||
You can get this information from copy and pasting the output of `atom --version` and `apm --version` from the command line. Also, please include the OS and what version of the OS you're running.
|
||||
|
||||
### Additional Information
|
||||
|
||||
Any additional information, configuration or data that might be necessary to reproduce the issue.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2014 GitHub Inc.
|
||||
Copyright (c) 2011-2017 GitHub Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
32
PULL_REQUEST_TEMPLATE.md
Normal file
32
PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,32 @@
|
||||
### Requirements
|
||||
|
||||
* Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
|
||||
* All new code requires tests to ensure against regressions
|
||||
|
||||
### Description of the Change
|
||||
|
||||
<!--
|
||||
|
||||
We must be able to understand the design of your change from this description. If we can't get a good idea of what the code will be doing from the description here, the pull request may be closed at the maintainers' discretion. Keep in mind that the maintainer reviewing this PR may not be familiar with or have worked with the code here recently, so please walk us through the concepts.
|
||||
|
||||
-->
|
||||
|
||||
### Alternate Designs
|
||||
|
||||
<!-- Explain what other alternates were considered and why the proposed version was selected -->
|
||||
|
||||
### Why Should This Be In Core?
|
||||
|
||||
<!-- Explain why this functionality should be in atom/atom as opposed to a package -->
|
||||
|
||||
### Benefits
|
||||
|
||||
<!-- What benefits will be realized by the code change? -->
|
||||
|
||||
### Possible Drawbacks
|
||||
|
||||
<!-- What are the possible side-effects or negative impacts of the code change? -->
|
||||
|
||||
### Applicable Issues
|
||||
|
||||
<!-- Enter any applicable Issues here -->
|
||||
15
README.md
15
README.md
@@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
[](https://circleci.com/gh/atom/atom) [](https://travis-ci.org/atom/atom) [](https://ci.appveyor.com/project/Atom/atom)
|
||||
[](https://circleci.com/gh/atom/atom) [](https://travis-ci.org/atom/atom) [](https://ci.appveyor.com/project/Atom/atom)
|
||||
[](https://david-dm.org/atom/atom)
|
||||
[](http://atom-slack.herokuapp.com/)
|
||||
|
||||
@@ -33,15 +33,14 @@ Atom will automatically update when a new release is available.
|
||||
|
||||
### Windows
|
||||
|
||||
Download the latest [AtomSetup.exe installer](https://github.com/atom/atom/releases/latest).
|
||||
Download the latest [Atom installer](https://github.com/atom/atom/releases/latest). AtomSetup.exe is 32-bit, AtomSetup-x64.exe for 64-bit systems.
|
||||
|
||||
Atom will automatically update when a new release is available.
|
||||
|
||||
You can also download an `atom-windows.zip` file from the [releases page](https://github.com/atom/atom/releases/latest).
|
||||
You can also download `atom-windows.zip` (32-bit) or `atom-x64-windows.zip` (64-bit) from the [releases page](https://github.com/atom/atom/releases/latest).
|
||||
The `.zip` version will not automatically update.
|
||||
|
||||
Using [chocolatey](https://chocolatey.org/)? Run `cinst Atom` to install
|
||||
the latest version of Atom.
|
||||
Using [chocolatey](https://chocolatey.org/)? Run `cinst Atom` to install the latest version of Atom.
|
||||
|
||||
### Debian Linux (Ubuntu)
|
||||
|
||||
@@ -97,3 +96,9 @@ repeat these steps to upgrade to future releases.
|
||||
* [macOS](./docs/build-instructions/macos.md)
|
||||
* [FreeBSD](./docs/build-instructions/freebsd.md)
|
||||
* [Windows](./docs/build-instructions/windows.md)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/atom/atom/blob/master/LICENSE.md)
|
||||
|
||||
When using the Atom or other GitHub logos, be sure to follow the [GitHub logo guidelines](https://github.com/logos).
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "1.13.0"
|
||||
"atom-package-manager": "1.15.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
image: Visual Studio 2015
|
||||
|
||||
version: "{build}"
|
||||
|
||||
skip_tags: true
|
||||
@@ -6,13 +8,14 @@ clone_depth: 10
|
||||
|
||||
platform:
|
||||
- x86
|
||||
- x64
|
||||
|
||||
environment:
|
||||
global:
|
||||
ATOM_DEV_RESOURCE_PATH: c:\projects\atom
|
||||
|
||||
matrix:
|
||||
- NODE_VERSION: 4.4.5
|
||||
- NODE_VERSION: 6.8.0
|
||||
|
||||
install:
|
||||
- SET PATH=C:\Program Files\Atom\resources\cli;%PATH%
|
||||
@@ -47,6 +50,4 @@ cache:
|
||||
- '%APPVEYOR_BUILD_FOLDER%\node_modules'
|
||||
- '%APPVEYOR_BUILD_FOLDER%\electron'
|
||||
- '%USERPROFILE%\.atom\.apm'
|
||||
- '%USERPROFILE%\.atom\.node-gyp\.atom'
|
||||
- '%USERPROFILE%\.atom\.npm'
|
||||
- '%USERPROFILE%\.atom\compile-cache'
|
||||
|
||||
21
atom.sh
21
atom.sh
@@ -55,27 +55,38 @@ if [ $EXPECT_OUTPUT ]; then
|
||||
fi
|
||||
|
||||
if [ $OS == 'Mac' ]; then
|
||||
if [ -L "$0" ]; then
|
||||
SCRIPT="$(readlink "$0")"
|
||||
else
|
||||
SCRIPT="$0"
|
||||
fi
|
||||
ATOM_APP="$(dirname "$(dirname "$(dirname "$(dirname "$SCRIPT")")")")"
|
||||
if [ "$ATOM_APP" == . ]; then
|
||||
unset ATOM_APP
|
||||
else
|
||||
ATOM_PATH="$(dirname "$ATOM_APP")"
|
||||
ATOM_APP_NAME="$(basename "$ATOM_APP")"
|
||||
fi
|
||||
|
||||
if [ -n "$BETA_VERSION" ]; then
|
||||
ATOM_APP_NAME="Atom Beta.app"
|
||||
ATOM_EXECUTABLE_NAME="Atom Beta"
|
||||
else
|
||||
ATOM_APP_NAME="Atom.app"
|
||||
ATOM_EXECUTABLE_NAME="Atom"
|
||||
fi
|
||||
|
||||
if [ -z "${ATOM_PATH}" ]; then
|
||||
# If ATOM_PATH isnt set, check /Applications and then ~/Applications for Atom.app
|
||||
# If ATOM_PATH isn't set, check /Applications and then ~/Applications for Atom.app
|
||||
if [ -x "/Applications/$ATOM_APP_NAME" ]; then
|
||||
ATOM_PATH="/Applications"
|
||||
elif [ -x "$HOME/Applications/$ATOM_APP_NAME" ]; then
|
||||
ATOM_PATH="$HOME/Applications"
|
||||
else
|
||||
# We havent found an Atom.app, use spotlight to search for Atom
|
||||
# We haven't found an Atom.app, use spotlight to search for Atom
|
||||
ATOM_PATH="$(mdfind "kMDItemCFBundleIdentifier == 'com.github.atom'" | grep -v ShipIt | head -1 | xargs -0 dirname)"
|
||||
|
||||
# Exit if Atom can't be found
|
||||
if [ ! -x "$ATOM_PATH/$ATOM_APP_NAME" ]; then
|
||||
echo "Cannot locate Atom.app, it is usually located in /Applications. Set the ATOM_PATH environment variable to the directory containing Atom.app."
|
||||
echo "Cannot locate ${ATOM_APP_NAME}, it is usually located in /Applications. Set the ATOM_PATH environment variable to the directory containing ${ATOM_APP_NAME}."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,112 +1,114 @@
|
||||
# Atom build status
|
||||
|
||||
| System | macOS | Windows | Dependencies |
|
||||
|--------|------|---------|--------------|
|
||||
| Atom | [](https://travis-ci.org/atom/atom) | [](https://ci.appveyor.com/project/Atom/atom) | [](https://david-dm.org/atom/atom) |
|
||||
| APM | [](https://travis-ci.org/atom/apm) | [](https://ci.appveyor.com/project/Atom/apm/branch/master) | [](https://david-dm.org/atom/apm) |
|
||||
| Electron | [](https://travis-ci.org/electron/electron) | [](https://ci.appveyor.com/project/Atom/electron) | [](https://david-dm.org/electron/electron)
|
||||
| System | Travis | AppVeyor/Win | Circle/Mac | Dependencies |
|
||||
|--------|--------|--------------|------------|--------------|
|
||||
| [Atom](https://github.com/atom/atom) | [](https://travis-ci.org/atom/atom) | [](https://ci.appveyor.com/project/Atom/atom) | [](https://circleci.com/gh/atom/atom) | [](https://david-dm.org/atom/atom) |
|
||||
| [APM](https://github.com/atom/apm) | [](https://travis-ci.org/atom/apm) | [](https://ci.appveyor.com/project/Atom/apm/branch/master) | | [](https://david-dm.org/atom/apm) |
|
||||
| [Electron](https://github.com/electron/electron) | [](https://travis-ci.org/electron/electron) | [](https://ci.appveyor.com/project/Atom/electron) | | [](https://david-dm.org/electron/electron)
|
||||
|
||||
## Packages
|
||||
|
||||
| Package | macOS | Windows | Dependencies |
|
||||
|---------|------|---------|--------------|
|
||||
| About | [](https://travis-ci.org/atom/about) | [](https://ci.appveyor.com/project/atom/about/branch/master) | [](https://david-dm.org/atom/about) |
|
||||
| Archive View | [](https://travis-ci.org/atom/archive-view) | [](https://ci.appveyor.com/project/Atom/archive-view/branch/master) | [](https://david-dm.org/atom/archive-view) |
|
||||
| AutoComplete Atom API | [](https://travis-ci.org/atom/autocomplete-atom-api) | [](https://ci.appveyor.com/project/Atom/autocomplete-atom-api/branch/master) | [](https://david-dm.org/atom/autocomplete-atom-api) |
|
||||
| Atom Space Pen Views | [](https://travis-ci.org/atom/atom-space-pen-views) | [](https://ci.appveyor.com/project/Atom/atom-space-pen-views/branch/master) | [](https://david-dm.org/atom/atom-space-pen-views) |
|
||||
| AutoComplete CSS | [](https://travis-ci.org/atom/autocomplete-css) | [](https://ci.appveyor.com/project/Atom/autocomplete-css/branch/master) | [](https://david-dm.org/atom/autocomplete-css) |
|
||||
| AutoComplete HTML | [](https://travis-ci.org/atom/autocomplete-html) | [](https://ci.appveyor.com/project/Atom/autocomplete-html/branch/master) | [](https://david-dm.org/atom/autocomplete-html) |
|
||||
| AutoComplete+ | [](https://travis-ci.org/atom/autocomplete-plus) | [](https://ci.appveyor.com/project/Atom/autocomplete-plus/branch/master) | [](https://david-dm.org/atom/autocomplete-plus) |
|
||||
| AutoComplete Snippets | [](https://travis-ci.org/atom/autocomplete-snippets) | [](https://ci.appveyor.com/project/Atom/autocomplete-snippets/branch/master) | [](https://david-dm.org/atom/autocomplete-snippets) |
|
||||
| AutoFlow | [](https://travis-ci.org/atom/autoflow) | [](https://ci.appveyor.com/project/Atom/autoflow/branch/master) | [](https://david-dm.org/atom/autoflow) |
|
||||
| AutoSave | [](https://travis-ci.org/atom/autosave) | [](https://ci.appveyor.com/project/Atom/autosave/branch/master) | [](https://david-dm.org/atom/autosave) |
|
||||
| Background Tips | [](https://travis-ci.org/atom/background-tips) | [](https://ci.appveyor.com/project/Atom/background-tips/branch/master) | [](https://david-dm.org/atom/background-tips) |
|
||||
| Bookmarks | [](https://travis-ci.org/atom/bookmarks) | [](https://ci.appveyor.com/project/Atom/bookmarks/branch/master) | [](https://david-dm.org/atom/bookmarks) |
|
||||
| Bracket Matcher | [](https://travis-ci.org/atom/bracket-matcher) | [](https://ci.appveyor.com/project/Atom/bracket-matcher/branch/master) | [](https://david-dm.org/atom/bracket-matcher) |
|
||||
| Command Palette | [](https://travis-ci.org/atom/command-palette) | [](https://ci.appveyor.com/project/Atom/command-palette/branch/master) | [](https://david-dm.org/atom/command-palette) |
|
||||
| Deprecation Cop | [](https://travis-ci.org/atom/deprecation-cop) | [](https://ci.appveyor.com/project/Atom/deprecation-cop/branch/master) | [](https://david-dm.org/atom/deprecation-cop) |
|
||||
| Dev Live Reload | [](https://travis-ci.org/atom/dev-live-reload) | [](https://ci.appveyor.com/project/Atom/dev-live-reload/branch/master) | [](https://david-dm.org/atom/dev-live-reload) |
|
||||
| Encoding Selector | [](https://travis-ci.org/atom/encoding-selector) | [](https://ci.appveyor.com/project/Atom/encoding-selector/branch/master) | [](https://david-dm.org/atom/encoding-selector) |
|
||||
| Exception Reporting | [](https://travis-ci.org/atom/exception-reporting) | [](https://ci.appveyor.com/project/Atom/exception-reporting/branch/master) | [](https://david-dm.org/atom/exception-reporting) |
|
||||
| Find and Replace | [](https://travis-ci.org/atom/find-and-replace) | [](https://ci.appveyor.com/project/Atom/find-and-replace/branch/master) | [](https://david-dm.org/atom/find-and-replace) |
|
||||
| Fuzzy Finder | [](https://travis-ci.org/atom/fuzzy-finder) | [](https://ci.appveyor.com/project/Atom/fuzzy-finder/branch/master) | [](https://david-dm.org/atom/fuzzy-finder) |
|
||||
| Git Diff | [](https://travis-ci.org/atom/git-diff) | [](https://ci.appveyor.com/project/Atom/git-diff/branch/master) | [](https://david-dm.org/atom/git-diff) |
|
||||
| Go to Line | [](https://travis-ci.org/atom/go-to-line) | [](https://ci.appveyor.com/project/Atom/go-to-line/branch/master) | [](https://david-dm.org/atom/go-to-line) |
|
||||
| Grammar Selector | [](https://travis-ci.org/atom/grammar-selector) | [](https://ci.appveyor.com/project/Atom/grammar-selector/branch/master) | [](https://david-dm.org/atom/grammar-selector) |
|
||||
| Image View | [](https://travis-ci.org/atom/image-view) | [](https://ci.appveyor.com/project/Atom/image-view/branch/master) | [](https://david-dm.org/atom/image-view) |
|
||||
| Incompatible Packages | [](https://travis-ci.org/atom/incompatible-packages) | [](https://ci.appveyor.com/project/Atom/incompatible-packages/branch/master) | [](https://david-dm.org/atom/incompatible-packages) |
|
||||
| Keybinding Resolver | [](https://travis-ci.org/atom/keybinding-resolver) | [](https://ci.appveyor.com/project/Atom/keybinding-resolver/branch/master) | [](https://david-dm.org/atom/keybinding-resolver) |
|
||||
| Line Ending Selector | [](https://travis-ci.org/atom/line-ending-selector) | [](https://ci.appveyor.com/project/Atom/line-ending-selector/branch/master) | [](https://david-dm.org/atom/line-ending-selector) |
|
||||
| Link | [](https://travis-ci.org/atom/link) | [](https://ci.appveyor.com/project/Atom/link/branch/master) | [](https://david-dm.org/atom/link) |
|
||||
| Markdown Preview | [](https://travis-ci.org/atom/markdown-preview) | [](https://ci.appveyor.com/project/Atom/markdown-preview/branch/master) | [](https://david-dm.org/atom/markdown-preview) |
|
||||
| Metrics | [](https://travis-ci.org/atom/metrics) | [](https://ci.appveyor.com/project/Atom/metrics/branch/master) | [](https://david-dm.org/atom/metrics) |
|
||||
| Notifications | [](https://travis-ci.org/atom/notifications) | [](https://ci.appveyor.com/project/Atom/notifications/branch/master) | [](https://david-dm.org/atom/notifications) |
|
||||
| Open on Github | [](https://travis-ci.org/atom/open-on-github) | [](https://ci.appveyor.com/project/Atom/open-on-github/branch/master) | [](https://david-dm.org/atom/open-on-github) |
|
||||
| Package Generator | [](https://travis-ci.org/atom/package-generator)| [](https://ci.appveyor.com/project/Atom/package-generator/branch/master) | [](https://david-dm.org/atom/package-generator) |
|
||||
| Settings View | [](https://travis-ci.org/atom/settings-view) | [](https://ci.appveyor.com/project/Atom/settings-view/branch/master) | [](https://david-dm.org/atom/settings-view) |
|
||||
| Snippets | [](https://travis-ci.org/atom/snippets) | [](https://ci.appveyor.com/project/Atom/snippets/branch/master) | [](https://david-dm.org/atom/snippets) |
|
||||
| Spell Check | [](https://travis-ci.org/atom/spell-check) | [](https://ci.appveyor.com/project/Atom/spell-check/branch/master) | [](https://david-dm.org/atom/spell-check) |
|
||||
| Status Bar | [](https://travis-ci.org/atom/status-bar) | [](https://ci.appveyor.com/project/Atom/status-bar/branch/master) | [](https://david-dm.org/atom/status-bar) |
|
||||
| Styleguide | [](https://travis-ci.org/atom/styleguide) | [](https://ci.appveyor.com/project/Atom/styleguide/branch/master) | [](https://david-dm.org/atom/styleguide) |
|
||||
| Symbols View | [](https://travis-ci.org/atom/symbols-view) | [](https://ci.appveyor.com/project/Atom/symbols-view/branch/master) | [](https://david-dm.org/atom/symbols-view) |
|
||||
| Tabs | [](https://travis-ci.org/atom/tabs) | [](https://ci.appveyor.com/project/Atom/tabs/branch/master) | [](https://david-dm.org/atom/tabs) |
|
||||
| Timecop | [](https://travis-ci.org/atom/timecop) | [](https://ci.appveyor.com/project/Atom/timecop/branch/master) | [](https://david-dm.org/atom/timecop) |
|
||||
| Tree View | [](https://travis-ci.org/atom/tree-view) | [](https://ci.appveyor.com/project/Atom/tree-view/branch/master) | [](https://david-dm.org/atom/tree-view) |
|
||||
| Update Package Dependencies | [](https://travis-ci.org/atom/update-package-dependencies) | [](https://ci.appveyor.com/project/Atom/update-package-dependencies/branch/master) | [](https://david-dm.org/atom/update-package-dependencies) |
|
||||
| Welcome | [](https://travis-ci.org/atom/welcome) | [](https://ci.appveyor.com/project/Atom/welcome/branch/master) | [](https://david-dm.org/atom/welcome) |
|
||||
| Whitespace | [](https://travis-ci.org/atom/whitespace) | [](https://ci.appveyor.com/project/Atom/whitespace/branch/master) | [](https://david-dm.org/atom/whitespace) |
|
||||
| Wrap Guide | [](https://travis-ci.org/atom/wrap-guide) | [](https://ci.appveyor.com/project/Atom/wrap-guide/branch/master) | [](https://david-dm.org/atom/wrap-guide) |
|
||||
|
||||
| Package | Travis | AppVeyor/Win | Dependencies |
|
||||
|---------|--------|--------------|--------------|
|
||||
| [About](https://github.com/atom/about) | [](https://travis-ci.org/atom/about) | [](https://ci.appveyor.com/project/atom/about/branch/master) | [](https://david-dm.org/atom/about) |
|
||||
| [Archive View](https://github.com/atom/archive-view) | [](https://travis-ci.org/atom/archive-view) | [](https://ci.appveyor.com/project/Atom/archive-view/branch/master) | [](https://david-dm.org/atom/archive-view) |
|
||||
| [AutoComplete Atom API](https://github.com/atom/autocomplete-atom-api) | [](https://travis-ci.org/atom/autocomplete-atom-api) | [](https://ci.appveyor.com/project/Atom/autocomplete-atom-api/branch/master) | [](https://david-dm.org/atom/autocomplete-atom-api) |
|
||||
| [AutoComplete CSS](https://github.com/atom/autocomplete-css) | [](https://travis-ci.org/atom/autocomplete-css) | [](https://ci.appveyor.com/project/Atom/autocomplete-css/branch/master) | [](https://david-dm.org/atom/autocomplete-css) |
|
||||
| [AutoComplete HTML](https://github.com/atom/autocomplete-html) | [](https://travis-ci.org/atom/autocomplete-html) | [](https://ci.appveyor.com/project/Atom/autocomplete-html/branch/master) | [](https://david-dm.org/atom/autocomplete-html) |
|
||||
| [AutoComplete+](https://github.com/atom/autocomplete-plus) | [](https://travis-ci.org/atom/autocomplete-plus) | [](https://ci.appveyor.com/project/Atom/autocomplete-plus/branch/master) | [](https://david-dm.org/atom/autocomplete-plus) |
|
||||
| [AutoComplete Snippets](https://github.com/atom/autocomplete-snippets) | [](https://travis-ci.org/atom/autocomplete-snippets) | [](https://ci.appveyor.com/project/Atom/autocomplete-snippets/branch/master) | [](https://david-dm.org/atom/autocomplete-snippets) |
|
||||
| [AutoFlow](https://github.com/atom/autoflow) | [](https://travis-ci.org/atom/autoflow) | [](https://ci.appveyor.com/project/Atom/autoflow/branch/master) | [](https://david-dm.org/atom/autoflow) |
|
||||
| [AutoSave](https://github.com/atom/autosave) | [](https://travis-ci.org/atom/autosave) | [](https://ci.appveyor.com/project/Atom/autosave/branch/master) | [](https://david-dm.org/atom/autosave) |
|
||||
| [Background Tips](https://github.com/atom/background-tips) | [](https://travis-ci.org/atom/background-tips) | [](https://ci.appveyor.com/project/Atom/background-tips/branch/master) | [](https://david-dm.org/atom/background-tips) |
|
||||
| [Bookmarks](https://github.com/atom/bookmarks) | [](https://travis-ci.org/atom/bookmarks) | [](https://ci.appveyor.com/project/Atom/bookmarks/branch/master) | [](https://david-dm.org/atom/bookmarks) |
|
||||
| [Bracket Matcher](https://github.com/atom/bracket-matcher) | [](https://travis-ci.org/atom/bracket-matcher) | [](https://ci.appveyor.com/project/Atom/bracket-matcher/branch/master) | [](https://david-dm.org/atom/bracket-matcher) |
|
||||
| [Command Palette](https://github.com/atom/command-palette) | [](https://travis-ci.org/atom/command-palette) | [](https://ci.appveyor.com/project/Atom/command-palette/branch/master) | [](https://david-dm.org/atom/command-palette) |
|
||||
| [Deprecation Cop](https://github.com/atom/deprecation-cop) | [](https://travis-ci.org/atom/deprecation-cop) | [](https://ci.appveyor.com/project/Atom/deprecation-cop/branch/master) | [](https://david-dm.org/atom/deprecation-cop) |
|
||||
| [Dev Live Reload](https://github.com/atom/dev-live-reload) | [](https://travis-ci.org/atom/dev-live-reload) | [](https://ci.appveyor.com/project/Atom/dev-live-reload/branch/master) | [](https://david-dm.org/atom/dev-live-reload) |
|
||||
| [Encoding Selector](https://github.com/atom/encoding-selector) | [](https://travis-ci.org/atom/encoding-selector) | [](https://ci.appveyor.com/project/Atom/encoding-selector/branch/master) | [](https://david-dm.org/atom/encoding-selector) |
|
||||
| [Exception Reporting](https://github.com/atom/exception-reporting) | [](https://travis-ci.org/atom/exception-reporting) | [](https://ci.appveyor.com/project/Atom/exception-reporting/branch/master) | [](https://david-dm.org/atom/exception-reporting) |
|
||||
| [Find and Replace](https://github.com/atom/find-and-replace) | [](https://travis-ci.org/atom/find-and-replace) | [](https://ci.appveyor.com/project/Atom/find-and-replace/branch/master) | [](https://david-dm.org/atom/find-and-replace) |
|
||||
| [Fuzzy Finder](https://github.com/atom/fuzzy-finder) | [](https://travis-ci.org/atom/fuzzy-finder) | [](https://ci.appveyor.com/project/Atom/fuzzy-finder/branch/master) | [](https://david-dm.org/atom/fuzzy-finder) |
|
||||
| [Git Diff](https://github.com/atom/git-diff) | [](https://travis-ci.org/atom/git-diff) | [](https://ci.appveyor.com/project/Atom/git-diff/branch/master) | [](https://david-dm.org/atom/git-diff) |
|
||||
| [Go to Line](https://github.com/atom/go-to-line) | [](https://travis-ci.org/atom/go-to-line) | [](https://ci.appveyor.com/project/Atom/go-to-line/branch/master) | [](https://david-dm.org/atom/go-to-line) |
|
||||
| [Grammar Selector](https://github.com/atom/grammar-selector) | [](https://travis-ci.org/atom/grammar-selector) | [](https://ci.appveyor.com/project/Atom/grammar-selector/branch/master) | [](https://david-dm.org/atom/grammar-selector) |
|
||||
| [Image View](https://github.com/atom/image-view) | [](https://travis-ci.org/atom/image-view) | [](https://ci.appveyor.com/project/Atom/image-view/branch/master) | [](https://david-dm.org/atom/image-view) |
|
||||
| [Incompatible Packages](https://github.com/atom/incompatible-packages) | [](https://travis-ci.org/atom/incompatible-packages) | [](https://ci.appveyor.com/project/Atom/incompatible-packages/branch/master) | [](https://david-dm.org/atom/incompatible-packages) |
|
||||
| [Keybinding Resolver](https://github.com/atom/keybinding-resolver) | [](https://travis-ci.org/atom/keybinding-resolver) | [](https://ci.appveyor.com/project/Atom/keybinding-resolver/branch/master) | [](https://david-dm.org/atom/keybinding-resolver) |
|
||||
| [Line Ending Selector](https://github.com/atom/line-ending-selector) | [](https://travis-ci.org/atom/line-ending-selector) | [](https://ci.appveyor.com/project/Atom/line-ending-selector/branch/master) | [](https://david-dm.org/atom/line-ending-selector) |
|
||||
| [Link](https://github.com/atom/link) | [](https://travis-ci.org/atom/link) | [](https://ci.appveyor.com/project/Atom/link/branch/master) | [](https://david-dm.org/atom/link) |
|
||||
| [Markdown Preview](https://github.com/atom/markdown-preview) | [](https://travis-ci.org/atom/markdown-preview) | [](https://ci.appveyor.com/project/Atom/markdown-preview/branch/master) | [](https://david-dm.org/atom/markdown-preview) |
|
||||
| [Metrics](https://github.com/atom/metrics) | [](https://travis-ci.org/atom/metrics) | [](https://ci.appveyor.com/project/Atom/metrics/branch/master) | [](https://david-dm.org/atom/metrics) |
|
||||
| [Notifications](https://github.com/atom/notifications) | [](https://travis-ci.org/atom/notifications) | [](https://ci.appveyor.com/project/Atom/notifications/branch/master) | [](https://david-dm.org/atom/notifications) |
|
||||
| [Open on Github](https://github.com/atom/open-on-github) | [](https://travis-ci.org/atom/open-on-github) | [](https://ci.appveyor.com/project/Atom/open-on-github/branch/master) | [](https://david-dm.org/atom/open-on-github) |
|
||||
| [Package Generator](https://github.com/atom/package-generator) | [](https://travis-ci.org/atom/package-generator)| [](https://ci.appveyor.com/project/Atom/package-generator/branch/master) | [](https://david-dm.org/atom/package-generator) |
|
||||
| [Settings View](https://github.com/atom/settings-view) | [](https://travis-ci.org/atom/settings-view) | [](https://ci.appveyor.com/project/Atom/settings-view/branch/master) | [](https://david-dm.org/atom/settings-view) |
|
||||
| [Snippets](https://github.com/atom/snippets) | [](https://travis-ci.org/atom/snippets) | [](https://ci.appveyor.com/project/Atom/snippets/branch/master) | [](https://david-dm.org/atom/snippets) |
|
||||
| [Spell Check](https://github.com/atom/spell-check) | [](https://travis-ci.org/atom/spell-check) | [](https://ci.appveyor.com/project/Atom/spell-check/branch/master) | [](https://david-dm.org/atom/spell-check) |
|
||||
| [Status Bar](https://github.com/atom/status-bar) | [](https://travis-ci.org/atom/status-bar) | [](https://ci.appveyor.com/project/Atom/status-bar/branch/master) | [](https://david-dm.org/atom/status-bar) |
|
||||
| [Styleguide](https://github.com/atom/styleguide) | [](https://travis-ci.org/atom/styleguide) | [](https://ci.appveyor.com/project/Atom/styleguide/branch/master) | [](https://david-dm.org/atom/styleguide) |
|
||||
| [Symbols View](https://github.com/atom/symbols-view) | [](https://travis-ci.org/atom/symbols-view) | [](https://ci.appveyor.com/project/Atom/symbols-view/branch/master) | [](https://david-dm.org/atom/symbols-view) |
|
||||
| [Tabs](https://github.com/atom/tabs) | [](https://travis-ci.org/atom/tabs) | [](https://ci.appveyor.com/project/Atom/tabs/branch/master) | [](https://david-dm.org/atom/tabs) |
|
||||
| [Timecop](https://github.com/atom/timecop) | [](https://travis-ci.org/atom/timecop) | [](https://ci.appveyor.com/project/Atom/timecop/branch/master) | [](https://david-dm.org/atom/timecop) |
|
||||
| [Tree View](https://github.com/atom/tree-view) | [](https://travis-ci.org/atom/tree-view) | [](https://ci.appveyor.com/project/Atom/tree-view/branch/master) | [](https://david-dm.org/atom/tree-view) |
|
||||
| [Update Package Dependencies](https://github.com/atom/update-package-dependencies) | [](https://travis-ci.org/atom/update-package-dependencies) | [](https://ci.appveyor.com/project/Atom/update-package-dependencies/branch/master) | [](https://david-dm.org/atom/update-package-dependencies) |
|
||||
| [Welcome](https://github.com/atom/welcome) | [](https://travis-ci.org/atom/welcome) | [](https://ci.appveyor.com/project/Atom/welcome/branch/master) | [](https://david-dm.org/atom/welcome) |
|
||||
| [Whitespace](https://github.com/atom/whitespace) | [](https://travis-ci.org/atom/whitespace) | [](https://ci.appveyor.com/project/Atom/whitespace/branch/master) | [](https://david-dm.org/atom/whitespace) |
|
||||
| [Wrap Guide](https://github.com/atom/wrap-guide) | [](https://travis-ci.org/atom/wrap-guide) | [](https://ci.appveyor.com/project/Atom/wrap-guide/branch/master) | [](https://david-dm.org/atom/wrap-guide) |
|
||||
|
||||
## Libraries
|
||||
|
||||
| Library | macOS | Windows | Dependencies |
|
||||
|---------|------|---------|--------------|
|
||||
| Clear Cut | [](https://travis-ci.org/atom/clear-cut) | [](https://ci.appveyor.com/project/Atom/clear-cut/branch/master) | [](https://david-dm.org/atom/clear-cut) |
|
||||
| Event Kit | [](https://travis-ci.org/atom/event-kit) | [](https://ci.appveyor.com/project/Atom/event-kit/branch/master) | [](https://david-dm.org/atom/event-kit) |
|
||||
| Fs Plus | [](https://travis-ci.org/atom/fs-plus) | [](https://ci.appveyor.com/project/Atom/fs-plus/branch/master) | [](https://david-dm.org/atom/fs-plus) |
|
||||
| Grim | [](https://travis-ci.org/atom/grim) | [](https://ci.appveyor.com/project/Atom/grim/branch/master) | [](https://david-dm.org/atom/grim) |
|
||||
| Jasmine Focused | [](https://travis-ci.org/atom/grim) | [](https://ci.appveyor.com/project/Atom/jasmine-focused/branch/master) | [](https://david-dm.org/atom/jasmine-focused) |
|
||||
| Property Accessors | [](https://travis-ci.org/atom/property-accessors) | [](https://ci.appveyor.com/project/Atom/property-accessors/branch/master) | [](https://david-dm.org/atom/property-accessors) |
|
||||
| TextBuffer | [](https://travis-ci.org/atom/text-buffer) | [](https://ci.appveyor.com/project/Atom/text-buffer/branch/master) | [](https://david-dm.org/atom/text-buffer) |
|
||||
| Underscore-Plus | [](https://travis-ci.org/atom/underscore-plus) | [](https://ci.appveyor.com/project/Atom/underscore-plus/branch/master) | [](https://david-dm.org/atom/underscore-plus) |
|
||||
|
||||
| Library | Travis | AppVeyor/Win | Dependencies |
|
||||
|---------|--------|--------------|--------------|
|
||||
| [Clear Cut](https://github.com/atom/clear-cut) | [](https://travis-ci.org/atom/clear-cut) | [](https://ci.appveyor.com/project/Atom/clear-cut/branch/master) | [](https://david-dm.org/atom/clear-cut) |
|
||||
| [Event Kit](https://github.com/atom/event-kit) | [](https://travis-ci.org/atom/event-kit) | [](https://ci.appveyor.com/project/Atom/event-kit/branch/master) | [](https://david-dm.org/atom/event-kit) |
|
||||
| [First Mate](https://github.com/atom/first-mate) | [](https://travis-ci.org/atom/first-mate) | [](https://ci.appveyor.com/project/Atom/first-mate) | [](https://david-dm.org/atom/first-mate) |
|
||||
| [Fs Plus](https://github.com/atom/fs-plus) | [](https://travis-ci.org/atom/fs-plus) | [](https://ci.appveyor.com/project/Atom/fs-plus/branch/master) | [](https://david-dm.org/atom/fs-plus) |
|
||||
| [Grim](https://github.com/atom/grim) | [](https://travis-ci.org/atom/grim) | [](https://ci.appveyor.com/project/Atom/grim/branch/master) | [](https://david-dm.org/atom/grim) |
|
||||
| [Jasmine Focused](https://github.com/atom/jasmine-focused) | [](https://travis-ci.org/atom/grim) | [](https://ci.appveyor.com/project/Atom/jasmine-focused/branch/master) | [](https://david-dm.org/atom/jasmine-focused) |
|
||||
| [Keyboard Layout](https://github.com/atom/keyboard-layout) | [](https://travis-ci.org/atom/keyboard-layout) | [](https://ci.appveyor.com/project/Atom/keyboard-layout) | [](https://david-dm.org/atom/keyboard-layout) |
|
||||
| [Oniguruma](https://github.com/atom/node-oniguruma) | [](https://travis-ci.org/atom/node-oniguruma) | [](https://ci.appveyor.com/project/Atom/node-oniguruma/branch/master) | [](https://david-dm.org/atom/node-oniguruma) |
|
||||
| [PathWatcher](https://github.com/atom/node-pathwatcher) | [](https://travis-ci.org/atom/node-pathwatcher) | [](https://ci.appveyor.com/project/Atom/node-pathwatcher) | [](https://david-dm.org/atom/node-pathwatcher) |
|
||||
| [Property Accessors](https://github.com/atom/property-accessors) | [](https://travis-ci.org/atom/property-accessors) | [](https://ci.appveyor.com/project/Atom/property-accessors/branch/master) | [](https://david-dm.org/atom/property-accessors) |
|
||||
| [Season](https://github.com/atom/season) | [](https://travis-ci.org/atom/season) | [](https://ci.appveyor.com/project/Atom/season) | [](https://david-dm.org/atom/season) |
|
||||
| [TextBuffer](https://github.com/atom/text-buffer) | [](https://travis-ci.org/atom/text-buffer) | [](https://ci.appveyor.com/project/Atom/text-buffer/branch/master) | [](https://david-dm.org/atom/text-buffer) |
|
||||
| [Underscore-Plus](https://github.com/atom/underscore-plus) | [](https://travis-ci.org/atom/underscore-plus) | [](https://ci.appveyor.com/project/Atom/underscore-plus/branch/master) | [](https://david-dm.org/atom/underscore-plus) |
|
||||
|
||||
## Tools
|
||||
| Language | macOS | Windows | Dependencies |
|
||||
|----------|------|---------|--------------|
|
||||
| AtomDoc | [](https://travis-ci.org/atom/atomdoc) | [](https://ci.appveyor.com/project/Atom/atomdoc/branch/master) | [](https://david-dm.org/atom/atomdoc)
|
||||
| Language | Travis | AppVeyor/Win | Dependencies |
|
||||
|----------|--------|--------------|--------------|
|
||||
| [AtomDoc](https://github.com/atom/atomdoc) | [](https://travis-ci.org/atom/atomdoc) | [](https://ci.appveyor.com/project/Atom/atomdoc/branch/master) | [](https://david-dm.org/atom/atomdoc)
|
||||
|
||||
## Languages
|
||||
|
||||
| Language | macOS | Windows |
|
||||
|----------|------|---------|
|
||||
| C/C++ | [](https://travis-ci.org/atom/language-c) | [](https://ci.appveyor.com/project/Atom/language-c/branch/master) |
|
||||
| C# | [](https://travis-ci.org/atom/language-csharp) | [](https://ci.appveyor.com/project/Atom/language-csharp/branch/master) |
|
||||
| Clojure | [](https://travis-ci.org/atom/language-clojure) | [](https://ci.appveyor.com/project/Atom/language-clojure/branch/master) |
|
||||
| CoffeeScript | [](https://travis-ci.org/atom/language-coffee-script) | [](https://ci.appveyor.com/project/Atom/language-coffee-script/branch/master) |
|
||||
| CSS | [](https://travis-ci.org/atom/language-css) | [](https://ci.appveyor.com/project/Atom/language-css/branch/master) |
|
||||
| Git | [](https://travis-ci.org/atom/language-git) | [](https://ci.appveyor.com/project/Atom/language-git/branch/master) |
|
||||
| GitHub Flavored Markdown | [](https://travis-ci.org/atom/language-gfm) | [](https://ci.appveyor.com/project/Atom/language-gfm/branch/master) |
|
||||
| Go | [](https://travis-ci.org/atom/language-go) | [](https://ci.appveyor.com/project/Atom/language-go/branch/master) |
|
||||
| HTML | [](https://travis-ci.org/atom/language-html) | [](https://ci.appveyor.com/project/Atom/language-html/branch/master) |
|
||||
| Hyperlink | [](https://travis-ci.org/atom/language-hyperlink) | [](https://ci.appveyor.com/project/Atom/language-hyperlink/branch/master) |
|
||||
| Java | [](https://travis-ci.org/atom/language-java) | [](https://ci.appveyor.com/project/Atom/language-java/branch/master) |
|
||||
| JavaScript | [](https://travis-ci.org/atom/language-javascript) | [](https://ci.appveyor.com/project/Atom/language-javascript-dijf8/branch/master) |
|
||||
| JSON | [](https://travis-ci.org/atom/language-json) | [](https://ci.appveyor.com/project/Atom/language-json/branch/master) |
|
||||
| Less | [](https://travis-ci.org/atom/language-less) | [](https://ci.appveyor.com/project/Atom/language-less/branch/master) |
|
||||
| Make | [](https://travis-ci.org/atom/language-make) | [](https://ci.appveyor.com/project/Atom/language-make/branch/master) |
|
||||
| Mustache | [](https://travis-ci.org/atom/language-mustache) | [](https://ci.appveyor.com/project/Atom/language-mustache/branch/master) |
|
||||
| Objective-C | [](https://travis-ci.org/atom/language-objective-c) | [](https://ci.appveyor.com/project/Atom/language-objective-c/branch/master) |
|
||||
| Perl | [](https://travis-ci.org/atom/language-perl) | [](https://ci.appveyor.com/project/Atom/language-perl/branch/master) |
|
||||
| PHP | [](https://travis-ci.org/atom/language-php) | [](https://ci.appveyor.com/project/Atom/language-php/branch/master) |
|
||||
| Python | [](https://travis-ci.org/atom/language-python) | [](https://ci.appveyor.com/project/Atom/language-python/branch/master) |
|
||||
| Ruby | [](https://travis-ci.org/atom/language-ruby) | [](https://ci.appveyor.com/project/Atom/language-ruby/branch/master) |
|
||||
| Ruby on Rails | [](https://travis-ci.org/atom/language-ruby-on-rails) | [](https://ci.appveyor.com/project/Atom/language-ruby-on-rails/branch/master) |
|
||||
| Sass | [](https://travis-ci.org/atom/language-sass) | [](https://ci.appveyor.com/project/Atom/language-sass/branch/master) |
|
||||
| ShellScript | [](https://travis-ci.org/atom/language-shellscript) | [](https://ci.appveyor.com/project/Atom/language-shellscript/branch/master) |
|
||||
| SQL | [](https://travis-ci.org/atom/language-sql) | [](https://ci.appveyor.com/project/Atom/language-sql/branch/master) |
|
||||
| TODO | [](https://travis-ci.org/atom/language-todo) | [](https://ci.appveyor.com/project/Atom/language-todo/branch/master) |
|
||||
| TOML | [](https://travis-ci.org/atom/language-toml) | [](https://ci.appveyor.com/project/Atom/language-toml/branch/master) |
|
||||
| XML | [](https://travis-ci.org/atom/language-xml) | [](https://ci.appveyor.com/project/Atom/language-xml/branch/master) |
|
||||
| YAML | [](https://travis-ci.org/atom/language-yaml) | [](https://ci.appveyor.com/project/Atom/language-yaml/branch/master) |
|
||||
| Language | Travis | AppVeyor/Win |
|
||||
|----------|--------|--------------|
|
||||
| [C/C++](https://github.com/atom/language-c) | [](https://travis-ci.org/atom/language-c) | [](https://ci.appveyor.com/project/Atom/language-c/branch/master) |
|
||||
| [C#](https://github.com/atom/language-csharp) | [](https://travis-ci.org/atom/language-csharp) | [](https://ci.appveyor.com/project/Atom/language-csharp/branch/master) |
|
||||
| [Clojure](https://github.com/atom/language-clojure) | [](https://travis-ci.org/atom/language-clojure) | [](https://ci.appveyor.com/project/Atom/language-clojure/branch/master) |
|
||||
| [CoffeeScript](https://github.com/atom/language-coffee-script) | [](https://travis-ci.org/atom/language-coffee-script) | [](https://ci.appveyor.com/project/Atom/language-coffee-script/branch/master) |
|
||||
| [CSS](https://github.com/atom/language-css) | [](https://travis-ci.org/atom/language-css) | [](https://ci.appveyor.com/project/Atom/language-css/branch/master) |
|
||||
| [Git](https://github.com/atom/language-git) | [](https://travis-ci.org/atom/language-git) | [](https://ci.appveyor.com/project/Atom/language-git/branch/master) |
|
||||
| [GitHub Flavored Markdown](https://github.com/atom/language-gfm) | [](https://travis-ci.org/atom/language-gfm) | [](https://ci.appveyor.com/project/Atom/language-gfm/branch/master) |
|
||||
| [Go](https://github.com/atom/language-go) | [](https://travis-ci.org/atom/language-go) | [](https://ci.appveyor.com/project/Atom/language-go/branch/master) |
|
||||
| [HTML](https://github.com/atom/language-html) | [](https://travis-ci.org/atom/language-html) | [](https://ci.appveyor.com/project/Atom/language-html/branch/master) |
|
||||
| [Hyperlink](https://github.com/atom/language-hyperlink) | [](https://travis-ci.org/atom/language-hyperlink) | [](https://ci.appveyor.com/project/Atom/language-hyperlink/branch/master) |
|
||||
| [Java](https://github.com/atom/language-java) | [](https://travis-ci.org/atom/language-java) | [](https://ci.appveyor.com/project/Atom/language-java/branch/master) |
|
||||
| [JavaScript](https://github.com/atom/language-javascript) | [](https://travis-ci.org/atom/language-javascript) | [](https://ci.appveyor.com/project/Atom/language-javascript-dijf8/branch/master) |
|
||||
| [JSON](https://github.com/atom/language-json) | [](https://travis-ci.org/atom/language-json) | [](https://ci.appveyor.com/project/Atom/language-json/branch/master) |
|
||||
| [Less](https://github.com/atom/language-less) | [](https://travis-ci.org/atom/language-less) | [](https://ci.appveyor.com/project/Atom/language-less/branch/master) |
|
||||
| [Make](https://github.com/atom/language-make) | [](https://travis-ci.org/atom/language-make) | [](https://ci.appveyor.com/project/Atom/language-make/branch/master) |
|
||||
| [Mustache](https://github.com/atom/language-mustache) | [](https://travis-ci.org/atom/language-mustache) | [](https://ci.appveyor.com/project/Atom/language-mustache/branch/master) |
|
||||
| [Objective-C](https://github.com/atom/language-objective-c) | [](https://travis-ci.org/atom/language-objective-c) | [](https://ci.appveyor.com/project/Atom/language-objective-c/branch/master) |
|
||||
| [Perl](https://github.com/atom/language-perl) | [](https://travis-ci.org/atom/language-perl) | [](https://ci.appveyor.com/project/Atom/language-perl/branch/master) |
|
||||
| [PHP](https://github.com/atom/language-php) | [](https://travis-ci.org/atom/language-php) | [](https://ci.appveyor.com/project/Atom/language-php/branch/master) |
|
||||
| [Python](https://github.com/atom/language-python) | [](https://travis-ci.org/atom/language-python) | [](https://ci.appveyor.com/project/Atom/language-python/branch/master) |
|
||||
| [Ruby](https://github.com/atom/language-ruby) | [](https://travis-ci.org/atom/language-ruby) | [](https://ci.appveyor.com/project/Atom/language-ruby/branch/master) |
|
||||
| [Ruby on Rails](https://github.com/atom/language-ruby-on-rails) | [](https://travis-ci.org/atom/language-ruby-on-rails) | [](https://ci.appveyor.com/project/Atom/language-ruby-on-rails/branch/master) |
|
||||
| [Sass](https://github.com/atom/language-sass) | [](https://travis-ci.org/atom/language-sass) | [](https://ci.appveyor.com/project/Atom/language-sass/branch/master) |
|
||||
| [ShellScript](https://github.com/atom/language-shellscript) | [](https://travis-ci.org/atom/language-shellscript) | [](https://ci.appveyor.com/project/Atom/language-shellscript/branch/master) |
|
||||
| [SQL](https://github.com/atom/language-sql) | [](https://travis-ci.org/atom/language-sql) | [](https://ci.appveyor.com/project/Atom/language-sql/branch/master) |
|
||||
| [TODO](https://github.com/atom/language-todo) | [](https://travis-ci.org/atom/language-todo) | [](https://ci.appveyor.com/project/Atom/language-todo/branch/master) |
|
||||
| [TOML](https://github.com/atom/language-toml) | [](https://travis-ci.org/atom/language-toml) | [](https://ci.appveyor.com/project/Atom/language-toml/branch/master) |
|
||||
| [XML](https://github.com/atom/language-xml) | [](https://travis-ci.org/atom/language-xml) | [](https://ci.appveyor.com/project/Atom/language-xml/branch/master) |
|
||||
| [YAML](https://github/atom/language-yaml) | [](https://travis-ci.org/atom/language-yaml) | [](https://ci.appveyor.com/project/Atom/language-yaml/branch/master) |
|
||||
|
||||
@@ -36,7 +36,7 @@ To also install the newly built application, use `--create-debian-package` or `-
|
||||
* Install GNOME headers and other basic prerequisites:
|
||||
|
||||
```sh
|
||||
sudo apt-get install build-essential git libgnome-keyring-dev fakeroot rpm
|
||||
sudo apt-get install build-essential git libgnome-keyring-dev fakeroot rpm libx11-dev libxkbfile-dev
|
||||
```
|
||||
|
||||
* If `script/build` exits with an error, you may need to install a newer C++ compiler with C++11:
|
||||
@@ -49,13 +49,17 @@ To also install the newly built application, use `--create-debian-package` or `-
|
||||
sudo update-alternatives --config gcc # choose gcc-5 from the list
|
||||
```
|
||||
|
||||
### Fedora / CentOS / RHEL
|
||||
### Fedora 22+
|
||||
|
||||
* `sudo dnf --assumeyes install make gcc gcc-c++ glibc-devel git-core libgnome-keyring-devel rpmdevtools`
|
||||
* `sudo dnf --assumeyes install make gcc gcc-c++ glibc-devel git-core libgnome-keyring-devel rpmdevtools libX11-devel libxkbfile-devel`
|
||||
|
||||
### Fedora 21 / CentOS / RHEL
|
||||
|
||||
* `sudo yum install -y make gcc gcc-c++ glibc-devel git-core libgnome-keyring-devel rpmdevtools`
|
||||
|
||||
### Arch
|
||||
|
||||
* `sudo pacman -S --needed gconf base-devel git nodejs npm libgnome-keyring python2`
|
||||
* `sudo pacman -S --needed gconf base-devel git nodejs npm libgnome-keyring python2 libX11-devel libxkbfile-devel`
|
||||
* `export PYTHON=/usr/bin/python2` before building Atom.
|
||||
|
||||
### Slackware
|
||||
@@ -64,7 +68,7 @@ To also install the newly built application, use `--create-debian-package` or `-
|
||||
|
||||
### openSUSE
|
||||
|
||||
* `sudo zypper install nodejs nodejs-devel make gcc gcc-c++ glibc-devel git-core libgnome-keyring-devel rpmdevtools`
|
||||
* `sudo zypper install nodejs nodejs-devel make gcc gcc-c++ glibc-devel git-core libgnome-keyring-devel rpmdevtools libX11-devel libxkbfile-devel`
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -26,4 +26,4 @@ To also install the newly built application, use `script/build --install`.
|
||||
## Troubleshooting
|
||||
|
||||
### macOS build error reports in atom/atom
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Aos-x&type=Issues) to get a list of reports about build errors on macOS.
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Amac&type=Issues) to get a list of reports about build errors on macOS.
|
||||
|
||||
@@ -2,20 +2,21 @@
|
||||
|
||||
## Requirements
|
||||
|
||||
* Node.js 4.4.x or later
|
||||
* Node.js 4.4.x or later (the architecture of node available to the build system will determine whether you build 32-bit or 64-bit Atom)
|
||||
* Python 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 directory containing the python.exe using: `mklink /d %SystemDrive%\Python27 D:\elsewhere\Python27`
|
||||
* The python.exe must be available at `%SystemDrive%\Python27\python.exe`. If it is installed elsewhere create a symbolic link to the directory containing the python.exe using: `mklink /d %SystemDrive%\Python27 D:\elsewhere\Python27`
|
||||
* 7zip (7z.exe available from the command line) - for creating distribution zip files
|
||||
* Visual Studio, either:
|
||||
* [Visual C++ Build Tools 2015](http://landinghub.visualstudio.com/visual-cpp-build-tools)
|
||||
* [Visual Studio 2013 Update 5](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Express Edition or better)
|
||||
* [Visual Studio 2015](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs) (Community Edition or better)
|
||||
|
||||
Whichever version you use, ensure that:
|
||||
Also ensure that:
|
||||
* The default installation folder is chosen so the build tools can find it
|
||||
* If using Visual Studio make sure Visual C++ support is selected/installed
|
||||
* If using Visual C++ Build Tools make sure Windows 8 SDK is selected/installed
|
||||
* A `git` command is in your path
|
||||
* Set the `GYP_MSVS_VERSION` environment variable to the Visual Studio/Build Tools version (`2013` or `2015`) e.g. ``[Environment]::SetEnvironmentVariable("GYP_MSVS_VERSION", "2015", "User")`` in PowerShell or set it in Windows advanced system settings control panel.
|
||||
* Set the `GYP_MSVS_VERSION` environment variable to the Visual Studio/Build Tools version (`2013` or `2015`) e.g. ``[Environment]::SetEnvironmentVariable("GYP_MSVS_VERSION", "2015", "User")`` in PowerShell (or set it in Windows advanced system settings).
|
||||
|
||||
## Instructions
|
||||
|
||||
@@ -32,30 +33,39 @@ To also install the newly built application, use `script\build --create-windows-
|
||||
|
||||
### `script\build` Options
|
||||
* `--code-sign`: signs the application with the GitHub certificate specified in `$WIN_P12KEY_URL`.
|
||||
* `--compress-artifacts`: zips the generated application as `out/atom-windows.zip` (requires 7-zip).
|
||||
* `--create-windows-installer`: creates an `.msi`, an `.exe` and a `.nupkg` installer in the `out/` directory.
|
||||
* `--compress-artifacts`: zips the generated application as `out\atom-windows.zip` (requires [7-Zip](http://www.7-zip.org)).
|
||||
* `--create-windows-installer`: creates an `.msi`, an `.exe` and two `.nupkg` packages in the `out` directory.
|
||||
* `--install`: installs the application in `%LOCALAPPDATA%\Atom\app-dev\`.
|
||||
|
||||
### Running tests
|
||||
|
||||
In order to run tests from command line you need `apm`, available after you install Atom or after you build from source. If you installed it, run the following commands (assuming `C:\atom` is the root of your Atom repository):
|
||||
|
||||
```bash
|
||||
cd C:\atom
|
||||
apm test
|
||||
```
|
||||
|
||||
When building Atom from source, the `apm` command is not added to the system path by default. In this case, you can either add it yourself or explicitly list the complete path in previous commands. The default install location is `%LOCALAPPDATA%\Atom\app-dev\resources\cli\`.
|
||||
|
||||
**NOTE**: Please keep in mind that there are still some tests that don't pass on Windows.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Errors
|
||||
* `node is not recognized`
|
||||
* If you just installed Node.js, you'll need to restart Command Prompt before the `node` command is available on your Path.
|
||||
* If you just installed Node.js, you'll need to restart Command Prompt before the `node` command is available on your path.
|
||||
|
||||
* `msbuild.exe failed with exit code: 1`
|
||||
* If you installed Visual Studio, ensure you have Visual C++ support installed. Go into Add/Remove Programs, select Visual Studio, press Modify, and then check the Visual C++ box.
|
||||
* If you installed Visual C++ Build Tools, ensure you have Windows 8 SDK support installed. Go into Add/Remove Programs, select Visual Studio, press Modify and then check the Windows 8 SDK box.
|
||||
* If using **Visual Studio**, ensure you have the **Visual C++** component installed. Go into Add/Remove Programs, select Visual Studio, press Modify, and then check the Visual C++ box.
|
||||
* If using **Visual C++ Build Tools**, ensure you have the **Windows 8 SDK** component installed. Go into Add/Remove Programs, select Visual C++ Build Tools, press Modify and then check the Windows 8 SDK box.
|
||||
|
||||
* `script\build` stop with no error or warning shortly after displaying the versions of node, npm and Python
|
||||
* `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. For example, use `C:\atom` instead of `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).
|
||||
* 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/)
|
||||
* 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
|
||||
@@ -67,14 +77,14 @@ To also install the newly built application, use `script\build --create-windows-
|
||||
* See the next item.
|
||||
|
||||
* `error MSB8020: The build tools for Visual Studio 201? (Platform Toolset = 'v1?0') cannot be found.`
|
||||
* Try setting the `GYP_MSVS_VERSION` environment variable to 2013 or 2015 depending on what version of Visual Studio/Build Tools is installed and then `script\clean` followed by `script\build` (re-open the Command Prompt if you set the variable using the GUI).
|
||||
* Try setting the `GYP_MSVS_VERSION` environment variable to **2013** or **2015** depending on what version of Visual Studio/Build Tools is installed and then `script\clean` followed by `script\build` (re-open the Command Prompt if you set the variable using the GUI).
|
||||
|
||||
* `'node-gyp' is not recognized as an internal or external command, operable program or batch file.`
|
||||
* Try running `npm install -g node-gyp`, and run `script\build` again.
|
||||
|
||||
* 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.
|
||||
* Do try the build command one more time as experience shows it often works on second try in many cases.
|
||||
|
||||
### Windows build error reports in atom/atom
|
||||
* If all fails, use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Awindows&type=Issues) to get a list of reports about build errors on Windows, and see if yours has already been reported.
|
||||
* If it hasn't, please open a new issue with your Windows version, architecture (x86 or amd64), and a screenshot of your build output, including the Node.js and Python versions.
|
||||
* If it hasn't, please open a new issue with your Windows version, architecture (x86 or x64), and a screenshot of your build output, including the Node.js and Python versions.
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
'atom-text-editor:not([mini])':
|
||||
# Atom Specific
|
||||
'ctrl-C': 'editor:copy-path'
|
||||
'ctrl-shift-c': 'editor:copy-path'
|
||||
|
||||
# Sublime Parity
|
||||
'tab': 'editor:indent'
|
||||
'enter': 'editor:newline'
|
||||
'shift-tab': 'editor:outdent-selected-rows'
|
||||
'ctrl-K': 'editor:delete-line'
|
||||
'ctrl-shift-k': 'editor:delete-line'
|
||||
|
||||
'.select-list atom-text-editor[mini]':
|
||||
'enter': 'core:confirm'
|
||||
@@ -24,7 +24,7 @@
|
||||
'atom-text-editor !important, atom-text-editor[mini] !important':
|
||||
'escape': 'editor:consolidate-selections'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
# Allow standard input fields to work correctly
|
||||
'body .native-key-bindings':
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
@@ -66,7 +66,7 @@
|
||||
'ctrl-shift-right': 'native!'
|
||||
'ctrl-b': 'native!'
|
||||
'ctrl-f': 'native!'
|
||||
'ctrl-F': 'native!'
|
||||
'ctrl-B': 'native!'
|
||||
'ctrl-shift-f': 'native!'
|
||||
'ctrl-shift-b': 'native!'
|
||||
'ctrl-h': 'native!'
|
||||
'ctrl-d': 'native!'
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
'ctrl-n': 'core:move-down'
|
||||
'ctrl-b': 'core:move-left'
|
||||
'ctrl-f': 'core:move-right'
|
||||
'ctrl-P': 'core:select-up'
|
||||
'ctrl-N': 'core:select-down'
|
||||
'ctrl-F': 'core:select-right'
|
||||
'ctrl-B': 'core:select-left'
|
||||
'ctrl-shift-p': 'core:select-up'
|
||||
'ctrl-shift-n': 'core:select-down'
|
||||
'ctrl-shift-f': 'core:select-right'
|
||||
'ctrl-shift-b': 'core:select-left'
|
||||
'ctrl-h': 'core:backspace'
|
||||
'ctrl-d': 'core:delete'
|
||||
|
||||
@@ -34,19 +34,19 @@
|
||||
|
||||
# Sublime Parity
|
||||
'cmd-,': 'application:show-settings'
|
||||
'cmd-N': 'application:new-window'
|
||||
'cmd-W': 'window:close'
|
||||
'cmd-shift-n': 'application:new-window'
|
||||
'cmd-shift-w': 'window:close'
|
||||
'cmd-o': 'application:open'
|
||||
'cmd-O': 'application:add-project-folder'
|
||||
'cmd-T': 'pane:reopen-closed-item'
|
||||
'cmd-shift-o': 'application:add-project-folder'
|
||||
'cmd-shift-t': 'pane:reopen-closed-item'
|
||||
'cmd-n': 'application:new-file'
|
||||
'cmd-s': 'core:save'
|
||||
'cmd-S': 'core:save-as'
|
||||
'cmd-shift-s': 'core:save-as'
|
||||
'cmd-alt-s': 'window:save-all'
|
||||
'cmd-w': 'core:close'
|
||||
'cmd-ctrl-f': 'window:toggle-full-screen'
|
||||
'cmd-z': 'core:undo'
|
||||
'cmd-Z': 'core:redo'
|
||||
'cmd-shift-z': 'core:redo'
|
||||
'cmd-y': 'core:redo'
|
||||
'cmd-x': 'core:cut'
|
||||
'cmd-c': 'core:copy'
|
||||
@@ -116,8 +116,8 @@
|
||||
'cmd-backspace': 'editor:delete-to-beginning-of-line'
|
||||
'cmd-shift-backspace': 'editor:delete-to-beginning-of-line'
|
||||
'cmd-delete': 'editor:delete-to-end-of-line'
|
||||
'ctrl-A': 'editor:select-to-first-character-of-line'
|
||||
'ctrl-E': 'editor:select-to-end-of-line'
|
||||
'ctrl-shift-a': 'editor:select-to-first-character-of-line'
|
||||
'ctrl-shift-e': 'editor:select-to-end-of-line'
|
||||
'cmd-left': 'editor:move-to-first-character-of-line'
|
||||
'cmd-right': 'editor:move-to-end-of-screen-line'
|
||||
'cmd-shift-left': 'editor:select-to-first-character-of-line'
|
||||
@@ -129,19 +129,19 @@
|
||||
'ctrl-k': 'editor:cut-to-end-of-line'
|
||||
|
||||
# Atom Specific
|
||||
'ctrl-W': 'editor:select-word'
|
||||
'ctrl-shift-w': 'editor:select-word'
|
||||
'cmd-ctrl-left': 'editor:move-selection-left'
|
||||
'cmd-ctrl-right': 'editor:move-selection-right'
|
||||
|
||||
# Emacs
|
||||
'alt-f': 'editor:move-to-end-of-word'
|
||||
'alt-ctrl-f': 'editor:move-to-next-subword-boundary'
|
||||
'alt-F': 'editor:select-to-end-of-word'
|
||||
'alt-ctrl-F': 'editor:select-to-next-subword-boundary'
|
||||
'alt-shift-f': 'editor:select-to-end-of-word'
|
||||
'alt-ctrl-shift-f': 'editor:select-to-next-subword-boundary'
|
||||
'alt-b': 'editor:move-to-beginning-of-word'
|
||||
'alt-ctrl-b': 'editor:move-to-previous-subword-boundary'
|
||||
'alt-B': 'editor:select-to-beginning-of-word'
|
||||
'alt-ctrl-B': 'editor:select-to-previous-subword-boundary'
|
||||
'alt-shift-b': 'editor:select-to-beginning-of-word'
|
||||
'alt-ctrl-shift-b': 'editor:select-to-previous-subword-boundary'
|
||||
'alt-h': 'editor:delete-to-beginning-of-word'
|
||||
'alt-ctrl-h': 'editor:delete-to-beginning-of-subword'
|
||||
'alt-d': 'editor:delete-to-end-of-word'
|
||||
@@ -178,8 +178,8 @@
|
||||
'ctrl-cmd-down': 'editor:move-line-down'
|
||||
'cmd-/': 'editor:toggle-line-comments'
|
||||
'cmd-j': 'editor:join-lines'
|
||||
'cmd-D': 'editor:duplicate-lines'
|
||||
'cmd-L': 'editor:split-selections-into-lines'
|
||||
'cmd-shift-d': 'editor:duplicate-lines'
|
||||
'cmd-shift-l': 'editor:split-selections-into-lines'
|
||||
'ctrl-shift-up': 'editor:add-selection-above'
|
||||
'ctrl-shift-down': 'editor:add-selection-below'
|
||||
|
||||
@@ -202,10 +202,10 @@
|
||||
'cmd-alt-=': 'pane:increase-size'
|
||||
'cmd-alt--': 'pane:decrease-size'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
# Allow standard input fields to work correctly
|
||||
'body .native-key-bindings':
|
||||
'cmd-z': 'native!'
|
||||
'cmd-Z': 'native!'
|
||||
'cmd-shift-z': 'native!'
|
||||
'cmd-x': 'native!'
|
||||
'cmd-c': 'native!'
|
||||
'cmd-v': 'native!'
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
'down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'ctrl-alt-r': 'window:reload'
|
||||
'ctrl-shift-f5': 'window:reload'
|
||||
'ctrl-shift-i': 'window:toggle-dev-tools'
|
||||
'ctrl-alt-p': 'window:run-package-specs'
|
||||
'ctrl-shift-y': 'window:run-package-specs'
|
||||
'ctrl-shift-o': 'application:open-folder'
|
||||
'ctrl-alt-o': 'application:add-project-folder'
|
||||
'ctrl-shift-a': 'application:add-project-folder'
|
||||
'ctrl-shift-pageup': 'pane:move-item-left'
|
||||
'ctrl-shift-pagedown': 'pane:move-item-right'
|
||||
'f11': 'window:toggle-full-screen'
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-,': 'application:show-settings'
|
||||
'ctrl-N': 'application:new-window'
|
||||
'ctrl-W': 'window:close'
|
||||
'ctrl-shift-n': 'application:new-window'
|
||||
'ctrl-shift-w': 'window:close'
|
||||
'ctrl-o': 'application:open-file'
|
||||
'ctrl-q': 'application:quit'
|
||||
'ctrl-T': 'pane:reopen-closed-item'
|
||||
'ctrl-shift-t': 'pane:reopen-closed-item'
|
||||
'ctrl-n': 'application:new-file'
|
||||
'ctrl-s': 'core:save'
|
||||
'ctrl-S': 'core:save-as'
|
||||
'ctrl-shift-s': 'core:save-as'
|
||||
'ctrl-f4': 'core:close'
|
||||
'ctrl-w': 'core:close'
|
||||
'ctrl-z': 'core:undo'
|
||||
@@ -70,12 +70,12 @@
|
||||
'ctrl-k left': 'pane:split-left-and-copy-active-item' # Atom Specific
|
||||
'ctrl-k right': 'pane:split-right-and-copy-active-item' # Atom Specific
|
||||
'ctrl-k ctrl-w': 'pane:close' # Atom Specific
|
||||
'ctrl-k alt-ctrl-w': 'pane:close-other-items' # Atom Specific
|
||||
'ctrl-k ctrl-alt-w': 'pane:close-other-items' # Atom Specific
|
||||
'ctrl-k ctrl-p': 'window:focus-previous-pane'
|
||||
'ctrl-k ctrl-n': 'window:focus-next-pane'
|
||||
'ctrl-k ctrl-up': 'window:focus-pane-above'
|
||||
'ctrl-k ctrl-down': 'window:focus-pane-below'
|
||||
'ctrl-k ctrl-left': 'window:focus-pane-on-left'
|
||||
'ctrl-k ctrl-up': 'window:focus-pane-above'
|
||||
'ctrl-k ctrl-down': 'window:focus-pane-below'
|
||||
'ctrl-k ctrl-left': 'window:focus-pane-on-left'
|
||||
'ctrl-k ctrl-right': 'window:focus-pane-on-right'
|
||||
'alt-1': 'pane:show-item-1'
|
||||
'alt-2': 'pane:show-item-2'
|
||||
@@ -108,16 +108,14 @@
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-a': 'core:select-all'
|
||||
'ctrl-alt-shift-p': 'editor:log-cursor-scope'
|
||||
'ctrl-k ctrl-u': 'editor:upper-case'
|
||||
'ctrl-k ctrl-l': 'editor:lower-case'
|
||||
'ctrl-l': 'editor:select-line'
|
||||
|
||||
'atom-workspace atom-text-editor:not([mini])':
|
||||
# Atom specific
|
||||
'alt-ctrl-z': 'editor:checkout-head-revision'
|
||||
'ctrl-<': 'editor:scroll-to-cursor'
|
||||
'alt-ctrl-f': 'editor:fold-selection'
|
||||
'ctrl-alt-shift-[': 'editor:fold-selection'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-enter': 'editor:newline-below'
|
||||
@@ -128,7 +126,7 @@
|
||||
'ctrl-down': 'editor:move-line-down'
|
||||
'ctrl-/': 'editor:toggle-line-comments'
|
||||
'ctrl-j': 'editor:join-lines'
|
||||
'ctrl-D': 'editor:duplicate-lines'
|
||||
'ctrl-shift-d': 'editor:duplicate-lines'
|
||||
'alt-shift-up': 'editor:add-selection-above'
|
||||
'alt-shift-down': 'editor:add-selection-below'
|
||||
|
||||
@@ -151,10 +149,10 @@
|
||||
'ctrl-alt-=': 'pane:increase-size'
|
||||
'ctrl-alt--': 'pane:decrease-size'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
# Allow standard input fields to work correctly
|
||||
'body .native-key-bindings':
|
||||
'ctrl-z': 'native!'
|
||||
'ctrl-Z': 'native!'
|
||||
'ctrl-shift-z': 'native!'
|
||||
'ctrl-x': 'native!'
|
||||
'ctrl-c': 'native!'
|
||||
'ctrl-v': 'native!'
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
'ctrl-down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'ctrl-alt-r': 'window:reload'
|
||||
'ctrl-shift-f5': 'window:reload'
|
||||
'ctrl-shift-i': 'window:toggle-dev-tools'
|
||||
'ctrl-alt-p': 'window:run-package-specs'
|
||||
'ctrl-shift-y': 'window:run-package-specs'
|
||||
'ctrl-shift-o': 'application:open-folder'
|
||||
'ctrl-alt-o': 'application:add-project-folder'
|
||||
'ctrl-shift-a': 'application:add-project-folder'
|
||||
'ctrl-shift-left': 'pane:move-item-left'
|
||||
'ctrl-shift-right': 'pane:move-item-right'
|
||||
'f11': 'window:toggle-full-screen'
|
||||
@@ -25,13 +25,13 @@
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-,': 'application:show-settings'
|
||||
'ctrl-N': 'application:new-window'
|
||||
'ctrl-W': 'window:close'
|
||||
'ctrl-shift-n': 'application:new-window'
|
||||
'ctrl-shift-w': 'window:close'
|
||||
'ctrl-o': 'application:open-file'
|
||||
'ctrl-T': 'pane:reopen-closed-item'
|
||||
'ctrl-shift-t': 'pane:reopen-closed-item'
|
||||
'ctrl-n': 'application:new-file'
|
||||
'ctrl-s': 'core:save'
|
||||
'ctrl-S': 'core:save-as'
|
||||
'ctrl-shift-s': 'core:save-as'
|
||||
'ctrl-f4': 'core:close'
|
||||
'ctrl-w': 'core:close'
|
||||
'ctrl-z': 'core:undo'
|
||||
@@ -75,12 +75,12 @@
|
||||
'ctrl-k left': 'pane:split-left-and-copy-active-item' # Atom Specific
|
||||
'ctrl-k right': 'pane:split-right-and-copy-active-item' # Atom Specific
|
||||
'ctrl-k ctrl-w': 'pane:close' # Atom Specific
|
||||
'ctrl-k alt-ctrl-w': 'pane:close-other-items' # Atom Specific
|
||||
'ctrl-k ctrl-alt-w': 'pane:close-other-items' # Atom Specific
|
||||
'ctrl-k ctrl-p': 'window:focus-previous-pane'
|
||||
'ctrl-k ctrl-n': 'window:focus-next-pane'
|
||||
'ctrl-k ctrl-up': 'window:focus-pane-above'
|
||||
'ctrl-k ctrl-down': 'window:focus-pane-below'
|
||||
'ctrl-k ctrl-left': 'window:focus-pane-on-left'
|
||||
'ctrl-k ctrl-up': 'window:focus-pane-above'
|
||||
'ctrl-k ctrl-down': 'window:focus-pane-below'
|
||||
'ctrl-k ctrl-left': 'window:focus-pane-on-left'
|
||||
'ctrl-k ctrl-right': 'window:focus-pane-on-right'
|
||||
'alt-1': 'pane:show-item-1'
|
||||
'alt-2': 'pane:show-item-2'
|
||||
@@ -113,16 +113,14 @@
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-a': 'core:select-all'
|
||||
'ctrl-alt-shift-p': 'editor:log-cursor-scope'
|
||||
'ctrl-k ctrl-u': 'editor:upper-case'
|
||||
'ctrl-k ctrl-l': 'editor:lower-case'
|
||||
'ctrl-l': 'editor:select-line'
|
||||
|
||||
'atom-workspace atom-text-editor:not([mini])':
|
||||
# Atom specific
|
||||
'alt-ctrl-z': 'editor:checkout-head-revision'
|
||||
'ctrl-<': 'editor:scroll-to-cursor'
|
||||
'alt-ctrl-f': 'editor:fold-selection'
|
||||
'ctrl-alt-shift-[': 'editor:fold-selection'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-enter': 'editor:newline-below'
|
||||
@@ -133,7 +131,7 @@
|
||||
'ctrl-down': 'editor:move-line-down'
|
||||
'ctrl-/': 'editor:toggle-line-comments'
|
||||
'ctrl-j': 'editor:join-lines'
|
||||
'ctrl-D': 'editor:duplicate-lines'
|
||||
'ctrl-shift-d': 'editor:duplicate-lines'
|
||||
|
||||
'ctrl-alt-[': 'editor:fold-current-row'
|
||||
'ctrl-alt-]': 'editor:unfold-current-row'
|
||||
@@ -154,10 +152,10 @@
|
||||
'ctrl-alt-=': 'pane:increase-size'
|
||||
'ctrl-alt--': 'pane:decrease-size'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
# Allow standard input fields to work correctly
|
||||
'body .native-key-bindings':
|
||||
'ctrl-z': 'native!'
|
||||
'ctrl-Z': 'native!'
|
||||
'ctrl-shift-z': 'native!'
|
||||
'ctrl-x': 'native!'
|
||||
'ctrl-c': 'native!'
|
||||
'ctrl-v': 'native!'
|
||||
|
||||
@@ -36,6 +36,13 @@
|
||||
{ label: 'New File', command: 'application:new-file' }
|
||||
{ label: 'Open…', command: 'application:open' }
|
||||
{ label: 'Add Project Folder…', command: 'application:add-project-folder' }
|
||||
{
|
||||
label: 'Reopen Project',
|
||||
submenu: [
|
||||
{ label: 'Clear Project History', command: 'application:clear-project-history' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
{ label: 'Reopen Last Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Save', command: 'core:save' }
|
||||
@@ -101,9 +108,9 @@
|
||||
submenu: [
|
||||
{ label: 'Fold', command: 'editor:fold-current-row' }
|
||||
{ label: 'Unfold', command: 'editor:unfold-current-row' }
|
||||
{ label: 'Fold All', command: 'editor:fold-all' }
|
||||
{ label: 'Unfold All', command: 'editor:unfold-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Fold All', command: 'editor:fold-all' }
|
||||
{ label: 'Fold Level 1', command: 'editor:fold-at-indent-level-1' }
|
||||
{ label: 'Fold Level 2', command: 'editor:fold-at-indent-level-2' }
|
||||
{ label: 'Fold Level 3', command: 'editor:fold-at-indent-level-3' }
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
{ label: '&Open File…', command: 'application:open-file' }
|
||||
{ label: 'Open Folder…', command: 'application:open-folder' }
|
||||
{ label: 'Add Project Folder…', command: 'application:add-project-folder' }
|
||||
{
|
||||
label: 'Reopen Project',
|
||||
submenu: [
|
||||
{ label: 'Clear Project History', command: 'application:clear-project-history' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Save', command: 'core:save' }
|
||||
@@ -74,9 +81,9 @@
|
||||
submenu: [
|
||||
{ label: '&Fold', command: 'editor:fold-current-row' }
|
||||
{ label: '&Unfold', command: 'editor:unfold-current-row' }
|
||||
{ label: 'Fol&d All', command: 'editor:fold-all' }
|
||||
{ label: 'Unfold &All', command: 'editor:unfold-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Fol&d All', command: 'editor:fold-all' }
|
||||
{ label: 'Fold Level 1', command: 'editor:fold-at-indent-level-1' }
|
||||
{ label: 'Fold Level 2', command: 'editor:fold-at-indent-level-2' }
|
||||
{ label: 'Fold Level 3', command: 'editor:fold-at-indent-level-3' }
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
{ label: '&Open File…', command: 'application:open-file' }
|
||||
{ label: 'Open Folder…', command: 'application:open-folder' }
|
||||
{ label: 'Add Project Folder…', command: 'application:add-project-folder' }
|
||||
{
|
||||
label: 'Reopen Project',
|
||||
submenu: [
|
||||
{ label: 'Clear Project History', command: 'application:clear-project-history' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Se&ttings', command: 'application:show-settings' }
|
||||
@@ -82,9 +89,9 @@
|
||||
submenu: [
|
||||
{ label: '&Fold', command: 'editor:fold-current-row' }
|
||||
{ label: '&Unfold', command: 'editor:unfold-current-row' }
|
||||
{ label: 'Fol&d All', command: 'editor:fold-all' }
|
||||
{ label: 'Unfold &All', command: 'editor:unfold-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Fol&d All', command: 'editor:fold-all' }
|
||||
{ label: 'Fold Level 1', command: 'editor:fold-at-indent-level-1' }
|
||||
{ label: 'Fold Level 2', command: 'editor:fold-at-indent-level-2' }
|
||||
{ label: 'Fold Level 3', command: 'editor:fold-at-indent-level-3' }
|
||||
|
||||
123
package.json
123
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.13.0-dev",
|
||||
"version": "1.15.0-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/main-process/main.js",
|
||||
"repository": {
|
||||
@@ -12,10 +12,11 @@
|
||||
"url": "https://github.com/atom/atom/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"electronVersion": "1.3.6",
|
||||
"electronVersion": "1.3.13",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "7.0.6",
|
||||
"atom-keymap": "7.1.18",
|
||||
"atom-select-list": "0.0.6",
|
||||
"atom-ui": "0.4.1",
|
||||
"babel-core": "5.8.38",
|
||||
"cached-run-in-this-context": "0.4.1",
|
||||
@@ -32,7 +33,7 @@
|
||||
"fs-plus": "2.9.2",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "^2.1",
|
||||
"git-utils": "^4.1.2",
|
||||
"git-utils": "4.1.2",
|
||||
"glob": "^7.1.1",
|
||||
"grim": "1.5.0",
|
||||
"jasmine-json": "~0.0",
|
||||
@@ -42,18 +43,20 @@
|
||||
"less-cache": "0.23",
|
||||
"line-top-index": "0.2.0",
|
||||
"marked": "^0.3.6",
|
||||
"minimatch": "^3.0.3",
|
||||
"mocha": "2.5.1",
|
||||
"mock-spawn": "^0.2.6",
|
||||
"normalize-package-data": "^2.0.0",
|
||||
"nslog": "^3",
|
||||
"oniguruma": "6.1.0",
|
||||
"pathwatcher": "~6.5",
|
||||
"pathwatcher": "6.8.0",
|
||||
"postcss": "5.2.4",
|
||||
"postcss-selector-parser": "2.2.1",
|
||||
"property-accessors": "^1.1.3",
|
||||
"random-words": "0.0.1",
|
||||
"resolve": "^1.1.6",
|
||||
"runas": "^3.1",
|
||||
"scandal": "^2.2.1",
|
||||
"scandal": "2.2.2",
|
||||
"scoped-property-store": "^0.17.0",
|
||||
"scrollbar-style": "^3.2",
|
||||
"season": "^5.4.1",
|
||||
@@ -62,7 +65,7 @@
|
||||
"sinon": "1.17.4",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "9.4.0",
|
||||
"text-buffer": "10.2.3",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"winreg": "^1.2.1",
|
||||
@@ -73,91 +76,91 @@
|
||||
"atom-dark-ui": "0.53.0",
|
||||
"atom-light-syntax": "0.29.0",
|
||||
"atom-light-ui": "0.46.0",
|
||||
"base16-tomorrow-dark-theme": "1.4.0",
|
||||
"base16-tomorrow-light-theme": "1.4.0",
|
||||
"one-dark-ui": "1.7.0",
|
||||
"one-light-ui": "1.7.0",
|
||||
"one-dark-syntax": "1.6.0",
|
||||
"one-light-syntax": "1.6.0",
|
||||
"base16-tomorrow-dark-theme": "1.5.0",
|
||||
"base16-tomorrow-light-theme": "1.5.0",
|
||||
"one-dark-ui": "1.9.1",
|
||||
"one-light-ui": "1.9.1",
|
||||
"one-dark-syntax": "1.7.1",
|
||||
"one-light-syntax": "1.7.1",
|
||||
"solarized-dark-syntax": "1.1.1",
|
||||
"solarized-light-syntax": "1.1.1",
|
||||
"about": "1.7.0",
|
||||
"archive-view": "0.62.0",
|
||||
"about": "1.7.2",
|
||||
"archive-view": "0.62.2",
|
||||
"autocomplete-atom-api": "0.10.0",
|
||||
"autocomplete-css": "0.14.1",
|
||||
"autocomplete-css": "0.14.2",
|
||||
"autocomplete-html": "0.7.2",
|
||||
"autocomplete-plus": "2.33.1",
|
||||
"autocomplete-plus": "2.34.2",
|
||||
"autocomplete-snippets": "1.11.0",
|
||||
"autoflow": "0.27.0",
|
||||
"autosave": "0.23.2",
|
||||
"autoflow": "0.29.0",
|
||||
"autosave": "0.24.0",
|
||||
"background-tips": "0.26.1",
|
||||
"bookmarks": "0.43.1",
|
||||
"bracket-matcher": "0.82.2",
|
||||
"command-palette": "0.39.1",
|
||||
"bookmarks": "0.43.4",
|
||||
"bracket-matcher": "0.85.2",
|
||||
"command-palette": "0.39.2",
|
||||
"deprecation-cop": "0.55.1",
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.22.0",
|
||||
"exception-reporting": "0.40.0",
|
||||
"find-and-replace": "0.203.0",
|
||||
"fuzzy-finder": "1.4.0",
|
||||
"exception-reporting": "0.40.1",
|
||||
"find-and-replace": "0.206.0",
|
||||
"fuzzy-finder": "1.4.1",
|
||||
"git-diff": "1.2.0",
|
||||
"go-to-line": "0.31.0",
|
||||
"go-to-line": "0.31.2",
|
||||
"grammar-selector": "0.48.2",
|
||||
"image-view": "0.60.0",
|
||||
"incompatible-packages": "0.26.1",
|
||||
"keybinding-resolver": "0.35.0",
|
||||
"line-ending-selector": "0.5.0",
|
||||
"line-ending-selector": "0.5.1",
|
||||
"link": "0.31.2",
|
||||
"markdown-preview": "0.159.1",
|
||||
"metrics": "1.0.0",
|
||||
"notifications": "0.65.1",
|
||||
"markdown-preview": "0.159.2",
|
||||
"metrics": "1.1.2",
|
||||
"notifications": "0.66.1",
|
||||
"open-on-github": "1.2.1",
|
||||
"package-generator": "1.0.2",
|
||||
"settings-view": "0.244.0",
|
||||
"snippets": "1.0.3",
|
||||
"spell-check": "0.68.5",
|
||||
"status-bar": "1.6.0",
|
||||
"styleguide": "0.47.3",
|
||||
"symbols-view": "0.113.1",
|
||||
"tabs": "0.103.0",
|
||||
"timecop": "0.33.2",
|
||||
"tree-view": "0.210.0",
|
||||
"settings-view": "0.246.0",
|
||||
"snippets": "1.0.5",
|
||||
"spell-check": "0.70.2",
|
||||
"status-bar": "1.7.0",
|
||||
"styleguide": "0.48.0",
|
||||
"symbols-view": "0.114.0",
|
||||
"tabs": "0.104.1",
|
||||
"timecop": "0.34.0",
|
||||
"tree-view": "0.213.1",
|
||||
"update-package-dependencies": "0.10.0",
|
||||
"welcome": "0.35.1",
|
||||
"whitespace": "0.35.0",
|
||||
"welcome": "0.36.0",
|
||||
"whitespace": "0.36.1",
|
||||
"wrap-guide": "0.39.0",
|
||||
"language-c": "0.54.0",
|
||||
"language-c": "0.54.1",
|
||||
"language-clojure": "0.22.1",
|
||||
"language-coffee-script": "0.48.0",
|
||||
"language-csharp": "0.12.1",
|
||||
"language-css": "0.40.1",
|
||||
"language-coffee-script": "0.48.2",
|
||||
"language-csharp": "0.14.0",
|
||||
"language-css": "0.42.0",
|
||||
"language-gfm": "0.88.0",
|
||||
"language-git": "0.15.0",
|
||||
"language-go": "0.43.0",
|
||||
"language-html": "0.46.1",
|
||||
"language-git": "0.19.0",
|
||||
"language-go": "0.43.1",
|
||||
"language-html": "0.47.1",
|
||||
"language-hyperlink": "0.16.1",
|
||||
"language-java": "0.24.0",
|
||||
"language-javascript": "0.122.0",
|
||||
"language-java": "0.25.0",
|
||||
"language-javascript": "0.125.1",
|
||||
"language-json": "0.18.3",
|
||||
"language-less": "0.29.6",
|
||||
"language-make": "0.22.2",
|
||||
"language-mustache": "0.13.0",
|
||||
"language-less": "0.30.1",
|
||||
"language-make": "0.22.3",
|
||||
"language-mustache": "0.13.1",
|
||||
"language-objective-c": "0.15.1",
|
||||
"language-perl": "0.37.0",
|
||||
"language-php": "0.37.3",
|
||||
"language-property-list": "0.8.0",
|
||||
"language-property-list": "0.9.0",
|
||||
"language-python": "0.45.1",
|
||||
"language-ruby": "0.70.2",
|
||||
"language-ruby": "0.70.4",
|
||||
"language-ruby-on-rails": "0.25.1",
|
||||
"language-sass": "0.57.0",
|
||||
"language-shellscript": "0.23.0",
|
||||
"language-sass": "0.57.1",
|
||||
"language-shellscript": "0.25.0",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.25.0",
|
||||
"language-sql": "0.25.2",
|
||||
"language-text": "0.7.1",
|
||||
"language-todo": "0.29.1",
|
||||
"language-toml": "0.18.1",
|
||||
"language-xml": "0.34.12",
|
||||
"language-yaml": "0.27.1"
|
||||
"language-xml": "0.34.15",
|
||||
"language-yaml": "0.27.2"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -7,7 +7,7 @@ URL: https://atom.io/
|
||||
AutoReqProv: no # Avoid libchromiumcontent.so missing dependency
|
||||
Prefix: <%= installDir %>
|
||||
|
||||
Requires: lsb-core-noarch
|
||||
Requires: lsb-core-noarch, libXss.so.1
|
||||
|
||||
%description
|
||||
<%= description %>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
@echo off
|
||||
|
||||
"%~dp0\..\app\apm\bin\node.exe" "%~dp0\..\app\apm\lib\cli.js" %*
|
||||
"%~dp0\..\app\apm\bin\apm.cmd" %*
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
directory=$(dirname "$0")
|
||||
"$directory/../app/apm/bin/node.exe" "$directory/../app/apm/lib/cli.js" "$@"
|
||||
"$(dirname "$0")/../app/apm/apm.sh" "$@"
|
||||
|
||||
BIN
resources/win/folder.ico
Normal file
BIN
resources/win/folder.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@@ -23,6 +23,7 @@ const argv = yargs
|
||||
.wrap(yargs.terminalWidth())
|
||||
.argv
|
||||
|
||||
const checkChromedriverVersion = require('./lib/check-chromedriver-version')
|
||||
const cleanOutputDirectory = require('./lib/clean-output-directory')
|
||||
const codeSignOnMac = require('./lib/code-sign-on-mac')
|
||||
const compressArtifacts = require('./lib/compress-artifacts')
|
||||
@@ -30,7 +31,6 @@ const copyAssets = require('./lib/copy-assets')
|
||||
const createDebianPackage = require('./lib/create-debian-package')
|
||||
const createRpmPackage = require('./lib/create-rpm-package')
|
||||
const createWindowsInstaller = require('./lib/create-windows-installer')
|
||||
const downloadChromedriver = require('./lib/download-chromedriver')
|
||||
const dumpSymbols = require('./lib/dump-symbols')
|
||||
const generateAPIDocs = require('./lib/generate-api-docs')
|
||||
const generateMetadata = require('./lib/generate-metadata')
|
||||
@@ -48,6 +48,7 @@ process.on('unhandledRejection', function (e) {
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
checkChromedriverVersion()
|
||||
cleanOutputDirectory()
|
||||
copyAssets()
|
||||
transpileBabelPaths()
|
||||
@@ -58,7 +59,6 @@ generateModuleCache()
|
||||
prebuildLessCache()
|
||||
generateMetadata()
|
||||
generateAPIDocs()
|
||||
downloadChromedriver()
|
||||
dumpSymbols()
|
||||
.then(packageApplication)
|
||||
.then(packagedAppPath => {
|
||||
|
||||
@@ -866,6 +866,10 @@
|
||||
"hasDeprecations": true,
|
||||
"latestHasDeprecations": true
|
||||
},
|
||||
"language-nlf": {
|
||||
"hasAlternative": true,
|
||||
"alternative": "language-nsis"
|
||||
},
|
||||
"language-rspec": {
|
||||
"version": "<=0.2.1",
|
||||
"hasDeprecations": true,
|
||||
|
||||
22
script/lib/check-chromedriver-version.js
Normal file
22
script/lib/check-chromedriver-version.js
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict'
|
||||
|
||||
const buildMetadata = require('../package.json')
|
||||
const CONFIG = require('../config')
|
||||
const semver = require('semver')
|
||||
|
||||
module.exports = function () {
|
||||
// Chromedriver should be specified as ~x.y where x and y match Electron major/minor
|
||||
const chromedriverVer = buildMetadata.dependencies['electron-chromedriver']
|
||||
|
||||
// Always use tilde on electron-chromedriver so that it can pick up the best patch vesion
|
||||
if (!chromedriverVer.startsWith('~')) {
|
||||
throw new Error(`electron-chromedriver version in script/package.json should start with a tilde to match latest patch version.`)
|
||||
}
|
||||
|
||||
const electronVer = CONFIG.appMetadata.electronVersion
|
||||
if (!semver.satisfies(electronVer, chromedriverVer)) {
|
||||
throw new Error(`electron-chromedriver ${chromedriverVer} incompatible with electron ${electronVer}.\n` +
|
||||
'Did you upgrade electron in package.json and forget to upgrade electron-chromedriver in ' +
|
||||
`script/package.json to '~${semver.major(electronVer)}.${semver.minor(electronVer)}' ?`)
|
||||
}
|
||||
}
|
||||
@@ -7,23 +7,7 @@ const spawnSync = require('./spawn-sync')
|
||||
const CONFIG = require('../config')
|
||||
|
||||
module.exports = function (packagedAppPath) {
|
||||
let appArchiveName
|
||||
if (process.platform === 'darwin') {
|
||||
appArchiveName = 'atom-mac.zip'
|
||||
} else if (process.platform === 'win32') {
|
||||
appArchiveName = 'atom-windows.zip'
|
||||
} else {
|
||||
let arch
|
||||
if (process.arch === 'ia32') {
|
||||
arch = 'i386'
|
||||
} else if (process.arch === 'x64') {
|
||||
arch = 'amd64'
|
||||
} else {
|
||||
arch = process.arch
|
||||
}
|
||||
appArchiveName = `atom-${arch}.tar.gz`
|
||||
}
|
||||
const appArchivePath = path.join(CONFIG.buildOutputPath, appArchiveName)
|
||||
const appArchivePath = path.join(CONFIG.buildOutputPath, getArchiveName())
|
||||
compress(packagedAppPath, appArchivePath)
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
@@ -32,6 +16,22 @@ module.exports = function (packagedAppPath) {
|
||||
}
|
||||
}
|
||||
|
||||
function getArchiveName () {
|
||||
switch (process.platform) {
|
||||
case 'darwin': return 'atom-mac.zip'
|
||||
case 'win32': return `atom-windows.zip`
|
||||
default: return `atom-${getLinuxArchiveArch()}.tar.gz`
|
||||
}
|
||||
}
|
||||
|
||||
function getLinuxArchiveArch () {
|
||||
switch (process.arch) {
|
||||
case 'ia32': return 'i386'
|
||||
case 'x64' : return 'amd64'
|
||||
default: return process.arch
|
||||
}
|
||||
}
|
||||
|
||||
function compress (inputDirPath, outputArchivePath) {
|
||||
if (fs.existsSync(outputArchivePath)) {
|
||||
console.log(`Deleting "${outputArchivePath}"`)
|
||||
|
||||
@@ -11,24 +11,31 @@ const spawnSync = require('./spawn-sync')
|
||||
const CONFIG = require('../config')
|
||||
|
||||
module.exports = function (packagedAppPath, codeSign) {
|
||||
const archSuffix = process.arch === 'ia32' ? '' : '-' + process.arch
|
||||
const options = {
|
||||
appDirectory: packagedAppPath,
|
||||
authors: 'GitHub Inc.',
|
||||
iconUrl: `https://raw.githubusercontent.com/atom/atom/master/resources/app-icons/${CONFIG.channel}/atom.ico`,
|
||||
loadingGif: path.join(CONFIG.repositoryRootPath, 'resources', 'win', 'loading.gif'),
|
||||
outputDirectory: CONFIG.buildOutputPath,
|
||||
remoteReleases: `https://atom.io/api/updates?version=${CONFIG.appMetadata.version}`,
|
||||
remoteReleases: `https://atom.io/api/updates${archSuffix}`,
|
||||
setupIcon: path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.ico')
|
||||
}
|
||||
|
||||
const certPath = path.join(os.tmpdir(), 'win.p12')
|
||||
const signing = codeSign && process.env.WIN_P12KEY_URL
|
||||
const signing = codeSign && process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL
|
||||
|
||||
if (signing) {
|
||||
downloadFileFromGithub(process.env.WIN_P12KEY_URL, certPath)
|
||||
options.certificateFile = certPath
|
||||
options.certificatePassword = process.env.WIN_P12KEY_PASSWORD
|
||||
downloadFileFromGithub(process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL, certPath)
|
||||
var signParams = []
|
||||
signParams.push(`/f ${certPath}`) // Signing cert file
|
||||
signParams.push(`/p ${process.env.ATOM_WIN_CODE_SIGNING_CERT_PASSWORD}`) // Signing cert password
|
||||
signParams.push('/fd sha256') // File digest algorithm
|
||||
signParams.push('/tr http://timestamp.digicert.com') // Time stamp server
|
||||
signParams.push('/td sha256') // Times stamp algorithm
|
||||
options.signWithParams = signParams.join(' ')
|
||||
} else {
|
||||
console.log('Skipping code-signing. Specify the --code-sign option and provide a WIN_P12KEY_URL environment variable to perform code-signing'.gray)
|
||||
console.log('Skipping code-signing. Specify the --code-sign option and provide a ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL environment variable to perform code-signing'.gray)
|
||||
}
|
||||
|
||||
const cleanUp = function () {
|
||||
@@ -50,10 +57,10 @@ module.exports = function (packagedAppPath, codeSign) {
|
||||
if (signing) {
|
||||
for (let nupkgPath of glob.sync(`${CONFIG.buildOutputPath}/*-full.nupkg`)) {
|
||||
if (nupkgPath.includes(CONFIG.appMetadata.version)) {
|
||||
nupkgPath = path.resolve(nupkgPath) // Switch from forward-slash notation
|
||||
console.log(`Extracting signed executables from ${nupkgPath} for use in portable zip`)
|
||||
var atomOutPath = path.join(path.dirname(packagedAppPath), 'Atom')
|
||||
spawnSync('7z.exe', ['e', nupkgPath, 'lib\\net45\\*.exe', '-aoa'], {cwd: atomOutPath})
|
||||
spawnSync(process.env.COMSPEC, ['/c', `move /y ${path.join(atomOutPath, 'squirrel.exe')} ${path.join(atomOutPath, 'update.exe')}`])
|
||||
spawnSync('7z.exe', ['e', nupkgPath, 'lib\\net45\\*.exe', '-aoa', `-o${packagedAppPath}`])
|
||||
spawnSync(process.env.COMSPEC, ['/c', 'move', '/y', path.join(packagedAppPath, 'squirrel.exe'), path.join(packagedAppPath, 'update.exe')])
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ module.exports = {
|
||||
// Include the electron minor version in the fingerprint since that changing requires a re-install
|
||||
const electronVersion = CONFIG.appMetadata.electronVersion.replace(/\.\d+$/, '')
|
||||
const apmVersion = CONFIG.apmMetadata.dependencies['atom-package-manager']
|
||||
const body = electronVersion + apmVersion + process.platform + process.version
|
||||
const body = electronVersion + apmVersion + process.platform + process.version + process.arch
|
||||
return crypto.createHash('sha1').update(body).digest('hex')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const downloadFileFromGithub = require('./download-file-from-github')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const semver = require('semver')
|
||||
const spawnSync = require('./spawn-sync')
|
||||
const syncRequest = require('sync-request')
|
||||
|
||||
const CONFIG = require('../config')
|
||||
|
||||
module.exports = function () {
|
||||
if (process.platform === 'darwin') {
|
||||
// Chromedriver is only distributed with the first patch release for any given
|
||||
// major and minor version of electron.
|
||||
const electronVersion = semver.parse(CONFIG.appMetadata.electronVersion)
|
||||
const electronVersionWithChromedriver = `${electronVersion.major}.${electronVersion.minor}.0`
|
||||
const electronAssets = getElectronAssetsForVersion(electronVersionWithChromedriver)
|
||||
const chromedriverAssets = electronAssets.filter(e => /chromedriver.*darwin-x64/.test(e.name))
|
||||
assert(chromedriverAssets.length === 1, 'Found more than one chrome driver asset to download!')
|
||||
const chromedriverAsset = chromedriverAssets[0]
|
||||
|
||||
const chromedriverZipPath = path.join(CONFIG.electronDownloadPath, `electron-${electronVersionWithChromedriver}-${chromedriverAsset.name}`)
|
||||
if (!fs.existsSync(chromedriverZipPath)) {
|
||||
downloadFileFromGithub(chromedriverAsset.url, chromedriverZipPath)
|
||||
}
|
||||
|
||||
const chromedriverDirPath = path.join(CONFIG.electronDownloadPath, 'chromedriver')
|
||||
unzipPath(chromedriverZipPath, chromedriverDirPath)
|
||||
} else {
|
||||
console.log('Skipping Chromedriver download because it is used only on macOS'.gray)
|
||||
}
|
||||
}
|
||||
|
||||
function getElectronAssetsForVersion (version) {
|
||||
const releaseURL = `https://api.github.com/repos/electron/electron/releases/tags/v${version}`
|
||||
const response = syncRequest('GET', releaseURL, {'headers': {'User-Agent': 'Atom Build'}})
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
const release = JSON.parse(response.body)
|
||||
return release.assets.map(a => { return {name: a.name, url: a.browser_download_url} })
|
||||
} else {
|
||||
throw new Error(`Error getting assets for ${releaseURL}. HTTP Status ${response.statusCode}.`)
|
||||
}
|
||||
}
|
||||
|
||||
function unzipPath (inputPath, outputPath) {
|
||||
if (fs.existsSync(outputPath)) {
|
||||
console.log(`Removing "${outputPath}"`)
|
||||
fs.removeSync(outputPath)
|
||||
}
|
||||
|
||||
console.log(`Unzipping "${inputPath}" to "${outputPath}"`)
|
||||
spawnSync('unzip', [inputPath, '-d', outputPath])
|
||||
}
|
||||
@@ -12,10 +12,6 @@ module.exports = function () {
|
||||
// Set our target (Electron) version so that node-pre-gyp can download the
|
||||
// proper binaries.
|
||||
installEnv.npm_config_target = CONFIG.appMetadata.electronVersion;
|
||||
// Force 32-bit modules on Windows. (Ref.: https://github.com/atom/atom/issues/10450)
|
||||
if (process.platform === 'win32') {
|
||||
installEnv.npm_config_target_arch = 'ia32'
|
||||
}
|
||||
childProcess.execFileSync(
|
||||
CONFIG.getApmBinPath(),
|
||||
['--loglevel=error', 'install'],
|
||||
|
||||
@@ -18,7 +18,7 @@ module.exports = function () {
|
||||
'app-bundle-id': 'com.github.atom',
|
||||
'app-copyright': `Copyright © 2014-${(new Date()).getFullYear()} GitHub, Inc. All rights reserved.`,
|
||||
'app-version': CONFIG.appMetadata.version,
|
||||
'arch': process.platform === 'win32' ? 'ia32' : 'x64',
|
||||
'arch': process.platform === 'darwin' ? 'x64' : process.arch, // OS X is 64-bit only
|
||||
'asar': {unpack: buildAsarUnpackGlobExpression()},
|
||||
'build-version': CONFIG.appMetadata.version,
|
||||
'download': {cache: CONFIG.electronDownloadPath},
|
||||
@@ -73,7 +73,7 @@ function copyNonASARResources (packagedAppPath, bundledResourcesPath) {
|
||||
} else if (process.platform === 'linux') {
|
||||
fs.copySync(path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'png', '1024.png'), path.join(packagedAppPath, 'atom.png'))
|
||||
} else if (process.platform === 'win32') {
|
||||
[ 'atom.cmd', 'atom.sh', 'atom.js', 'apm.cmd', 'apm.sh', 'file.ico' ]
|
||||
[ 'atom.cmd', 'atom.sh', 'atom.js', 'apm.cmd', 'apm.sh', 'file.ico', 'folder.ico' ]
|
||||
.forEach(file => fs.copySync(path.join('resources', 'win', file), path.join(bundledResourcesPath, 'cli', file)))
|
||||
}
|
||||
|
||||
@@ -169,6 +169,9 @@ function renamePackagedAppDir (packageOutputDirPath) {
|
||||
} else {
|
||||
const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
|
||||
packagedAppPath = path.join(CONFIG.buildOutputPath, appName)
|
||||
if (process.platform === 'win32' && process.arch !== 'ia32') {
|
||||
packagedAppPath += ` ${process.arch}`
|
||||
}
|
||||
if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
|
||||
fs.renameSync(packageOutputDirPath, packagedAppPath)
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@ module.exports = function () {
|
||||
function verifyNode () {
|
||||
const fullVersion = process.versions.node
|
||||
const majorVersion = fullVersion.split('.')[0]
|
||||
if (majorVersion >= 4) {
|
||||
if (majorVersion >= 4 && majorVersion < 7) {
|
||||
console.log(`Node:\tv${fullVersion}`)
|
||||
} else if (majorVersion >= 7) {
|
||||
throw new Error(`Atom does not build properly on node v7+. node v${fullVersion} is installed.`)
|
||||
} else {
|
||||
throw new Error(`node v4+ is required to build Atom. node v${fullVersion} is installed.`)
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@
|
||||
"colors": "1.1.2",
|
||||
"csslint": "1.0.2",
|
||||
"donna": "1.0.13",
|
||||
"electron-chromedriver": "~1.3",
|
||||
"electron-packager": "7.3.0",
|
||||
"electron-winstaller": "2.3.4",
|
||||
"electron-winstaller": "2.5.1",
|
||||
"fs-extra": "0.30.0",
|
||||
"glob": "7.0.3",
|
||||
"joanna": "0.0.6",
|
||||
"joanna": "0.0.8",
|
||||
"legal-eagle": "0.13.0",
|
||||
"lodash.template": "4.4.0",
|
||||
"minidump": "0.9.0",
|
||||
|
||||
14
script/test
14
script/test
@@ -93,11 +93,15 @@ function runBenchmarkTests (callback) {
|
||||
cp.on('close', exitCode => { callback(null, exitCode) })
|
||||
}
|
||||
|
||||
let testSuitesToRun
|
||||
if (process.platform === 'darwin') {
|
||||
testSuitesToRun = [runCoreMainProcessTests, runCoreRenderProcessTests, runBenchmarkTests].concat(packageTestSuites)
|
||||
} else {
|
||||
testSuitesToRun = [runCoreMainProcessTests]
|
||||
let testSuitesToRun = testSuitesForPlatform(process.platform)
|
||||
|
||||
function testSuitesForPlatform(platform) {
|
||||
switch(platform) {
|
||||
case 'darwin': return [runCoreMainProcessTests, runCoreRenderProcessTests, runBenchmarkTests].concat(packageTestSuites)
|
||||
case 'win32': return (process.arch === 'x64') ? [runCoreMainProcessTests, runCoreRenderProcessTests] : [runCoreMainProcessTests]
|
||||
case 'linux': return [runCoreMainProcessTests]
|
||||
default: return []
|
||||
}
|
||||
}
|
||||
|
||||
async.series(testSuitesToRun, function (err, exitCodes) {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
_ = require 'underscore-plus'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
AtomEnvironment = require '../src/atom-environment'
|
||||
StorageFolder = require '../src/storage-folder'
|
||||
|
||||
describe "AtomEnvironment", ->
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
describe 'window sizing methods', ->
|
||||
describe '::getPosition and ::setPosition', ->
|
||||
originalPosition = null
|
||||
@@ -324,7 +327,7 @@ describe "AtomEnvironment", ->
|
||||
|
||||
describe "::unloadEditorWindow()", ->
|
||||
it "saves the BlobStore so it can be loaded after reload", ->
|
||||
configDirPath = temp.mkdirSync()
|
||||
configDirPath = temp.mkdirSync('atom-spec-environment')
|
||||
fakeBlobStore = jasmine.createSpyObj("blob store", ["save"])
|
||||
atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, enablePersistence: true, configDirPath, blobStore: fakeBlobStore, window, document})
|
||||
|
||||
@@ -336,7 +339,7 @@ describe "AtomEnvironment", ->
|
||||
|
||||
describe "::destroy()", ->
|
||||
it "does not throw exceptions when unsubscribing from ipc events (regression)", ->
|
||||
configDirPath = temp.mkdirSync()
|
||||
configDirPath = temp.mkdirSync('atom-spec-environment')
|
||||
fakeDocument = {
|
||||
addEventListener: ->
|
||||
removeEventListener: ->
|
||||
@@ -401,6 +404,8 @@ describe "AtomEnvironment", ->
|
||||
subscription?.dispose()
|
||||
|
||||
it "invokes onUpdateAvailable listeners", ->
|
||||
return unless process.platform is 'darwin' # Test tied to electron autoUpdater, we use something else on Linux and Win32
|
||||
|
||||
atom.listenForUpdates()
|
||||
|
||||
updateAvailableHandler = jasmine.createSpy("update-available-handler")
|
||||
|
||||
119
spec/atom-paths-spec.js
Normal file
119
spec/atom-paths-spec.js
Normal file
@@ -0,0 +1,119 @@
|
||||
/** @babel */
|
||||
|
||||
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
|
||||
import {app} from 'remote'
|
||||
import atomPaths from '../src/atom-paths'
|
||||
import fs from 'fs-plus'
|
||||
import path from 'path'
|
||||
const temp = require('temp').track()
|
||||
|
||||
describe("AtomPaths", () => {
|
||||
const portableAtomHomePath = path.join(atomPaths.getAppDirectory(), '.atom')
|
||||
console.log(portableAtomHomePath)
|
||||
|
||||
afterEach(() => {
|
||||
atomPaths.setAtomHome(app.getPath('home'))
|
||||
})
|
||||
|
||||
describe('SetAtomHomePath', () => {
|
||||
describe('when a portable .atom folder exists', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.ATOM_HOME
|
||||
if (!fs.existsSync(portableAtomHomePath))
|
||||
fs.mkdirSync(portableAtomHomePath)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env.ATOM_HOME
|
||||
fs.removeSync(portableAtomHomePath)
|
||||
})
|
||||
|
||||
it('sets ATOM_HOME to the portable .atom folder if it has permission', () => {
|
||||
atomPaths.setAtomHome(app.getPath('home'))
|
||||
expect(process.env.ATOM_HOME).toEqual(portableAtomHomePath)
|
||||
})
|
||||
|
||||
it('uses ATOM_HOME if no write access to portable .atom folder', () => {
|
||||
if (process.platform === 'win32') return
|
||||
|
||||
const readOnlyPath = temp.mkdirSync('atom-path-spec-no-write-access')
|
||||
process.env.ATOM_HOME = readOnlyPath
|
||||
fs.chmodSync(portableAtomHomePath, 444)
|
||||
atomPaths.setAtomHome(app.getPath('home'))
|
||||
expect(process.env.ATOM_HOME).toEqual(readOnlyPath)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when a portable folder does not exist', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.ATOM_HOME
|
||||
fs.removeSync(portableAtomHomePath)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env.ATOM_HOME
|
||||
})
|
||||
|
||||
it('leaves ATOM_HOME unmodified if it was already set', () => {
|
||||
const temporaryHome = temp.mkdirSync('atom-spec-setatomhomepath')
|
||||
process.env.ATOM_HOME = temporaryHome
|
||||
atomPaths.setAtomHome(app.getPath('home'))
|
||||
expect(process.env.ATOM_HOME).toEqual(temporaryHome)
|
||||
})
|
||||
|
||||
it('sets ATOM_HOME to a default location if not yet set', () => {
|
||||
const expectedPath = path.join(app.getPath('home'), '.atom')
|
||||
atomPaths.setAtomHome(app.getPath('home'))
|
||||
expect(process.env.ATOM_HOME).toEqual(expectedPath)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('setUserData', () => {
|
||||
let tempAtomHomePath = null
|
||||
let electronUserDataPath = null
|
||||
let defaultElectronUserDataPath = null
|
||||
|
||||
beforeEach(() => {
|
||||
defaultElectronUserDataPath = app.getPath('userData')
|
||||
delete process.env.ATOM_HOME
|
||||
tempAtomHomePath = temp.mkdirSync('atom-paths-specs-userdata-home')
|
||||
tempAtomConfigPath = path.join(tempAtomHomePath, '.atom')
|
||||
fs.mkdirSync(tempAtomConfigPath)
|
||||
electronUserDataPath = path.join(tempAtomConfigPath, 'electronUserData')
|
||||
atomPaths.setAtomHome(tempAtomHomePath)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env.ATOM_HOME
|
||||
fs.removeSync(electronUserDataPath)
|
||||
temp.cleanupSync()
|
||||
app.setPath('userData', defaultElectronUserDataPath)
|
||||
})
|
||||
|
||||
describe('when an electronUserData folder exists', () => {
|
||||
it('sets userData path to the folder if it has permission', () => {
|
||||
fs.mkdirSync(electronUserDataPath)
|
||||
atomPaths.setUserData(app)
|
||||
expect(app.getPath('userData')).toEqual(electronUserDataPath)
|
||||
})
|
||||
|
||||
it('leaves userData unchanged if no write access to electronUserData folder', () => {
|
||||
if (process.platform === 'win32') return
|
||||
|
||||
fs.mkdirSync(electronUserDataPath)
|
||||
fs.chmodSync(electronUserDataPath, 444)
|
||||
atomPaths.setUserData(app)
|
||||
fs.chmodSync(electronUserDataPath, 666)
|
||||
expect(app.getPath('userData')).toEqual(defaultElectronUserDataPath)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when an electronUserDataPath folder does not exist', () => {
|
||||
it('leaves userData app path unchanged', () => {
|
||||
atomPaths.setUserData(app)
|
||||
expect(app.getPath('userData')).toEqual(defaultElectronUserDataPath)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,67 +0,0 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
AtomPortable = require '../src/main-process/atom-portable'
|
||||
|
||||
portableModeCommonPlatformBehavior = (platform) ->
|
||||
describe "with ATOM_HOME environment variable", ->
|
||||
it "returns false", ->
|
||||
expect(AtomPortable.isPortableInstall(platform, "C:\\some\\path")).toBe false
|
||||
|
||||
describe "without ATOM_HOME environment variable", ->
|
||||
environmentAtomHome = undefined
|
||||
portableAtomHomePath = path.join(path.dirname(process.execPath), "..", ".atom")
|
||||
portableAtomHomeNaturallyExists = fs.existsSync(portableAtomHomePath)
|
||||
portableAtomHomeBackupPath = "#{portableAtomHomePath}.temp"
|
||||
|
||||
beforeEach ->
|
||||
fs.renameSync(portableAtomHomePath, portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomePath)
|
||||
|
||||
afterEach ->
|
||||
if portableAtomHomeNaturallyExists
|
||||
fs.renameSync(portableAtomHomeBackupPath, portableAtomHomePath) if not fs.existsSync(portableAtomHomePath)
|
||||
else
|
||||
fs.removeSync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath)
|
||||
fs.removeSync(portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomeBackupPath)
|
||||
|
||||
describe "with .atom directory sibling to exec", ->
|
||||
beforeEach ->
|
||||
fs.mkdirSync(portableAtomHomePath) if not fs.existsSync(portableAtomHomePath)
|
||||
|
||||
describe "without .atom directory sibling to exec", ->
|
||||
beforeEach ->
|
||||
fs.removeSync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath)
|
||||
|
||||
it "returns false", ->
|
||||
expect(AtomPortable.isPortableInstall(platform, environmentAtomHome)).toBe false
|
||||
|
||||
describe "Set Portable Mode on #win32", ->
|
||||
portableAtomHomePath = path.join(path.dirname(process.execPath), "..", ".atom")
|
||||
portableAtomHomeNaturallyExists = fs.existsSync(portableAtomHomePath)
|
||||
portableAtomHomeBackupPath = "#{portableAtomHomePath}.temp"
|
||||
|
||||
beforeEach ->
|
||||
fs.renameSync(portableAtomHomePath, portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomePath)
|
||||
|
||||
afterEach ->
|
||||
if portableAtomHomeNaturallyExists
|
||||
fs.renameSync(portableAtomHomeBackupPath, portableAtomHomePath) if not fs.existsSync(portableAtomHomePath)
|
||||
else
|
||||
fs.removeSync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath)
|
||||
fs.removeSync(portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomeBackupPath)
|
||||
|
||||
it "creates a portable home directory", ->
|
||||
expect(fs.existsSync(portableAtomHomePath)).toBe false
|
||||
|
||||
AtomPortable.setPortable(process.env.ATOM_HOME)
|
||||
expect(fs.existsSync(portableAtomHomePath)).toBe true
|
||||
|
||||
describe "Check for Portable Mode", ->
|
||||
describe "Windows", ->
|
||||
portableModeCommonPlatformBehavior "win32"
|
||||
|
||||
describe "Mac", ->
|
||||
it "returns false", ->
|
||||
expect(AtomPortable.isPortableInstall("darwin", "darwin")).toBe false
|
||||
|
||||
describe "Linux", ->
|
||||
portableModeCommonPlatformBehavior "linux"
|
||||
@@ -5,6 +5,9 @@ import {remote} from 'electron'
|
||||
const electronAutoUpdater = remote.require('electron').autoUpdater
|
||||
|
||||
describe('AutoUpdateManager (renderer)', () => {
|
||||
|
||||
if (process.platform !== 'darwin') return // Tests are tied to electron autoUpdater, we use something else on Linux and Win32
|
||||
|
||||
let autoUpdateManager
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -19,6 +19,7 @@ describe "Babel transpiler support", ->
|
||||
|
||||
afterEach ->
|
||||
CompileCache.setCacheDirectory(originalCacheDir)
|
||||
temp.cleanupSync()
|
||||
|
||||
describe 'when a .js file starts with /** @babel */;', ->
|
||||
it "transpiles it using babel", ->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
CommandInstaller = require '../src/command-installer'
|
||||
|
||||
describe "CommandInstaller on #darwin", ->
|
||||
@@ -20,6 +20,9 @@ describe "CommandInstaller on #darwin", ->
|
||||
spyOn(CommandInstaller::, 'getResourcesDirectory').andReturn(resourcesPath)
|
||||
spyOn(CommandInstaller::, 'getInstallDirectory').andReturn(installationPath)
|
||||
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
it "shows an error dialog when installing commands interactively fails", ->
|
||||
appDelegate = jasmine.createSpyObj("appDelegate", ["confirm"])
|
||||
installer = new CommandInstaller("2.0.2", appDelegate)
|
||||
|
||||
@@ -21,8 +21,9 @@ describe 'CompileCache', ->
|
||||
spyOn(TypeScriptSimple::, 'compile').andReturn 'the-typescript-code'
|
||||
|
||||
afterEach ->
|
||||
CSON.setCacheDir(CompileCache.getCacheDirectory())
|
||||
CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME)
|
||||
CSON.setCacheDir(CompileCache.getCacheDirectory())
|
||||
temp.cleanupSync()
|
||||
|
||||
describe 'addPathToCache(filePath, atomHome)', ->
|
||||
describe 'when the given file is plain javascript', ->
|
||||
@@ -77,6 +78,8 @@ describe 'CompileCache', ->
|
||||
|
||||
describe 'overriding Error.prepareStackTrace', ->
|
||||
it 'removes the override on the next tick, and always assigns the raw stack', ->
|
||||
return if process.platform is 'win32' # Flakey Error.stack contents on Win32
|
||||
|
||||
Error.prepareStackTrace = -> 'a-stack-trace'
|
||||
|
||||
error = new Error("Oops")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
CSON = require 'season'
|
||||
fs = require 'fs-plus'
|
||||
|
||||
@@ -9,13 +9,14 @@ describe "Config", ->
|
||||
beforeEach ->
|
||||
spyOn(atom.config, "load")
|
||||
spyOn(atom.config, "save")
|
||||
dotAtomPath = temp.path('dot-atom-dir')
|
||||
dotAtomPath = temp.path('atom-spec-config')
|
||||
atom.config.configDirPath = dotAtomPath
|
||||
atom.config.enablePersistence = true
|
||||
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
|
||||
|
||||
afterEach ->
|
||||
atom.config.enablePersistence = false
|
||||
fs.removeSync(dotAtomPath)
|
||||
|
||||
describe ".get(keyPath, {scope, sources, excludeSources})", ->
|
||||
it "allows a key path's value to be read", ->
|
||||
@@ -486,8 +487,8 @@ describe "Config", ->
|
||||
observeHandler.reset() # clear the initial call
|
||||
atom.config.set('foo.bar.baz', "value 2")
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 2")
|
||||
observeHandler.reset()
|
||||
|
||||
observeHandler.reset()
|
||||
atom.config.set('foo.bar.baz', "value 1")
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 1")
|
||||
advanceClock(100) # complete pending save that was requested in ::set
|
||||
@@ -1079,6 +1080,7 @@ describe "Config", ->
|
||||
|
||||
describe "when the configDirPath doesn't exist", ->
|
||||
it "copies the contents of dot-atom to ~/.atom", ->
|
||||
return if process.platform is 'win32' # Flakey test on Win32
|
||||
initializationDone = false
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
atom.config.initializeConfigDirectory ->
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
DefaultDirectoryProvider = require '../src/default-directory-provider'
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
|
||||
describe "DefaultDirectoryProvider", ->
|
||||
tmp = null
|
||||
|
||||
beforeEach ->
|
||||
tmp = temp.mkdirSync('atom-spec-default-dir-provider')
|
||||
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
describe ".directoryForURISync(uri)", ->
|
||||
it "returns a Directory with a path that matches the uri", ->
|
||||
provider = new DefaultDirectoryProvider()
|
||||
tmp = temp.mkdirSync()
|
||||
|
||||
directory = provider.directoryForURISync(tmp)
|
||||
expect(directory.getPath()).toEqual tmp
|
||||
|
||||
it "normalizes its input before creating a Directory for it", ->
|
||||
provider = new DefaultDirectoryProvider()
|
||||
tmp = temp.mkdirSync()
|
||||
nonNormalizedPath = tmp + path.sep + ".." + path.sep + path.basename(tmp)
|
||||
expect(tmp.includes("..")).toBe false
|
||||
expect(nonNormalizedPath.includes("..")).toBe true
|
||||
@@ -24,7 +30,6 @@ describe "DefaultDirectoryProvider", ->
|
||||
|
||||
it "creates a Directory for its parent dir when passed a file", ->
|
||||
provider = new DefaultDirectoryProvider()
|
||||
tmp = temp.mkdirSync()
|
||||
file = path.join(tmp, "example.txt")
|
||||
fs.writeFileSync(file, "data")
|
||||
|
||||
@@ -40,7 +45,6 @@ describe "DefaultDirectoryProvider", ->
|
||||
describe ".directoryForURI(uri)", ->
|
||||
it "returns a Promise that resolves to a Directory with a path that matches the uri", ->
|
||||
provider = new DefaultDirectoryProvider()
|
||||
tmp = temp.mkdirSync()
|
||||
|
||||
waitsForPromise ->
|
||||
provider.directoryForURI(tmp).then (directory) ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
FileSystemBlobStore = require '../src/file-system-blob-store'
|
||||
@@ -7,9 +7,12 @@ describe "FileSystemBlobStore", ->
|
||||
[storageDirectory, blobStore] = []
|
||||
|
||||
beforeEach ->
|
||||
storageDirectory = temp.path()
|
||||
storageDirectory = temp.path('atom-spec-filesystemblobstore')
|
||||
blobStore = FileSystemBlobStore.load(storageDirectory)
|
||||
|
||||
afterEach ->
|
||||
fs.removeSync(storageDirectory)
|
||||
|
||||
it "is empty when the file doesn't exist", ->
|
||||
expect(blobStore.get("foo", "invalidation-key-1")).toBeUndefined()
|
||||
expect(blobStore.get("bar", "invalidation-key-2")).toBeUndefined()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = {
|
||||
initialize() {},
|
||||
activate () {},
|
||||
|
||||
deserializeMethod1 (state) {
|
||||
|
||||
2
spec/fixtures/sample.txt
vendored
2
spec/fixtures/sample.txt
vendored
@@ -1 +1 @@
|
||||
Some text.
|
||||
Some textSome text.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
{Directory} = require 'pathwatcher'
|
||||
GitRepository = require '../src/git-repository'
|
||||
GitRepositoryProvider = require '../src/git-repository-provider'
|
||||
@@ -11,6 +11,9 @@ describe "GitRepositoryProvider", ->
|
||||
beforeEach ->
|
||||
provider = new GitRepositoryProvider(atom.project, atom.config, atom.confirm)
|
||||
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
describe ".repositoryForDirectory(directory)", ->
|
||||
describe "when specified a Directory with a Git repository", ->
|
||||
it "returns a Promise that resolves to a GitRepository", ->
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
GitRepository = require '../src/git-repository'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
Project = require '../src/project'
|
||||
|
||||
copyRepository = ->
|
||||
workingDirPath = temp.mkdirSync('atom-working-dir')
|
||||
workingDirPath = temp.mkdirSync('atom-spec-git')
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'git', 'working-dir'), workingDirPath)
|
||||
fs.renameSync(path.join(workingDirPath, 'git.git'), path.join(workingDirPath, '.git'))
|
||||
workingDirPath
|
||||
@@ -19,6 +19,8 @@ describe "GitRepository", ->
|
||||
|
||||
afterEach ->
|
||||
repo.destroy() if repo?.repo?
|
||||
try
|
||||
temp.cleanupSync() # These tests sometimes lag at shutting down resources
|
||||
|
||||
describe "@open(path)", ->
|
||||
it "returns null when no repository is found", ->
|
||||
@@ -29,10 +31,15 @@ describe "GitRepository", ->
|
||||
expect(-> new GitRepository(path.join(temp.dir, 'nogit.txt'))).toThrow()
|
||||
|
||||
describe ".getPath()", ->
|
||||
it "returns the repository path for a .git directory path", ->
|
||||
it "returns the repository path for a .git directory path with a file", ->
|
||||
return if process.platform is 'win32' #Win32TestFailures - libgit2 does not detect files in .git folders
|
||||
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git', 'HEAD'))
|
||||
expect(repo.getPath()).toBe path.join(__dirname, 'fixtures', 'git', 'master.git')
|
||||
|
||||
it "returns the repository path for a .git directory path with a directory", ->
|
||||
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects'))
|
||||
expect(repo.getPath()).toBe path.join(__dirname, 'fixtures', 'git', 'master.git')
|
||||
|
||||
it "returns the repository path for a repository path", ->
|
||||
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
expect(repo.getPath()).toBe path.join(__dirname, 'fixtures', 'git', 'master.git')
|
||||
@@ -137,6 +144,8 @@ describe "GitRepository", ->
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
|
||||
it "displays a confirmation dialog by default", ->
|
||||
return if process.platform is 'win32' # Permissions issues with this test on Windows
|
||||
|
||||
atom.confirm.andCallFake ({buttons}) -> buttons.OK()
|
||||
atom.config.set('editor.confirmCheckoutHeadRevision', true)
|
||||
|
||||
@@ -145,6 +154,7 @@ describe "GitRepository", ->
|
||||
expect(fs.readFileSync(filePath, 'utf8')).toBe ''
|
||||
|
||||
it "does not display a dialog when confirmation is disabled", ->
|
||||
return if process.platform is 'win32' # Flakey EPERM opening a.txt on Win32
|
||||
atom.config.set('editor.confirmCheckoutHeadRevision', false)
|
||||
|
||||
repo.checkoutHeadForEditor(editor)
|
||||
@@ -154,7 +164,7 @@ describe "GitRepository", ->
|
||||
|
||||
describe ".destroy()", ->
|
||||
it "throws an exception when any method is called after it is called", ->
|
||||
repo = new GitRepository(require.resolve('./fixtures/git/master.git/HEAD'))
|
||||
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
repo.destroy()
|
||||
expect(-> repo.getShortHead()).toThrow()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
GrammarRegistry = require '../src/grammar-registry'
|
||||
Grim = require 'grim'
|
||||
|
||||
@@ -24,6 +24,7 @@ describe "the `grammars` global", ->
|
||||
afterEach ->
|
||||
atom.packages.deactivatePackages()
|
||||
atom.packages.unloadPackages()
|
||||
temp.cleanupSync()
|
||||
|
||||
describe ".selectGrammar(filePath)", ->
|
||||
it "always returns a grammar", ->
|
||||
@@ -96,6 +97,7 @@ describe "the `grammars` global", ->
|
||||
)
|
||||
grammar1 = atom.grammars.loadGrammarSync(grammarPath1)
|
||||
expect(atom.grammars.selectGrammar('more.test', '')).toBe grammar1
|
||||
fs.removeSync(grammarPath1)
|
||||
|
||||
grammarPath2 = temp.path(suffix: '.json')
|
||||
fs.writeFileSync grammarPath2, JSON.stringify(
|
||||
@@ -105,6 +107,7 @@ describe "the `grammars` global", ->
|
||||
)
|
||||
grammar2 = atom.grammars.loadGrammarSync(grammarPath2)
|
||||
expect(atom.grammars.selectGrammar('more.test', '')).toBe grammar2
|
||||
fs.removeSync(grammarPath2)
|
||||
|
||||
it "favors non-bundled packages when breaking scoring ties", ->
|
||||
waitsForPromise ->
|
||||
|
||||
@@ -139,3 +139,22 @@ describe "GutterContainerComponent", ->
|
||||
expect(expectedCustomGutterNode1).toBe atom.views.getView(customGutter1)
|
||||
expectedCustomGutterNode3 = gutterContainerComponent.getDomNode().children.item(2)
|
||||
expect(expectedCustomGutterNode3).toBe atom.views.getView(customGutter3)
|
||||
|
||||
it "reorders correctly when prepending multiple gutters at once", ->
|
||||
lineNumberGutter = new Gutter(mockGutterContainer, {name: 'line-number'})
|
||||
testState = buildTestState([lineNumberGutter])
|
||||
gutterContainerComponent.updateSync(testState)
|
||||
expect(gutterContainerComponent.getDomNode().children.length).toBe 1
|
||||
expectedCustomGutterNode = gutterContainerComponent.getDomNode().children.item(0)
|
||||
expect(expectedCustomGutterNode).toBe atom.views.getView(lineNumberGutter)
|
||||
|
||||
# Prepend two gutters at once
|
||||
customGutter1 = new Gutter(mockGutterContainer, {name: 'first', priority: -200})
|
||||
customGutter2 = new Gutter(mockGutterContainer, {name: 'second', priority: -100})
|
||||
testState = buildTestState([customGutter1, customGutter2, lineNumberGutter])
|
||||
gutterContainerComponent.updateSync(testState)
|
||||
expect(gutterContainerComponent.getDomNode().children.length).toBe 3
|
||||
expectedCustomGutterNode1 = gutterContainerComponent.getDomNode().children.item(0)
|
||||
expect(expectedCustomGutterNode1).toBe atom.views.getView(customGutter1)
|
||||
expectedCustomGutterNode2 = gutterContainerComponent.getDomNode().children.item(1)
|
||||
expect(expectedCustomGutterNode2).toBe atom.views.getView(customGutter2)
|
||||
|
||||
199
spec/history-manager-spec.js
Normal file
199
spec/history-manager-spec.js
Normal file
@@ -0,0 +1,199 @@
|
||||
/** @babel */
|
||||
|
||||
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
|
||||
import {Emitter, Disposable, CompositeDisposable} from 'event-kit'
|
||||
|
||||
import {HistoryManager, HistoryProject} from '../src/history-manager'
|
||||
|
||||
describe("HistoryManager", () => {
|
||||
let historyManager, commandRegistry, project, localStorage, stateStore
|
||||
let commandDisposable, projectDisposable
|
||||
|
||||
beforeEach(() => {
|
||||
commandDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
|
||||
commandRegistry = jasmine.createSpyObj('CommandRegistry', ['add'])
|
||||
commandRegistry.add.andReturn(commandDisposable)
|
||||
|
||||
localStorage = jasmine.createSpyObj('LocalStorage', ['getItem', 'setItem'])
|
||||
localStorage.items = {
|
||||
history: JSON.stringify({
|
||||
projects: [
|
||||
{ paths: ['/1', 'c:\\2'], lastOpened: new Date(2016, 9, 17, 17, 16, 23) },
|
||||
{ paths: ['/test'], lastOpened: new Date(2016, 9, 17, 11, 12, 13) }
|
||||
]
|
||||
})
|
||||
}
|
||||
localStorage.getItem.andCallFake((key) => localStorage.items[key])
|
||||
localStorage.setItem.andCallFake((key, value) => localStorage.items[key] = value)
|
||||
|
||||
projectDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
|
||||
project = jasmine.createSpyObj('Project', ['onDidChangePaths'])
|
||||
project.onDidChangePaths.andCallFake((f) => {
|
||||
project.didChangePathsListener = f
|
||||
return projectDisposable
|
||||
})
|
||||
|
||||
historyManager = new HistoryManager({project, commands:commandRegistry, localStorage})
|
||||
})
|
||||
|
||||
describe("constructor", () => {
|
||||
it("registers the 'clear-project-history' command function", () => {
|
||||
expect(commandRegistry.add).toHaveBeenCalled()
|
||||
const cmdCall = commandRegistry.add.calls[0]
|
||||
expect(cmdCall.args.length).toBe(2)
|
||||
expect(cmdCall.args[0]).toBe('atom-workspace')
|
||||
expect(typeof cmdCall.args[1]['application:clear-project-history']).toBe('function')
|
||||
})
|
||||
|
||||
describe("getProjects", () => {
|
||||
it("returns an array of HistoryProjects", () => {
|
||||
expect(historyManager.getProjects()).toEqual([
|
||||
new HistoryProject(['/1', 'c:\\2'], new Date(2016, 9, 17, 17, 16, 23)),
|
||||
new HistoryProject(['/test'], new Date(2016, 9, 17, 11, 12, 13))
|
||||
])
|
||||
})
|
||||
|
||||
it("returns an array of HistoryProjects that is not mutable state", () => {
|
||||
const firstProjects = historyManager.getProjects()
|
||||
firstProjects.pop()
|
||||
firstProjects[0].path = 'modified'
|
||||
|
||||
const secondProjects = historyManager.getProjects()
|
||||
expect(secondProjects.length).toBe(2)
|
||||
expect(secondProjects[0].path).not.toBe('modified')
|
||||
})
|
||||
})
|
||||
|
||||
describe("clearProjects", () => {
|
||||
it("clears the list of projects", () => {
|
||||
expect(historyManager.getProjects().length).not.toBe(0)
|
||||
historyManager.clearProjects()
|
||||
expect(historyManager.getProjects().length).toBe(0)
|
||||
})
|
||||
|
||||
it("saves the state", () => {
|
||||
expect(localStorage.setItem).not.toHaveBeenCalled()
|
||||
historyManager.clearProjects()
|
||||
expect(localStorage.setItem).toHaveBeenCalled()
|
||||
expect(localStorage.setItem.calls[0].args[0]).toBe('history')
|
||||
expect(historyManager.getProjects().length).toBe(0)
|
||||
})
|
||||
|
||||
it("fires the onDidChangeProjects event", () => {
|
||||
expect(localStorage.setItem).not.toHaveBeenCalled()
|
||||
historyManager.clearProjects()
|
||||
expect(localStorage.setItem).toHaveBeenCalled()
|
||||
expect(localStorage.setItem.calls[0].args[0]).toBe('history')
|
||||
expect(historyManager.getProjects().length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("loads state", () => {
|
||||
expect(localStorage.getItem).toHaveBeenCalledWith('history')
|
||||
})
|
||||
|
||||
it("listens to project.onDidChangePaths adding a new project", () => {
|
||||
const start = new Date()
|
||||
project.didChangePathsListener(['/a/new', '/path/or/two'])
|
||||
const projects = historyManager.getProjects()
|
||||
expect(projects.length).toBe(3)
|
||||
expect(projects[0].paths).toEqual(['/a/new', '/path/or/two'])
|
||||
expect(projects[0].lastOpened).not.toBeLessThan(start)
|
||||
})
|
||||
|
||||
it("listens to project.onDidChangePaths updating an existing project", () => {
|
||||
const start = new Date()
|
||||
project.didChangePathsListener(['/test'])
|
||||
const projects = historyManager.getProjects()
|
||||
expect(projects.length).toBe(2)
|
||||
expect(projects[0].paths).toEqual(['/test'])
|
||||
expect(projects[0].lastOpened).not.toBeLessThan(start)
|
||||
})
|
||||
})
|
||||
|
||||
describe("loadState", () => {
|
||||
it("defaults to an empty array if no state", () => {
|
||||
localStorage.items.history = null
|
||||
historyManager.loadState()
|
||||
expect(historyManager.getProjects()).toEqual([])
|
||||
})
|
||||
|
||||
it("defaults to an empty array if no projects", () => {
|
||||
localStorage.items.history = JSON.stringify('')
|
||||
historyManager.loadState()
|
||||
expect(historyManager.getProjects()).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe("addProject", () => {
|
||||
it("adds a new project to the end", () => {
|
||||
const date = new Date(2010, 10, 9, 8, 7, 6)
|
||||
historyManager.addProject(['/a/b'], date)
|
||||
const projects = historyManager.getProjects()
|
||||
expect(projects.length).toBe(3)
|
||||
expect(projects[2].paths).toEqual(['/a/b'])
|
||||
expect(projects[2].lastOpened).toBe(date)
|
||||
})
|
||||
|
||||
it("adds a new project to the start", () => {
|
||||
const date = new Date()
|
||||
historyManager.addProject(['/so/new'], date)
|
||||
const projects = historyManager.getProjects()
|
||||
expect(projects.length).toBe(3)
|
||||
expect(projects[0].paths).toEqual(['/so/new'])
|
||||
expect(projects[0].lastOpened).toBe(date)
|
||||
})
|
||||
|
||||
it("updates an existing project and moves it to the start", () => {
|
||||
const date = new Date()
|
||||
historyManager.addProject(['/test'], date)
|
||||
const projects = historyManager.getProjects()
|
||||
expect(projects.length).toBe(2)
|
||||
expect(projects[0].paths).toEqual(['/test'])
|
||||
expect(projects[0].lastOpened).toBe(date)
|
||||
})
|
||||
|
||||
it("fires the onDidChangeProjects event when adding a project", () => {
|
||||
const didChangeSpy = jasmine.createSpy()
|
||||
const beforeCount = historyManager.getProjects().length
|
||||
historyManager.onDidChangeProjects(didChangeSpy)
|
||||
historyManager.addProject(['/test-new'], new Date())
|
||||
expect(didChangeSpy).toHaveBeenCalled()
|
||||
expect(historyManager.getProjects().length).toBe(beforeCount + 1)
|
||||
})
|
||||
|
||||
it("fires the onDidChangeProjects event when updating a project", () => {
|
||||
const didChangeSpy = jasmine.createSpy()
|
||||
const beforeCount = historyManager.getProjects().length
|
||||
historyManager.onDidChangeProjects(didChangeSpy)
|
||||
historyManager.addProject(['/test'], new Date())
|
||||
expect(didChangeSpy).toHaveBeenCalled()
|
||||
expect(historyManager.getProjects().length).toBe(beforeCount)
|
||||
})
|
||||
})
|
||||
|
||||
describe("getProject", () => {
|
||||
it("returns a project that matches the paths", () => {
|
||||
const project = historyManager.getProject(['/1', 'c:\\2'])
|
||||
expect(project).not.toBeNull()
|
||||
expect(project.paths).toEqual(['/1', 'c:\\2'])
|
||||
})
|
||||
|
||||
it("returns null when it can't find the project", () => {
|
||||
const project = historyManager.getProject(['/1'])
|
||||
expect(project).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe("saveState" ,() => {
|
||||
it("saves the state", () => {
|
||||
historyManager.addProject(["/save/state"])
|
||||
historyManager.saveState()
|
||||
expect(localStorage.setItem).toHaveBeenCalled()
|
||||
expect(localStorage.setItem.calls[0].args[0]).toBe('history')
|
||||
expect(localStorage.items['history']).toContain('/save/state')
|
||||
historyManager.loadState()
|
||||
expect(historyManager.getProjects()[0].paths).toEqual(['/save/state'])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -10,13 +10,13 @@ webdriverio = require '../../../script/node_modules/webdriverio'
|
||||
|
||||
AtomPath = remote.process.argv[0]
|
||||
AtomLauncherPath = path.join(__dirname, "..", "helpers", "atom-launcher.sh")
|
||||
ChromedriverPath = path.resolve(__dirname, '..', '..', '..', 'electron', 'chromedriver', 'chromedriver')
|
||||
ChromedriverPath = path.resolve(__dirname, '..', '..', '..', 'script', 'node_modules', 'electron-chromedriver', 'bin', 'chromedriver')
|
||||
SocketPath = path.join(os.tmpdir(), "atom-integration-test-#{Date.now()}.sock")
|
||||
ChromedriverPort = 9515
|
||||
ChromedriverURLBase = "/wd/hub"
|
||||
ChromedriverStatusURL = "http://localhost:#{ChromedriverPort}#{ChromedriverURLBase}/status"
|
||||
|
||||
userDataDir = temp.mkdirSync('atom-user-data-dir')
|
||||
userDataDir = null
|
||||
|
||||
chromeDriverUp = (done) ->
|
||||
checkStatus = ->
|
||||
@@ -38,6 +38,7 @@ chromeDriverDown = (done) ->
|
||||
setTimeout(checkStatus, 100)
|
||||
|
||||
buildAtomClient = (args, env) ->
|
||||
userDataDir = temp.mkdirSync('atom-user-data-dir')
|
||||
client = webdriverio.remote(
|
||||
host: 'localhost'
|
||||
port: ChromedriverPort
|
||||
|
||||
@@ -5,6 +5,8 @@ temp = require('temp').track()
|
||||
runAtom = require './helpers/start-atom'
|
||||
|
||||
describe "Smoke Test", ->
|
||||
return unless process.platform is 'darwin' # Fails on win32
|
||||
|
||||
atomHome = temp.mkdirSync('atom-home')
|
||||
|
||||
beforeEach ->
|
||||
|
||||
23
spec/keymap-extensions-spec.coffee
Normal file
23
spec/keymap-extensions-spec.coffee
Normal file
@@ -0,0 +1,23 @@
|
||||
path = require 'path'
|
||||
temp = require('temp').track()
|
||||
CSON = require 'season'
|
||||
fs = require 'fs-plus'
|
||||
|
||||
describe "keymap-extensions", ->
|
||||
|
||||
beforeEach ->
|
||||
atom.keymaps.configDirPath = temp.path('atom-spec-keymap-ext')
|
||||
fs.writeFileSync(atom.keymaps.getUserKeymapPath(), '#')
|
||||
@userKeymapLoaded = ->
|
||||
atom.keymaps.onDidLoadUserKeymap => @userKeymapLoaded()
|
||||
|
||||
afterEach ->
|
||||
fs.removeSync(atom.keymaps.configDirPath)
|
||||
atom.keymaps.destroy()
|
||||
|
||||
describe "did-load-user-keymap", ->
|
||||
|
||||
it "fires when user keymap is loaded", ->
|
||||
spyOn(this, 'userKeymapLoaded')
|
||||
atom.keymaps.loadUserKeymap()
|
||||
expect(@userKeymapLoaded).toHaveBeenCalled()
|
||||
@@ -78,9 +78,10 @@ describe "LinesYardstick", ->
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 0))).toEqual({left: 0, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 1))).toEqual({left: 7, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 5))).toEqual({left: 38, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 6))).toEqual({left: 43, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 9))).toEqual({left: 72, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(2, Infinity))).toEqual({left: 287.859375, top: 28})
|
||||
if process.platform is 'darwin' # One pixel off on left on Win32
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 6))).toEqual({left: 43, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 9))).toEqual({left: 72, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(2, Infinity))).toEqual({left: 287.875, top: 28})
|
||||
|
||||
it "reuses already computed pixel positions unless it is invalidated", ->
|
||||
atom.styles.addStyleSheet """
|
||||
@@ -133,72 +134,92 @@ describe "LinesYardstick", ->
|
||||
|
||||
editor.setText(text)
|
||||
|
||||
return unless process.platform is 'darwin' # These numbers are 15 higher on win32 and always integer
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 35)).left).toBe 230.90625
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 36)).left).toBe 237.5
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 37)).left).toBe 244.09375
|
||||
|
||||
describe "::screenPositionForPixelPosition(pixelPosition)", ->
|
||||
it "converts pixel positions to screen positions", ->
|
||||
if process.platform is 'darwin' # Expectations fail on win32
|
||||
it "handles lines containing a mix of left-to-right and right-to-left characters", ->
|
||||
editor.setText('Persian, locally known as Parsi or Farsi (زبان فارسی), the predominant modern descendant of Old Persian.\n')
|
||||
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.syntax--function {
|
||||
font-size: 16px
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 12.5})).toEqual([0, 2])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 14, left: 18.8})).toEqual([1, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 28, left: 100})).toEqual([2, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 32, left: 24.3})).toEqual([2, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 46, left: 66.5})).toEqual([3, 9])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 99.9})).toEqual([5, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 224.2365234375})).toEqual([5, 29])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 225})).toEqual([5, 30])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 84, left: 247.1})).toEqual([6, 33])
|
||||
|
||||
it "overshoots to the nearest character when text nodes are not spatially contiguous", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
buildLineNode = (screenRow) ->
|
||||
lineNode = document.createElement("div")
|
||||
lineNode.style.whiteSpace = "pre"
|
||||
lineNode.innerHTML = '<span>foo</span><span style="margin-left: 40px">bar</span>'
|
||||
jasmine.attachToDOM(lineNode)
|
||||
createdLineNodes.push(lineNode)
|
||||
lineNode
|
||||
editor.setText("foobar")
|
||||
lineTopIndex = new LineTopIndex({defaultLineHeight: editor.getLineHeightInPixels()})
|
||||
linesYardstick = new LinesYardstick(editor, mockLineNodesProvider, lineTopIndex, atom.grammars)
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 15))).toEqual({left: 126, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 62))).toEqual({left: 521, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 58))).toEqual({left: 487, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, Infinity))).toEqual({left: 873.625, top: 0})
|
||||
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 7})).toEqual([0, 1])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 14})).toEqual([0, 2])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 21})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 30})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 50})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 62})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 69})).toEqual([0, 4])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 76})).toEqual([0, 5])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 100})).toEqual([0, 6])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 200})).toEqual([0, 6])
|
||||
describe "::screenPositionForPixelPosition(pixelPosition)", ->
|
||||
it "converts pixel positions to screen positions", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.syntax--function {
|
||||
font-size: 16px
|
||||
}
|
||||
"""
|
||||
|
||||
it "clips pixel positions above buffer start", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: Infinity)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -1, left: Infinity)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: 0, left: Infinity)).toEqual [0, 29]
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 12.5})).toEqual([0, 2])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 14, left: 18.8})).toEqual([1, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 28, left: 100})).toEqual([2, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 32, left: 24.3})).toEqual([2, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 46, left: 66.5})).toEqual([3, 9])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 99.9})).toEqual([5, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 225})).toEqual([5, 30])
|
||||
return unless process.platform is 'darwin' # Following tests are 1 pixel off on Win32
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 224.2365234375})).toEqual([5, 29])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 84, left: 247.1})).toEqual([6, 33])
|
||||
|
||||
it "clips pixel positions below buffer end", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: Infinity, left: -Infinity)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: Infinity, left: Infinity)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: (editor.getLastScreenRow() + 1) * 14, left: 0)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: editor.getLastScreenRow() * 14, left: 0)).toEqual [12, 0]
|
||||
it "overshoots to the nearest character when text nodes are not spatially contiguous", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
it "clips negative horizontal pixel positions", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: 0, left: -10)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: 1 * 14, left: -10)).toEqual [1, 0]
|
||||
buildLineNode = (screenRow) ->
|
||||
lineNode = document.createElement("div")
|
||||
lineNode.style.whiteSpace = "pre"
|
||||
lineNode.innerHTML = '<span>foo</span><span style="margin-left: 40px">bar</span>'
|
||||
jasmine.attachToDOM(lineNode)
|
||||
createdLineNodes.push(lineNode)
|
||||
lineNode
|
||||
editor.setText("foobar")
|
||||
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 7})).toEqual([0, 1])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 14})).toEqual([0, 2])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 21})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 30})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 50})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 62})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 69})).toEqual([0, 4])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 76})).toEqual([0, 5])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 100})).toEqual([0, 6])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 200})).toEqual([0, 6])
|
||||
|
||||
it "clips pixel positions above buffer start", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: Infinity)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -1, left: Infinity)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: 0, left: Infinity)).toEqual [0, 29]
|
||||
|
||||
it "clips pixel positions below buffer end", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: Infinity, left: -Infinity)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: Infinity, left: Infinity)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: (editor.getLastScreenRow() + 1) * 14, left: 0)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: editor.getLastScreenRow() * 14, left: 0)).toEqual [12, 0]
|
||||
|
||||
it "clips negative horizontal pixel positions", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: 0, left: -10)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: 1 * 14, left: -10)).toEqual [1, 0]
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('AtomApplication', function () {
|
||||
originalAtomHome = process.env.ATOM_HOME
|
||||
process.env.ATOM_HOME = makeTempDir('atom-home')
|
||||
// Symlinking the compile cache into the temporary home dir makes the windows load much faster
|
||||
fs.symlinkSync(path.join(originalAtomHome, 'compile-cache'), path.join(process.env.ATOM_HOME, 'compile-cache'))
|
||||
fs.symlinkSync(path.join(originalAtomHome, 'compile-cache'), path.join(process.env.ATOM_HOME, 'compile-cache'), 'junction')
|
||||
season.writeFileSync(path.join(process.env.ATOM_HOME, 'config.cson'), {
|
||||
'*': {
|
||||
welcome: {showOnStartup: false},
|
||||
@@ -309,7 +309,7 @@ describe('AtomApplication', function () {
|
||||
const packagePath = path.join(__dirname, '..', 'fixtures', 'packages', 'package-with-directory-provider')
|
||||
const packagesDirPath = path.join(process.env.ATOM_HOME, 'packages')
|
||||
fs.mkdirSync(packagesDirPath)
|
||||
fs.symlinkSync(packagePath, path.join(packagesDirPath, 'package-with-directory-provider'))
|
||||
fs.symlinkSync(packagePath, path.join(packagesDirPath, 'package-with-directory-provider'), 'junction')
|
||||
|
||||
const atomApplication = buildAtomApplication()
|
||||
atomApplication.config.set('core.disabledPackages', ['fuzzy-finder'])
|
||||
@@ -396,6 +396,30 @@ describe('AtomApplication', function () {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('when adding or removing project folders', function () {
|
||||
it('stores the window state immediately', async function () {
|
||||
const dirA = makeTempDir()
|
||||
const dirB = makeTempDir()
|
||||
|
||||
const atomApplication = buildAtomApplication()
|
||||
const window = atomApplication.launch(parseCommandLine([dirA, dirB]))
|
||||
await focusWindow(window)
|
||||
assert.deepEqual(await getTreeViewRootDirectories(window), [dirA, dirB])
|
||||
|
||||
await evalInWebContents(window.browserWindow.webContents, (sendBackToMainProcess) => {
|
||||
atom.project.removePath(atom.project.getPaths()[0])
|
||||
sendBackToMainProcess(null)
|
||||
})
|
||||
assert.deepEqual(await getTreeViewRootDirectories(window), [dirB])
|
||||
|
||||
// Window state should be saved when the project folder is removed
|
||||
const atomApplication2 = buildAtomApplication()
|
||||
const [window2] = atomApplication2.launch(parseCommandLine([]))
|
||||
await focusWindow(window2)
|
||||
assert.deepEqual(await getTreeViewRootDirectories(window2), [dirB])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('before quitting', function () {
|
||||
|
||||
@@ -2,19 +2,23 @@
|
||||
|
||||
import {dialog} from 'electron'
|
||||
import FileRecoveryService from '../../src/main-process/file-recovery-service'
|
||||
import temp from 'temp'
|
||||
import fs from 'fs-plus'
|
||||
import sinon from 'sinon'
|
||||
import {escapeRegExp} from 'underscore-plus'
|
||||
const temp = require('temp').track()
|
||||
|
||||
describe("FileRecoveryService", () => {
|
||||
let recoveryService, recoveryDirectory
|
||||
|
||||
beforeEach(() => {
|
||||
recoveryDirectory = temp.mkdirSync()
|
||||
recoveryDirectory = temp.mkdirSync('atom-spec-file-recovery')
|
||||
recoveryService = new FileRecoveryService(recoveryDirectory)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
temp.cleanupSync()
|
||||
})
|
||||
|
||||
describe("when no crash happens during a save", () => {
|
||||
it("creates a recovery file and deletes it after saving", () => {
|
||||
const mockWindow = {}
|
||||
@@ -28,6 +32,8 @@ describe("FileRecoveryService", () => {
|
||||
recoveryService.didSavePath(mockWindow, filePath)
|
||||
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
|
||||
assert.equal(fs.readFileSync(filePath, 'utf8'), "changed")
|
||||
|
||||
fs.removeSync(filePath)
|
||||
})
|
||||
|
||||
it("creates only one recovery file when many windows attempt to save the same file, deleting it when the last one finishes saving it", () => {
|
||||
@@ -48,6 +54,8 @@ describe("FileRecoveryService", () => {
|
||||
recoveryService.didSavePath(anotherMockWindow, filePath)
|
||||
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
|
||||
assert.equal(fs.readFileSync(filePath, 'utf8'), "changed")
|
||||
|
||||
fs.removeSync(filePath)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -64,6 +72,8 @@ describe("FileRecoveryService", () => {
|
||||
recoveryService.didCrashWindow(mockWindow)
|
||||
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
|
||||
assert.equal(fs.readFileSync(filePath, 'utf8'), "some content")
|
||||
|
||||
fs.removeSync(filePath)
|
||||
})
|
||||
|
||||
it("restores the created recovery file when many windows attempt to save the same file and one of them crashes", () => {
|
||||
@@ -94,6 +104,8 @@ describe("FileRecoveryService", () => {
|
||||
recoveryService.didCrashWindow(anotherMockWindow)
|
||||
assert.equal(fs.readFileSync(filePath, 'utf8'), "D")
|
||||
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
|
||||
|
||||
fs.removeSync(filePath)
|
||||
})
|
||||
|
||||
it("emits a warning when a file can't be recovered", sinon.test(function () {
|
||||
@@ -113,6 +125,8 @@ describe("FileRecoveryService", () => {
|
||||
assert.equal(logs.length, 1)
|
||||
assert.match(logs[0], new RegExp(escapeRegExp(filePath)))
|
||||
assert.match(logs[0], new RegExp(escapeRegExp(recoveryFiles[0])))
|
||||
|
||||
fs.removeSync(filePath)
|
||||
}))
|
||||
})
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ describe "MenuManager", ->
|
||||
runs -> expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined()
|
||||
|
||||
it "omits key bindings that could conflict with AltGraph characters on macOS", ->
|
||||
Object.defineProperty process, 'platform', value: 'darwin'
|
||||
spyOn(menu, 'sendToBrowserProcess')
|
||||
menu.add [{label: "A", submenu: [
|
||||
{label: "B", command: "b"},
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
path = require 'path'
|
||||
Module = require 'module'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
ModuleCache = require '../src/module-cache'
|
||||
|
||||
describe 'ModuleCache', ->
|
||||
beforeEach ->
|
||||
spyOn(Module, '_findPath').andCallThrough()
|
||||
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
it 'resolves Electron module paths without hitting the filesystem', ->
|
||||
builtins = ModuleCache.cache.builtins
|
||||
expect(Object.keys(builtins).length).toBeGreaterThan 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
path = require 'path'
|
||||
Package = require '../src/package'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
fs = require 'fs-plus'
|
||||
{Disposable} = require 'atom'
|
||||
{buildKeydownEvent} = require '../src/keymap-extensions'
|
||||
@@ -17,6 +17,9 @@ describe "PackageManager", ->
|
||||
beforeEach ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
describe "::getApmPath()", ->
|
||||
it "returns the path to the apm command", ->
|
||||
apmPath = path.join(process.resourcesPath, "app", "apm", "bin", "apm")
|
||||
@@ -440,11 +443,9 @@ describe "PackageManager", ->
|
||||
spyOn(mainModule, 'activate').andCallThrough()
|
||||
spyOn(Package.prototype, 'requireMainModule').andCallThrough()
|
||||
|
||||
promise = atom.packages.activatePackage('package-with-activation-hooks')
|
||||
|
||||
it "defers requiring/activating the main module until an triggering of an activation hook occurs", ->
|
||||
promise = atom.packages.activatePackage('package-with-activation-hooks')
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 0
|
||||
|
||||
atom.packages.triggerActivationHook('language-fictitious:grammar-used')
|
||||
atom.packages.triggerDeferredActivationHooks()
|
||||
|
||||
@@ -455,6 +456,7 @@ describe "PackageManager", ->
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 1
|
||||
|
||||
it "does not double register activation hooks when deactivating and reactivating", ->
|
||||
promise = atom.packages.activatePackage('package-with-activation-hooks')
|
||||
expect(mainModule.activate.callCount).toBe 0
|
||||
atom.packages.triggerActivationHook('language-fictitious:grammar-used')
|
||||
atom.packages.triggerDeferredActivationHooks()
|
||||
@@ -489,6 +491,17 @@ describe "PackageManager", ->
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 1
|
||||
|
||||
it "activates the package immediately if the activation hook had already been triggered", ->
|
||||
atom.packages.triggerActivationHook('language-fictitious:grammar-used')
|
||||
atom.packages.triggerDeferredActivationHooks()
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 0
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-activation-hooks')
|
||||
|
||||
runs ->
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 1
|
||||
|
||||
describe "when the package has no main module", ->
|
||||
it "does not throw an exception", ->
|
||||
spyOn(console, "error")
|
||||
@@ -643,7 +656,7 @@ describe "PackageManager", ->
|
||||
[element, events, userKeymapPath] = []
|
||||
|
||||
beforeEach ->
|
||||
userKeymapPath = path.join(temp.path(), "user-keymaps.cson")
|
||||
userKeymapPath = path.join(temp.mkdirSync(), "user-keymaps.cson")
|
||||
spyOn(atom.keymaps, "getUserKeymapPath").andReturn(userKeymapPath)
|
||||
|
||||
element = createTestElement('test-1')
|
||||
@@ -660,6 +673,8 @@ describe "PackageManager", ->
|
||||
atom.keymaps.watchSubscriptions[userKeymapPath].dispose()
|
||||
delete atom.keymaps.watchSubscriptions[userKeymapPath]
|
||||
|
||||
temp.cleanupSync()
|
||||
|
||||
it "doesn't override user-defined keymaps", ->
|
||||
fs.writeFileSync userKeymapPath, """
|
||||
".test-1":
|
||||
@@ -740,10 +755,6 @@ describe "PackageManager", ->
|
||||
two = require.resolve("./fixtures/packages/package-with-style-sheets-manifest/styles/2.less")
|
||||
three = require.resolve("./fixtures/packages/package-with-style-sheets-manifest/styles/3.css")
|
||||
|
||||
one = atom.themes.stringToId(one)
|
||||
two = atom.themes.stringToId(two)
|
||||
three = atom.themes.stringToId(three)
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
@@ -765,11 +776,6 @@ describe "PackageManager", ->
|
||||
three = require.resolve("./fixtures/packages/package-with-styles/styles/3.test-context.css")
|
||||
four = require.resolve("./fixtures/packages/package-with-styles/styles/4.css")
|
||||
|
||||
one = atom.themes.stringToId(one)
|
||||
two = atom.themes.stringToId(two)
|
||||
three = atom.themes.stringToId(three)
|
||||
four = atom.themes.stringToId(four)
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
|
||||
@@ -205,3 +205,26 @@ describe "Package", ->
|
||||
|
||||
it "uses the package name defined in package.json", ->
|
||||
expect(metadata.name).toBe 'package-with-a-totally-different-name'
|
||||
|
||||
describe "the initialize() hook", ->
|
||||
it "gets called when the package is activated", ->
|
||||
packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-deserializers')
|
||||
pack = buildPackage(packagePath)
|
||||
pack.requireMainModule()
|
||||
mainModule = pack.mainModule
|
||||
spyOn(mainModule, 'initialize')
|
||||
expect(mainModule.initialize).not.toHaveBeenCalled()
|
||||
pack.activate()
|
||||
expect(mainModule.initialize).toHaveBeenCalled()
|
||||
expect(mainModule.initialize.callCount).toBe(1)
|
||||
|
||||
it "gets called when a deserializer is used", ->
|
||||
packagePath = atom.project.getDirectories()[0].resolve('packages/package-with-deserializers')
|
||||
pack = buildPackage(packagePath)
|
||||
pack.requireMainModule()
|
||||
mainModule = pack.mainModule
|
||||
spyOn(mainModule, 'initialize')
|
||||
pack.load()
|
||||
expect(mainModule.initialize).not.toHaveBeenCalled()
|
||||
atom.deserializers.deserialize({deserializer: 'Deserializer1', a: 'b'})
|
||||
expect(mainModule.initialize).toHaveBeenCalled()
|
||||
|
||||
147
spec/package-transpilation-registry-spec.js
Normal file
147
spec/package-transpilation-registry-spec.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/** @babel */
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
|
||||
|
||||
import PackageTranspilationRegistry from '../src/package-transpilation-registry'
|
||||
|
||||
const originalCompiler = {
|
||||
getCachePath: (sourceCode, filePath) => {
|
||||
return "orig-cache-path"
|
||||
},
|
||||
|
||||
compile: (sourceCode, filePath) => {
|
||||
return sourceCode + "-original-compiler"
|
||||
},
|
||||
|
||||
shouldCompile: (sourceCode, filePath) => {
|
||||
return path.extname(filePath) === '.js'
|
||||
}
|
||||
}
|
||||
|
||||
describe("PackageTranspilationRegistry", () => {
|
||||
let registry
|
||||
let wrappedCompiler
|
||||
|
||||
beforeEach(() => {
|
||||
registry = new PackageTranspilationRegistry()
|
||||
wrappedCompiler = registry.wrapTranspiler(originalCompiler)
|
||||
})
|
||||
|
||||
it('falls through to the original compiler by default', () => {
|
||||
spyOn(originalCompiler, 'getCachePath')
|
||||
spyOn(originalCompiler, 'compile')
|
||||
spyOn(originalCompiler, 'shouldCompile')
|
||||
|
||||
wrappedCompiler.getCachePath('source', '/path/to/file.js')
|
||||
wrappedCompiler.compile('source', '/path/to/filejs')
|
||||
wrappedCompiler.shouldCompile('source', '/path/to/file.js')
|
||||
|
||||
expect(originalCompiler.getCachePath).toHaveBeenCalled()
|
||||
expect(originalCompiler.compile).toHaveBeenCalled()
|
||||
expect(originalCompiler.shouldCompile).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
describe('when a file is contained in a path that has custom transpilation', () => {
|
||||
const hitPath = path.join('/path/to/lib/file.js')
|
||||
const hitPathCoffee = path.join('/path/to/file2.coffee')
|
||||
const missPath = path.join('/path/other/file3.js')
|
||||
const hitPathMissSubdir =path.join('/path/to/file4.js')
|
||||
const hitPathMissExt = path.join('/path/to/file5.ts')
|
||||
const nodeModulesFolder = path.join('/path/to/lib/node_modules/file6.js')
|
||||
const hitNonStandardExt = path.join('/path/to/file7.omgwhatisthis')
|
||||
|
||||
const jsSpec = { glob: "lib/**/*.js", transpiler: './transpiler-js', options: { type: 'js' } }
|
||||
const coffeeSpec = { glob: "*.coffee", transpiler: './transpiler-coffee', options: { type: 'coffee' } }
|
||||
const omgSpec = { glob: "*.omgwhatisthis", transpiler: './transpiler-omg', options: { type: 'omg' } }
|
||||
|
||||
const expectedMeta = { name: 'my-package', path: path.join('/path/to'), meta: { some: 'metadata' } }
|
||||
|
||||
const jsTranspiler = {
|
||||
transpile: (sourceCode, filePath, options) => {
|
||||
return {code: sourceCode + "-transpiler-js"}
|
||||
},
|
||||
|
||||
getCacheKeyData: (sourceCode, filePath, options) => {
|
||||
return 'js-transpiler-cache-data'
|
||||
}
|
||||
}
|
||||
|
||||
const coffeeTranspiler = {
|
||||
transpile: (sourceCode, filePath, options) => {
|
||||
return {code: sourceCode + "-transpiler-coffee"}
|
||||
},
|
||||
|
||||
getCacheKeyData: (sourceCode, filePath, options) => {
|
||||
return 'coffee-transpiler-cache-data'
|
||||
}
|
||||
}
|
||||
|
||||
const omgTranspiler = {
|
||||
transpile: (sourceCode, filePath, options) => {
|
||||
return {code: sourceCode + "-transpiler-omg"}
|
||||
},
|
||||
|
||||
getCacheKeyData: (sourceCode, filePath, options) => {
|
||||
return 'omg-transpiler-cache-data'
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jsSpec._transpilerSource = "js-transpiler-source"
|
||||
coffeeSpec._transpilerSource = "coffee-transpiler-source"
|
||||
omgTranspiler._transpilerSource = "omg-transpiler-source"
|
||||
|
||||
spyOn(registry, "getTranspiler").andCallFake(spec => {
|
||||
if (spec.transpiler === './transpiler-js') return jsTranspiler
|
||||
if (spec.transpiler === './transpiler-coffee') return coffeeTranspiler
|
||||
if (spec.transpiler === './transpiler-omg') return omgTranspiler
|
||||
throw new Error('bad transpiler path ' + spec.transpiler)
|
||||
})
|
||||
|
||||
registry.addTranspilerConfigForPath(path.join('/path/to'), 'my-package', { some: 'metadata' }, [
|
||||
jsSpec, coffeeSpec, omgSpec
|
||||
])
|
||||
})
|
||||
|
||||
it('always returns true from shouldCompile for a file in that dir that match a glob', () => {
|
||||
spyOn(originalCompiler, 'shouldCompile').andReturn(false)
|
||||
expect(wrappedCompiler.shouldCompile('source', hitPath)).toBe(true)
|
||||
expect(wrappedCompiler.shouldCompile('source', hitPathCoffee)).toBe(true)
|
||||
expect(wrappedCompiler.shouldCompile('source', hitNonStandardExt)).toBe(true)
|
||||
expect(wrappedCompiler.shouldCompile('source', hitPathMissExt)).toBe(false)
|
||||
expect(wrappedCompiler.shouldCompile('source', hitPathMissSubdir)).toBe(false)
|
||||
expect(wrappedCompiler.shouldCompile('source', missPath)).toBe(false)
|
||||
expect(wrappedCompiler.shouldCompile('source', nodeModulesFolder)).toBe(false)
|
||||
})
|
||||
|
||||
it('calls getCacheKeyData on the transpiler to get additional cache key data', () => {
|
||||
spyOn(registry, "getTranspilerPath").andReturn("./transpiler-js")
|
||||
spyOn(jsTranspiler, 'getCacheKeyData').andCallThrough()
|
||||
|
||||
wrappedCompiler.getCachePath('source', missPath, jsSpec)
|
||||
expect(jsTranspiler.getCacheKeyData).not.toHaveBeenCalledWith('source', missPath, jsSpec.options, expectedMeta)
|
||||
wrappedCompiler.getCachePath('source', hitPath, jsSpec)
|
||||
expect(jsTranspiler.getCacheKeyData).toHaveBeenCalledWith('source', hitPath, jsSpec.options, expectedMeta)
|
||||
})
|
||||
|
||||
it('compiles files matching a glob with the associated transpiler, and the old one otherwise', () => {
|
||||
spyOn(jsTranspiler, "transpile").andCallThrough()
|
||||
spyOn(coffeeTranspiler, "transpile").andCallThrough()
|
||||
spyOn(omgTranspiler, "transpile").andCallThrough()
|
||||
|
||||
expect(wrappedCompiler.compile('source', hitPath)).toEqual('source-transpiler-js')
|
||||
expect(jsTranspiler.transpile).toHaveBeenCalledWith('source', hitPath, jsSpec.options, expectedMeta)
|
||||
expect(wrappedCompiler.compile('source', hitPathCoffee)).toEqual('source-transpiler-coffee')
|
||||
expect(coffeeTranspiler.transpile).toHaveBeenCalledWith('source', hitPathCoffee, coffeeSpec.options, expectedMeta)
|
||||
expect(wrappedCompiler.compile('source', hitNonStandardExt)).toEqual('source-transpiler-omg')
|
||||
expect(omgTranspiler.transpile).toHaveBeenCalledWith('source', hitNonStandardExt, omgSpec.options, expectedMeta)
|
||||
|
||||
expect(wrappedCompiler.compile('source', missPath)).toEqual('source-original-compiler')
|
||||
expect(wrappedCompiler.compile('source', hitPathMissExt)).toEqual('source-original-compiler')
|
||||
expect(wrappedCompiler.compile('source', hitPathMissSubdir)).toEqual('source-original-compiler')
|
||||
expect(wrappedCompiler.compile('source', nodeModulesFolder)).toEqual('source-original-compiler')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1080,6 +1080,7 @@ describe "Pane", ->
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "only calls terminate handler once when text is modified twice", ->
|
||||
originalText = editor1.getText()
|
||||
editor1.insertText('Some text')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
@@ -1091,6 +1092,10 @@ describe "Pane", ->
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
# Reset fixture back to original state
|
||||
editor1.setText(originalText)
|
||||
editor1.save()
|
||||
|
||||
it "only calls clearPendingItem if there is a pending item to clear", ->
|
||||
spyOn(pane, "clearPendingItem").andCallThrough()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
Project = require '../src/project'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
@@ -12,6 +12,9 @@ describe "Project", ->
|
||||
# Wait for project's service consumers to be asynchronously added
|
||||
waits(1)
|
||||
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
describe "serialization", ->
|
||||
deserializedProject = null
|
||||
|
||||
@@ -51,7 +54,7 @@ describe "Project", ->
|
||||
|
||||
|
||||
it "does not deserialize buffers when their path is a directory that exists", ->
|
||||
pathToOpen = path.join(temp.mkdirSync(), 'file.txt')
|
||||
pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(pathToOpen)
|
||||
@@ -64,7 +67,8 @@ describe "Project", ->
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
|
||||
it "does not deserialize buffers when their path is inaccessible", ->
|
||||
pathToOpen = path.join(temp.mkdirSync(), 'file.txt')
|
||||
return if process.platform is 'win32' # chmod not supported on win32
|
||||
pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt')
|
||||
fs.writeFileSync(pathToOpen, '')
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -151,7 +155,7 @@ describe "Project", ->
|
||||
expect(notification.getType()).toBe 'warning'
|
||||
expect(notification.getDetail()).toBe 'SomeError'
|
||||
expect(notification.getMessage()).toContain '`resurrect`'
|
||||
expect(notification.getMessage()).toContain 'fixtures/dir/a'
|
||||
expect(notification.getMessage()).toContain path.join('fixtures', 'dir', 'a')
|
||||
|
||||
describe "when a custom repository-provider service is provided", ->
|
||||
[fakeRepositoryProvider, fakeRepository] = []
|
||||
|
||||
267
spec/reopen-project-menu-manager-spec.js
Normal file
267
spec/reopen-project-menu-manager-spec.js
Normal file
@@ -0,0 +1,267 @@
|
||||
/** @babel */
|
||||
|
||||
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
|
||||
import {Emitter, Disposable, CompositeDisposable} from 'event-kit'
|
||||
|
||||
const ReopenProjectMenuManager = require('../src/reopen-project-menu-manager')
|
||||
|
||||
numberRange = (low, high) => {
|
||||
const size = high - low
|
||||
const result = new Array(size)
|
||||
for (var i = 0; i < size; i++)
|
||||
result[i] = low + i
|
||||
return result
|
||||
}
|
||||
|
||||
describe("ReopenProjectMenuManager", () => {
|
||||
let menuManager, commandRegistry, config, historyManager, reopenProjects
|
||||
let commandDisposable, configDisposable, historyDisposable
|
||||
|
||||
beforeEach(() => {
|
||||
menuManager = jasmine.createSpyObj('MenuManager', ['add'])
|
||||
menuManager.add.andReturn(new Disposable())
|
||||
|
||||
commandRegistry = jasmine.createSpyObj('CommandRegistry', ['add'])
|
||||
commandDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
|
||||
commandRegistry.add.andReturn(commandDisposable)
|
||||
|
||||
config = jasmine.createSpyObj('Config', ['onDidChange', 'get'])
|
||||
config.get.andReturn(10)
|
||||
configDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
|
||||
config.didChangeListener = { }
|
||||
config.onDidChange.andCallFake((key, fn) => {
|
||||
config.didChangeListener[key] = fn
|
||||
return configDisposable
|
||||
})
|
||||
|
||||
historyManager = jasmine.createSpyObj('historyManager', ['getProjects','onDidChangeProjects'])
|
||||
historyManager.getProjects.andReturn([])
|
||||
historyDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
|
||||
historyManager.onDidChangeProjects.andCallFake((fn) => {
|
||||
historyManager.changeProjectsListener = fn
|
||||
return historyDisposable
|
||||
})
|
||||
|
||||
openFunction = jasmine.createSpy()
|
||||
reopenProjects = new ReopenProjectMenuManager({menu:menuManager, commands: commandRegistry, history: historyManager, config, open:openFunction})
|
||||
})
|
||||
|
||||
describe("constructor", () => {
|
||||
it("registers the 'reopen-project' command function", () => {
|
||||
expect(commandRegistry.add).toHaveBeenCalled()
|
||||
const cmdCall = commandRegistry.add.calls[0]
|
||||
expect(cmdCall.args.length).toBe(2)
|
||||
expect(cmdCall.args[0]).toBe('atom-workspace')
|
||||
expect(typeof cmdCall.args[1]['application:reopen-project']).toBe('function')
|
||||
})
|
||||
})
|
||||
|
||||
describe("dispose", () => {
|
||||
it("disposes of the history, command and config disposables", () => {
|
||||
reopenProjects.dispose()
|
||||
expect(historyDisposable.dispose).toHaveBeenCalled()
|
||||
expect(configDisposable.dispose).toHaveBeenCalled()
|
||||
expect(commandDisposable.dispose).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("disposes of the menu disposable once used", () => {
|
||||
const menuDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
|
||||
menuManager.add.andReturn(menuDisposable)
|
||||
reopenProjects.update()
|
||||
expect(menuDisposable.dispose).not.toHaveBeenCalled()
|
||||
reopenProjects.dispose()
|
||||
expect(menuDisposable.dispose).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("the command", () => {
|
||||
it("calls open with the paths of the project specified by the detail index", () => {
|
||||
historyManager.getProjects.andReturn([ { paths: ['/a'] }, { paths: ['/b', 'c:\\'] }])
|
||||
reopenProjects.update()
|
||||
|
||||
reopenProjectCommand = commandRegistry.add.calls[0].args[1]['application:reopen-project']
|
||||
reopenProjectCommand({ detail: { index: 1 } })
|
||||
|
||||
expect(openFunction).toHaveBeenCalled()
|
||||
expect(openFunction.calls[0].args[0]).toEqual(['/b', 'c:\\'])
|
||||
})
|
||||
|
||||
it("does not call open when no command detail is supplied", () => {
|
||||
reopenProjectCommand = commandRegistry.add.calls[0].args[1]['application:reopen-project']
|
||||
reopenProjectCommand({})
|
||||
|
||||
expect(openFunction).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("does not call open when no command detail index is supplied", () => {
|
||||
reopenProjectCommand = commandRegistry.add.calls[0].args[1]['application:reopen-project']
|
||||
reopenProjectCommand({ detail: { anything: 'here' } })
|
||||
|
||||
expect(openFunction).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
it("adds menu items to MenuManager based on projects from HistoryManager", () => {
|
||||
historyManager.getProjects.andReturn([ { paths: ['/a'] }, { paths: ['/b', 'c:\\'] }])
|
||||
reopenProjects.update()
|
||||
expect(historyManager.getProjects).toHaveBeenCalled()
|
||||
expect(menuManager.add).toHaveBeenCalled()
|
||||
const menuArg = menuManager.add.calls[0].args[0]
|
||||
expect(menuArg.length).toBe(1)
|
||||
expect(menuArg[0].label).toBe('File')
|
||||
expect(menuArg[0].submenu.length).toBe(1)
|
||||
const projectsMenu = menuArg[0].submenu[0]
|
||||
expect(projectsMenu.label).toBe('Reopen Project')
|
||||
expect(projectsMenu.submenu.length).toBe(2)
|
||||
|
||||
const first = projectsMenu.submenu[0]
|
||||
expect(first.label).toBe('/a')
|
||||
expect(first.command).toBe('application:reopen-project')
|
||||
expect(first.commandDetail).toEqual({ index: 0 })
|
||||
|
||||
const second = projectsMenu.submenu[1]
|
||||
expect(second.label).toBe('b, c:\\')
|
||||
expect(second.command).toBe('application:reopen-project')
|
||||
expect(second.commandDetail).toEqual({ index: 1 })
|
||||
})
|
||||
|
||||
it("adds only the number of menu items specified in the 'core.reopenProjectMenuCount' config", () => {
|
||||
historyManager.getProjects.andReturn(numberRange(1, 100).map(i => ({ paths: [ '/test/' + i ] })))
|
||||
reopenProjects.update()
|
||||
expect(menuManager.add).toHaveBeenCalled()
|
||||
const menu = menuManager.add.calls[0].args[0][0]
|
||||
expect(menu.label).toBe('File')
|
||||
expect(menu.submenu.length).toBe(1)
|
||||
expect(menu.submenu[0].label).toBe('Reopen Project')
|
||||
expect(menu.submenu[0].submenu.length).toBe(10)
|
||||
})
|
||||
|
||||
it("disposes the previously menu built", () => {
|
||||
const menuDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
|
||||
menuManager.add.andReturn(menuDisposable)
|
||||
reopenProjects.update()
|
||||
expect(menuDisposable.dispose).not.toHaveBeenCalled()
|
||||
reopenProjects.update()
|
||||
expect(menuDisposable.dispose).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("is called when the Config changes for 'core.reopenProjectMenuCount'", () => {
|
||||
historyManager.getProjects.andReturn(numberRange(1, 100).map(i => ({ paths: [ '/test/' + i ] })))
|
||||
reopenProjects.update()
|
||||
config.get.andReturn(25)
|
||||
config.didChangeListener['core.reopenProjectMenuCount']({oldValue:10, newValue: 25})
|
||||
|
||||
const finalArgs = menuManager.add.calls[1].args[0]
|
||||
const projectsMenu = finalArgs[0].submenu[0].submenu
|
||||
|
||||
expect(projectsMenu.length).toBe(25)
|
||||
})
|
||||
|
||||
it("is called when the HistoryManager's projects change", () => {
|
||||
reopenProjects.update()
|
||||
historyManager.getProjects.andReturn([ { paths: ['/a'] }, { paths: ['/b', 'c:\\'] } ])
|
||||
historyManager.changeProjectsListener()
|
||||
expect(menuManager.add.calls.length).toBe(2)
|
||||
|
||||
const finalArgs = menuManager.add.calls[1].args[0]
|
||||
const projectsMenu = finalArgs[0].submenu[0]
|
||||
|
||||
const first = projectsMenu.submenu[0]
|
||||
expect(first.label).toBe('/a')
|
||||
expect(first.command).toBe('application:reopen-project')
|
||||
expect(first.commandDetail).toEqual({ index: 0 })
|
||||
|
||||
const second = projectsMenu.submenu[1]
|
||||
expect(second.label).toBe('b, c:\\')
|
||||
expect(second.command).toBe('application:reopen-project')
|
||||
expect(second.commandDetail).toEqual({ index: 1 })
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateProjects", () => {
|
||||
it("creates correct menu items commands for recent projects", () => {
|
||||
const projects = [
|
||||
{ paths: [ '/users/neila' ] },
|
||||
{ paths: [ '/users/buzza', 'users/michaelc' ] }
|
||||
]
|
||||
|
||||
const menu = ReopenProjectMenuManager.createProjectsMenu(projects)
|
||||
expect(menu.label).toBe('File')
|
||||
expect(menu.submenu.length).toBe(1)
|
||||
|
||||
const recentMenu = menu.submenu[0]
|
||||
expect(recentMenu.label).toBe('Reopen Project')
|
||||
expect(recentMenu.submenu.length).toBe(2)
|
||||
|
||||
const first = recentMenu.submenu[0]
|
||||
expect(first.label).toBe('/users/neila')
|
||||
expect(first.command).toBe('application:reopen-project')
|
||||
expect(first.commandDetail).toEqual({index: 0})
|
||||
|
||||
const second = recentMenu.submenu[1]
|
||||
expect(second.label).toBe('buzza, michaelc')
|
||||
expect(second.command).toBe('application:reopen-project')
|
||||
expect(second.commandDetail).toEqual({index: 1})
|
||||
})
|
||||
})
|
||||
|
||||
describe("createLabel", () => {
|
||||
it("returns the Unix path unchanged if there is only one", () => {
|
||||
const label = ReopenProjectMenuManager.createLabel({ paths: ['/a/b/c/d/e/f'] })
|
||||
expect(label).toBe('/a/b/c/d/e/f')
|
||||
})
|
||||
|
||||
it("returns the Windows path unchanged if there is only one", () => {
|
||||
const label = ReopenProjectMenuManager.createLabel({ paths: ['c:\\missions\\apollo11'] })
|
||||
expect(label).toBe('c:\\missions\\apollo11')
|
||||
})
|
||||
|
||||
it("returns the URL unchanged if there is only one", () => {
|
||||
const label = ReopenProjectMenuManager.createLabel({ paths: ['https://launch.pad/apollo/11'] })
|
||||
expect(label).toBe('https://launch.pad/apollo/11')
|
||||
})
|
||||
|
||||
it("returns a comma-seperated list of base names if there are multiple", () => {
|
||||
const project = { paths: [ '/var/one', '/usr/bin/two', '/etc/mission/control/three' ] }
|
||||
const label = ReopenProjectMenuManager.createLabel(project)
|
||||
expect(label).toBe('one, two, three')
|
||||
})
|
||||
|
||||
describe("betterBaseName", () => {
|
||||
it("returns the standard base name for an absolute Unix path", () => {
|
||||
const name = ReopenProjectMenuManager.betterBaseName('/one/to/three')
|
||||
expect(name).toBe('three')
|
||||
})
|
||||
|
||||
it("returns the standard base name for a relative Windows path", () => {
|
||||
if (process.platform === 'win32') {
|
||||
const name = ReopenProjectMenuManager.betterBaseName('.\\one\\two')
|
||||
expect(name).toBe('two')
|
||||
}
|
||||
})
|
||||
|
||||
it("returns the standard base name for an absolute Windows path", () => {
|
||||
if (process.platform === 'win32') {
|
||||
const name = ReopenProjectMenuManager.betterBaseName('c:\\missions\\apollo\\11')
|
||||
expect(name).toBe('11')
|
||||
}
|
||||
})
|
||||
|
||||
it("returns the drive root for a Windows drive name", () => {
|
||||
const name = ReopenProjectMenuManager.betterBaseName('d:')
|
||||
expect(name).toBe('d:\\')
|
||||
})
|
||||
|
||||
it("returns the drive root for a Windows drive root", () => {
|
||||
const name = ReopenProjectMenuManager.betterBaseName('e:\\')
|
||||
expect(name).toBe('e:\\')
|
||||
})
|
||||
|
||||
it("returns the final path for a URI", () => {
|
||||
const name = ReopenProjectMenuManager.betterBaseName('https://something/else')
|
||||
expect(name).toBe('else')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -81,8 +81,9 @@ describe "Selection", ->
|
||||
describe "when the selection is destroyed", ->
|
||||
it "destroys its marker", ->
|
||||
selection.setBufferRange([[2, 0], [2, 10]])
|
||||
marker = selection.marker
|
||||
selection.destroy()
|
||||
expect(selection.marker.isDestroyed()).toBeTruthy()
|
||||
expect(marker.isDestroyed()).toBeTruthy()
|
||||
|
||||
describe ".insertText(text, options)", ->
|
||||
it "allows pasting white space only lines when autoIndent is enabled", ->
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
ChildProcess = require 'child_process'
|
||||
Spawner = require '../src/main-process/spawner'
|
||||
|
||||
describe "Spawner", ->
|
||||
beforeEach ->
|
||||
# Prevent any commands from actually running and affecting the host
|
||||
originalSpawn = ChildProcess.spawn
|
||||
|
||||
harmlessSpawn =
|
||||
# Just spawn something that won't actually modify the host
|
||||
if process.platform is 'win32'
|
||||
originalSpawn('dir')
|
||||
else
|
||||
originalSpawn('ls')
|
||||
|
||||
spyOn(ChildProcess, 'spawn').andCallFake (command, args, callback) ->
|
||||
harmlessSpawn
|
||||
|
||||
it "invokes passed callback", ->
|
||||
someCallback = jasmine.createSpy('someCallback')
|
||||
|
||||
Spawner.spawn('some-command', 'some-args', someCallback)
|
||||
|
||||
waitsFor ->
|
||||
someCallback.callCount is 1
|
||||
|
||||
it "spawns passed command with arguments", ->
|
||||
actualCommand = null
|
||||
actualArgs = null
|
||||
|
||||
# Redefine fake invocation, so to remember passed arguments
|
||||
jasmine.unspy(ChildProcess, 'spawn')
|
||||
spyOn(ChildProcess, 'spawn').andCallFake (command, args) ->
|
||||
actualCommand = command
|
||||
actualArgs = args
|
||||
harmlessSpawn
|
||||
|
||||
expectedCommand = 'some-command'
|
||||
expectedArgs = 'some-args'
|
||||
someCallback = jasmine.createSpy('someCallback')
|
||||
|
||||
Spawner.spawn(expectedCommand, expectedArgs, someCallback)
|
||||
|
||||
expect(actualCommand).toBe expectedCommand
|
||||
expect(actualArgs).toBe expectedArgs
|
||||
|
||||
it "ignores errors by spawned process", ->
|
||||
# Redefine fake invocation, so to cause an error
|
||||
jasmine.unspy(ChildProcess, 'spawn')
|
||||
spyOn(ChildProcess, 'spawn').andCallFake -> throw new Error("EBUSY")
|
||||
|
||||
someCallback = jasmine.createSpy('someCallback')
|
||||
|
||||
expect(Spawner.spawn('some-command', 'some-args', someCallback)).toBe undefined
|
||||
|
||||
waitsFor ->
|
||||
someCallback.callCount is 1
|
||||
@@ -1,7 +1,7 @@
|
||||
{EventEmitter} = require 'events'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
SquirrelUpdate = require '../src/main-process/squirrel-update'
|
||||
Spawner = require '../src/main-process/spawner'
|
||||
WinShell = require '../src/main-process/win-shell'
|
||||
@@ -36,6 +36,9 @@ describe "Windows Squirrel Update", ->
|
||||
WinShell.folderContextMenu = new FakeShellOption()
|
||||
WinShell.folderBackgroundContextMenu = new FakeShellOption()
|
||||
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
it "quits the app on all squirrel events", ->
|
||||
app = quit: jasmine.createSpy('quit')
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const temp = require('temp').track()
|
||||
const StyleManager = require('../src/style-manager')
|
||||
|
||||
describe('StyleManager', () => {
|
||||
let [styleManager, addEvents, removeEvents, updateEvents] = []
|
||||
|
||||
beforeEach(() => {
|
||||
styleManager = new StyleManager({configDirPath: atom.getConfigDirPath()})
|
||||
styleManager = new StyleManager({configDirPath: temp.mkdirSync('atom-config')})
|
||||
addEvents = []
|
||||
removeEvents = []
|
||||
updateEvents = []
|
||||
@@ -13,6 +14,10 @@ describe('StyleManager', () => {
|
||||
styleManager.onDidUpdateStyleElement((event) => { updateEvents.push(event) })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
temp.cleanupSync()
|
||||
})
|
||||
|
||||
describe('::addStyleSheet(source, params)', () => {
|
||||
it('adds a style sheet based on the given source and returns a disposable allowing it to be removed', () => {
|
||||
const disposable = styleManager.addStyleSheet('a {color: red}')
|
||||
@@ -43,12 +48,12 @@ describe('StyleManager', () => {
|
||||
atom-text-editor[mini].is-focused::shadow .class-7 { color: green; }
|
||||
`)
|
||||
expect(Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map((r) => r.selectorText)).toEqual([
|
||||
'atom-text-editor .class-1, atom-text-editor .class-2',
|
||||
'atom-text-editor > .class-3',
|
||||
'atom-text-editor.editor .class-1, atom-text-editor.editor .class-2',
|
||||
'atom-text-editor.editor > .class-3',
|
||||
'atom-text-editor .class-4',
|
||||
'another-element::shadow .class-5',
|
||||
'atom-text-editor[data-grammar*=\"js\"] .class-6',
|
||||
'atom-text-editor[mini].is-focused .class-7'
|
||||
'atom-text-editor[data-grammar*=\"js\"].editor .class-6',
|
||||
'atom-text-editor[mini].is-focused.editor .class-7'
|
||||
])
|
||||
})
|
||||
|
||||
@@ -75,8 +80,8 @@ describe('StyleManager', () => {
|
||||
`)
|
||||
expect(Array.from(styleManager.getStyleElements()[1].sheet.cssRules).map((r) => r.selectorText)).toEqual([
|
||||
'.source > .js, .source.coffee',
|
||||
'atom-text-editor .syntax--source > .syntax--js',
|
||||
'atom-text-editor[mini].is-focused .syntax--source > .syntax--js',
|
||||
'atom-text-editor.editor .syntax--source > .syntax--js',
|
||||
'atom-text-editor[mini].is-focused.editor .syntax--source > .syntax--js',
|
||||
'atom-text-editor .source > .js'
|
||||
])
|
||||
})
|
||||
|
||||
@@ -566,7 +566,7 @@ describe('TextEditorComponent', function () {
|
||||
editor.setSoftWrapped(true)
|
||||
runAnimationFrames()
|
||||
|
||||
componentNode.style.width = 16 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px'
|
||||
componentNode.style.width = 17 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px'
|
||||
component.measureDimensions()
|
||||
runAnimationFrames()
|
||||
})
|
||||
@@ -700,13 +700,9 @@ describe('TextEditorComponent', function () {
|
||||
runAnimationFrames()
|
||||
|
||||
let line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
|
||||
expect(line2LeafNodes.length).toBe(3)
|
||||
expect(line2LeafNodes[0].textContent).toBe(' ')
|
||||
expect(line2LeafNodes.length).toBe(1)
|
||||
expect(line2LeafNodes[0].textContent).toBe(' ')
|
||||
expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe(false)
|
||||
expect(line2LeafNodes[1].textContent).toBe(' ')
|
||||
expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe(false)
|
||||
expect(line2LeafNodes[2].textContent).toBe(' ')
|
||||
expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -939,13 +935,17 @@ describe('TextEditorComponent', function () {
|
||||
})
|
||||
|
||||
it('pads line numbers to be right-justified based on the maximum number of line number digits', function () {
|
||||
editor.getBuffer().setText([1, 2, 3, 4, 5, 6, 7, 8, 9, 10].join('\n'))
|
||||
const input = [];
|
||||
for (let i = 1; i <= 100; ++i) {
|
||||
input.push(i);
|
||||
}
|
||||
editor.getBuffer().setText(input.join('\n'))
|
||||
runAnimationFrames()
|
||||
|
||||
for (let screenRow = 0; screenRow <= 8; ++screenRow) {
|
||||
expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe('' + NBSP + (screenRow + 1))
|
||||
expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe('' + NBSP + NBSP + (screenRow + 1))
|
||||
}
|
||||
expect(component.lineNumberNodeForScreenRow(9).textContent).toBe('10')
|
||||
expect(component.lineNumberNodeForScreenRow(99).textContent).toBe('100')
|
||||
let gutterNode = componentNode.querySelector('.gutter')
|
||||
let initialGutterWidth = gutterNode.offsetWidth
|
||||
editor.getBuffer().delete([[1, 0], [2, 0]])
|
||||
@@ -953,7 +953,7 @@ describe('TextEditorComponent', function () {
|
||||
runAnimationFrames()
|
||||
|
||||
for (let screenRow = 0; screenRow <= 8; ++screenRow) {
|
||||
expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe('' + (screenRow + 1))
|
||||
expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe('' + NBSP + (screenRow + 1))
|
||||
}
|
||||
expect(gutterNode.offsetWidth).toBeLessThan(initialGutterWidth)
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
@@ -961,9 +961,9 @@ describe('TextEditorComponent', function () {
|
||||
runAnimationFrames()
|
||||
|
||||
for (let screenRow = 0; screenRow <= 8; ++screenRow) {
|
||||
expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe('' + NBSP + (screenRow + 1))
|
||||
expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe('' + NBSP + NBSP + (screenRow + 1))
|
||||
}
|
||||
expect(component.lineNumberNodeForScreenRow(9).textContent).toBe('10')
|
||||
expect(component.lineNumberNodeForScreenRow(99).textContent).toBe('100')
|
||||
expect(gutterNode.offsetWidth).toBe(initialGutterWidth)
|
||||
})
|
||||
|
||||
@@ -1269,10 +1269,10 @@ describe('TextEditorComponent', function () {
|
||||
|
||||
let cursor = componentNode.querySelector('.cursor')
|
||||
let cursorRect = cursor.getBoundingClientRect()
|
||||
let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.syntax--source.syntax--js').childNodes[2]
|
||||
let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.syntax--source.syntax--js').childNodes[0]
|
||||
let range = document.createRange(cursorLocationTextNode)
|
||||
range.setStart(cursorLocationTextNode, 0)
|
||||
range.setEnd(cursorLocationTextNode, 1)
|
||||
range.setStart(cursorLocationTextNode, 3)
|
||||
range.setEnd(cursorLocationTextNode, 4)
|
||||
let rangeRect = range.getBoundingClientRect()
|
||||
expect(cursorRect.left).toBeCloseTo(rangeRect.left, 0)
|
||||
expect(cursorRect.width).toBeCloseTo(rangeRect.width, 0)
|
||||
@@ -2291,7 +2291,9 @@ describe('TextEditorComponent', function () {
|
||||
|
||||
let position = wrapperNode.pixelPositionForBufferPosition([0, 26])
|
||||
let overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
|
||||
expect(overlay.style.left).toBe(Math.round(position.left + gutterWidth) + 'px')
|
||||
if (process.platform == 'darwin') { // Result is 359px on win32, expects 375px
|
||||
expect(overlay.style.left).toBe(Math.round(position.left + gutterWidth) + 'px')
|
||||
}
|
||||
expect(overlay.style.top).toBe(position.top + editor.getLineHeightInPixels() + 'px')
|
||||
|
||||
editor.insertText('a')
|
||||
@@ -3846,6 +3848,40 @@ describe('TextEditorComponent', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the mousewheel event\'s target is an SVG element inside a block decoration', function () {
|
||||
it('keeps the block decoration on the DOM if it is scrolled off-screen', function () {
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
wrapperNode.style.width = 20 * charWidth + 'px'
|
||||
editor.update({autoHeight: false})
|
||||
component.measureDimensions()
|
||||
runAnimationFrames()
|
||||
|
||||
const item = document.createElement('div')
|
||||
const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg")
|
||||
item.appendChild(svgElement)
|
||||
editor.decorateMarker(
|
||||
editor.markScreenPosition([0, 0], {invalidate: "never"}),
|
||||
{type: "block", item: item}
|
||||
)
|
||||
|
||||
runAnimationFrames()
|
||||
|
||||
let wheelEvent = new WheelEvent('mousewheel', {
|
||||
wheelDeltaX: 0,
|
||||
wheelDeltaY: -500
|
||||
})
|
||||
Object.defineProperty(wheelEvent, 'target', {
|
||||
get: function () {
|
||||
return svgElement
|
||||
}
|
||||
})
|
||||
componentNode.dispatchEvent(wheelEvent)
|
||||
runAnimationFrames()
|
||||
|
||||
expect(component.getTopmostDOMNode().contains(item)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
it('only prevents the default action of the mousewheel event if it actually lead to scrolling', function () {
|
||||
spyOn(WheelEvent.prototype, 'preventDefault').andCallThrough()
|
||||
wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
|
||||
@@ -78,6 +78,19 @@ describe "TextEditorElement", ->
|
||||
jasmine.attachToDOM(element)
|
||||
expect(element.querySelectorAll('.decoration').length).toBe initialDecorationCount
|
||||
|
||||
it "can be re-focused using the previous `document.activeElement`", ->
|
||||
editorElement = document.createElement('atom-text-editor')
|
||||
jasmine.attachToDOM(editorElement)
|
||||
editorElement.focus()
|
||||
|
||||
activeElement = document.activeElement
|
||||
|
||||
editorElement.remove()
|
||||
jasmine.attachToDOM(editorElement)
|
||||
activeElement.focus()
|
||||
|
||||
expect(editorElement.hasFocus()).toBe(true)
|
||||
|
||||
describe "focus and blur handling", ->
|
||||
it "proxies focus/blur events to/from the hidden input", ->
|
||||
element = new TextEditorElement
|
||||
|
||||
@@ -165,23 +165,6 @@ describe "TextEditorPresenter", ->
|
||||
expect(stateFn(presenter).tiles[10]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[12]).toBeUndefined()
|
||||
|
||||
it "excludes invalid tiles for screen rows to measure", ->
|
||||
presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2)
|
||||
presenter.setScreenRowsToMeasure([20, 30]) # unexisting rows
|
||||
|
||||
expect(stateFn(presenter).tiles[0]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[2]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[4]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[6]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[8]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[10]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[12]).toBeUndefined()
|
||||
|
||||
presenter.setScreenRowsToMeasure([12])
|
||||
buffer.deleteRows(12, 13)
|
||||
|
||||
expect(stateFn(presenter).tiles[12]).toBeUndefined()
|
||||
|
||||
describe "when there are block decorations", ->
|
||||
it "computes each tile's height and scrollTop based on block decorations' height", ->
|
||||
presenter = buildPresenter(explicitHeight: 120, scrollTop: 0, lineHeight: 10, tileSize: 2)
|
||||
@@ -2045,6 +2028,27 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
expect(stateForHighlightInTile(presenter, highlight, 0)).toBeUndefined()
|
||||
|
||||
it "handles highlights that extend to the left of the visible area (regression)", ->
|
||||
editor.setSelectedBufferRanges([
|
||||
[[0, 2], [1, 4]],
|
||||
])
|
||||
|
||||
presenter = buildPresenter(explicitHeight: 20, scrollLeft: 0, tileSize: 2)
|
||||
expectValues stateForSelectionInTile(presenter, 0, 0), {
|
||||
regions: [
|
||||
{top: 0 * 10, height: 10, left: 2 * 10, right: 0 * 10},
|
||||
{top: 1 * 10, height: 10, left: 0 * 10, width: 4 * 10}
|
||||
]
|
||||
}
|
||||
|
||||
presenter = buildPresenter(explicitHeight: 20, scrollLeft: 20, tileSize: 2)
|
||||
expectValues stateForSelectionInTile(presenter, 0, 0), {
|
||||
regions: [
|
||||
{top: 0 * 10, height: 10, left: 2 * 10, right: 0 * 10},
|
||||
{top: 1 * 10, height: 10, left: 0 * 10, width: 4 * 10}
|
||||
]
|
||||
}
|
||||
|
||||
it "updates when ::scrollTop changes", ->
|
||||
editor.setSelectedBufferRanges([
|
||||
[[6, 2], [6, 4]],
|
||||
@@ -2177,7 +2181,7 @@ describe "TextEditorPresenter", ->
|
||||
editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]], autoscroll: false)
|
||||
waitsForStateToUpdate presenter
|
||||
|
||||
destroyedSelection = null
|
||||
[destroyedSelection, destroyedDecoration] = []
|
||||
runs ->
|
||||
expectValues stateForSelectionInTile(presenter, 2, 0), {
|
||||
regions: [{top: 10, left: 4 * 10, width: 4 * 10, height: 10}]
|
||||
@@ -2185,10 +2189,11 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
# destroying
|
||||
destroyedSelection = editor.getSelections()[2]
|
||||
destroyedDecoration = destroyedSelection.decoration
|
||||
|
||||
waitsForStateToUpdate presenter, -> destroyedSelection.destroy()
|
||||
runs ->
|
||||
expectUndefinedStateForHighlight(presenter, destroyedSelection.decoration)
|
||||
expectUndefinedStateForHighlight(presenter, destroyedDecoration)
|
||||
|
||||
it "updates when highlight decorations' properties are updated", ->
|
||||
marker = editor.markBufferPosition([2, 2])
|
||||
@@ -2518,13 +2523,13 @@ describe "TextEditorPresenter", ->
|
||||
pixelPosition: {top: 1 * 10, left: 26 * 10 + gutterWidth - scrollLeft}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> editor.insertText('a')
|
||||
expectStateUpdate presenter, -> editor.insertText('abc', autoscroll: false)
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: windowWidth - itemWidth}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> editor.insertText('b')
|
||||
expectStateUpdate presenter, -> editor.insertText('d', autoscroll: false)
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: windowWidth - itemWidth}
|
||||
@@ -2545,14 +2550,55 @@ describe "TextEditorPresenter", ->
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, ->
|
||||
editor.insertNewline()
|
||||
presenter.setScrollTop(scrollTop) # I'm fighting the editor
|
||||
editor.insertNewline(autoscroll: false)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 6 * 10 - scrollTop - itemHeight, left: gutterWidth}
|
||||
}
|
||||
|
||||
it "when avoidOverflow is false, does not move horizontally when overflowing the editor's scrollView horizontally", ->
|
||||
scrollLeft = 20
|
||||
marker = editor.markBufferPosition([0, 26], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item, avoidOverflow: false})
|
||||
|
||||
presenter = buildPresenter({scrollLeft, windowWidth, windowHeight, contentFrameWidth, boundingClientRect, gutterWidth})
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: 26 * 10 + gutterWidth - scrollLeft}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, -> editor.insertText('a', autoscroll: false)
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 1 * 10, left: 27 * 10 + gutterWidth - scrollLeft}
|
||||
}
|
||||
|
||||
it "when avoidOverflow is false, does not flip vertically when overflowing the editor's scrollView vertically", ->
|
||||
scrollTop = 10
|
||||
marker = editor.markBufferPosition([5, 0], invalidate: 'never')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item, avoidOverflow: false})
|
||||
|
||||
presenter = buildPresenter({scrollTop, windowWidth, windowHeight, contentFrameWidth, boundingClientRect, gutterWidth})
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 6 * 10 - scrollTop, left: gutterWidth}
|
||||
}
|
||||
|
||||
expectStateUpdate presenter, ->
|
||||
editor.insertNewline(autoscroll: false)
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 7 * 10 - scrollTop, left: gutterWidth}
|
||||
}
|
||||
|
||||
describe "when the overlay item has a margin", ->
|
||||
beforeEach ->
|
||||
itemWidth = 12 * 10
|
||||
@@ -2773,7 +2819,7 @@ describe "TextEditorPresenter", ->
|
||||
expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 2
|
||||
|
||||
editor.setText("1\n2\n3")
|
||||
expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 1
|
||||
expect(getLineNumberGutterState(presenter).content.maxLineNumberDigits).toBe 2
|
||||
|
||||
describe ".content.tiles", ->
|
||||
lineNumberStateForScreenRow = (presenter, screenRow) ->
|
||||
|
||||
@@ -299,7 +299,7 @@ describe "TextEditor", ->
|
||||
|
||||
it "positions the cursor at the buffer position that corresponds to the given screen position", ->
|
||||
editor.setCursorScreenPosition([9, 0])
|
||||
expect(editor.getCursorBufferPosition()).toEqual [8, 10]
|
||||
expect(editor.getCursorBufferPosition()).toEqual [8, 11]
|
||||
|
||||
describe ".moveUp()", ->
|
||||
it "moves the cursor up", ->
|
||||
@@ -4325,15 +4325,17 @@ describe "TextEditor", ->
|
||||
expect(editor.getLastSelection().isEmpty()).toBeTruthy()
|
||||
|
||||
it "does not explode if the current language mode has no comment regex", ->
|
||||
editor.destroy()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(null, autoIndent: false).then (o) -> editor = o
|
||||
editor = new TextEditor(buffer: new TextBuffer(text: 'hello'))
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 5]])
|
||||
editor.toggleLineCommentsInSelection()
|
||||
expect(editor.lineTextForBufferRow(0)).toBe "hello"
|
||||
|
||||
it "does nothing for empty lines and null grammar", ->
|
||||
runs ->
|
||||
editor.setSelectedBufferRange([[4, 5], [4, 5]])
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('text.plain.null-grammar'))
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
editor.toggleLineCommentsInSelection()
|
||||
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
|
||||
expect(editor.buffer.lineForRow(10)).toBe ""
|
||||
|
||||
it "uncomments when the line lacks the trailing whitespace in the comment regex", ->
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
@@ -4860,15 +4862,13 @@ describe "TextEditor", ->
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 2]]
|
||||
|
||||
describe '.setTabLength(tabLength)', ->
|
||||
it 'retokenizes the editor with the given tab length', ->
|
||||
it 'clips atomic soft tabs to the given tab length', ->
|
||||
expect(editor.getTabLength()).toBe 2
|
||||
leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> 'leading-whitespace' in token.scopes
|
||||
expect(leadingWhitespaceTokens.length).toBe(3)
|
||||
expect(editor.clipScreenPosition([5, 1], clipDirection: 'forward')).toEqual([5, 2])
|
||||
|
||||
editor.setTabLength(6)
|
||||
expect(editor.getTabLength()).toBe 6
|
||||
leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> 'leading-whitespace' in token.scopes
|
||||
expect(leadingWhitespaceTokens.length).toBe(1)
|
||||
expect(editor.clipScreenPosition([5, 1], clipDirection: 'forward')).toEqual([5, 6])
|
||||
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
editor.onDidChange(changeHandler)
|
||||
@@ -5051,11 +5051,13 @@ describe "TextEditor", ->
|
||||
|
||||
describe ".destroy()", ->
|
||||
it "destroys marker layers associated with the text editor", ->
|
||||
buffer.retain()
|
||||
selectionsMarkerLayerId = editor.selectionsMarkerLayer.id
|
||||
foldsMarkerLayerId = editor.displayLayer.foldsMarkerLayer.id
|
||||
editor.destroy()
|
||||
expect(buffer.getMarkerLayer(selectionsMarkerLayerId)).toBeUndefined()
|
||||
expect(buffer.getMarkerLayer(foldsMarkerLayerId)).toBeUndefined()
|
||||
buffer.release()
|
||||
|
||||
it "notifies ::onDidDestroy observers when the editor is destroyed", ->
|
||||
destroyObserverCalled = false
|
||||
@@ -5064,6 +5066,23 @@ describe "TextEditor", ->
|
||||
editor.destroy()
|
||||
expect(destroyObserverCalled).toBe true
|
||||
|
||||
it "does not blow up when query methods are called afterward", ->
|
||||
editor.destroy()
|
||||
editor.getGrammar()
|
||||
editor.getLastCursor()
|
||||
editor.lineTextForBufferRow(0)
|
||||
|
||||
it "emits the destroy event after destroying the editor's buffer", ->
|
||||
events = []
|
||||
editor.getBuffer().onDidDestroy ->
|
||||
expect(editor.isDestroyed()).toBe(true)
|
||||
events.push('buffer-destroyed')
|
||||
editor.onDidDestroy ->
|
||||
expect(buffer.isDestroyed()).toBe(true)
|
||||
events.push('editor-destroyed')
|
||||
editor.destroy()
|
||||
expect(events).toEqual(['buffer-destroyed', 'editor-destroyed'])
|
||||
|
||||
describe ".joinLines()", ->
|
||||
describe "when no text is selected", ->
|
||||
describe "when the line below isn't empty", ->
|
||||
@@ -5141,7 +5160,7 @@ describe "TextEditor", ->
|
||||
expect(editor.lineTextForScreenRow(7)).toBe " while(items.length > 0) {" + editor.displayLayer.foldCharacter
|
||||
expect(editor.lineTextForScreenRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));"
|
||||
|
||||
it "duplicates all folded lines for empty selections on folded lines", ->
|
||||
it "duplicates all folded lines for empty selections on lines containing folds", ->
|
||||
editor.foldBufferRow(4)
|
||||
editor.setCursorBufferPosition([4, 0])
|
||||
|
||||
@@ -5172,13 +5191,49 @@ describe "TextEditor", ->
|
||||
"""
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[13, 0], [14, 2]]
|
||||
|
||||
it "only duplicates lines containing multiple selections once", ->
|
||||
editor.setText("""
|
||||
aaaaaa
|
||||
bbbbbb
|
||||
cccccc
|
||||
dddddd
|
||||
""")
|
||||
editor.setSelectedBufferRanges([
|
||||
[[0, 1], [0, 2]],
|
||||
[[0, 3], [0, 4]],
|
||||
[[2, 1], [2, 2]],
|
||||
[[2, 3], [3, 1]],
|
||||
[[3, 3], [3, 4]],
|
||||
])
|
||||
editor.duplicateLines()
|
||||
expect(editor.getText()).toBe("""
|
||||
aaaaaa
|
||||
aaaaaa
|
||||
bbbbbb
|
||||
cccccc
|
||||
dddddd
|
||||
cccccc
|
||||
dddddd
|
||||
""")
|
||||
expect(editor.getSelectedBufferRanges()).toEqual([
|
||||
[[1, 1], [1, 2]],
|
||||
[[1, 3], [1, 4]],
|
||||
[[5, 1], [5, 2]],
|
||||
[[5, 3], [6, 1]],
|
||||
[[6, 3], [6, 4]],
|
||||
])
|
||||
|
||||
describe ".shouldPromptToSave()", ->
|
||||
it "returns false when an edit session's buffer is in use by more than one session", ->
|
||||
it "returns true when buffer changed", ->
|
||||
jasmine.unspy(editor, 'shouldPromptToSave')
|
||||
expect(editor.shouldPromptToSave()).toBeFalsy()
|
||||
buffer.setText('changed')
|
||||
expect(editor.shouldPromptToSave()).toBeTruthy()
|
||||
|
||||
it "returns false when an edit session's buffer is in use by more than one session", ->
|
||||
jasmine.unspy(editor, 'shouldPromptToSave')
|
||||
buffer.setText('changed')
|
||||
|
||||
editor2 = null
|
||||
waitsForPromise ->
|
||||
atom.workspace.getActivePane().splitRight()
|
||||
@@ -5189,6 +5244,16 @@ describe "TextEditor", ->
|
||||
editor2.destroy()
|
||||
expect(editor.shouldPromptToSave()).toBeTruthy()
|
||||
|
||||
it "returns false when close of a window requested and edit session opened inside project", ->
|
||||
jasmine.unspy(editor, 'shouldPromptToSave')
|
||||
buffer.setText('changed')
|
||||
expect(editor.shouldPromptToSave(windowCloseRequested: true, projectHasPaths: true)).toBeFalsy()
|
||||
|
||||
it "returns true when close of a window requested and edit session opened without project", ->
|
||||
jasmine.unspy(editor, 'shouldPromptToSave')
|
||||
buffer.setText('changed')
|
||||
expect(editor.shouldPromptToSave(windowCloseRequested: true, projectHasPaths: false)).toBeTruthy()
|
||||
|
||||
describe "when the editor contains surrogate pair characters", ->
|
||||
it "correctly backspaces over them", ->
|
||||
editor.setText('\uD835\uDF97\uD835\uDF97\uD835\uDF97')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
|
||||
describe "atom.themes", ->
|
||||
beforeEach ->
|
||||
@@ -8,6 +8,7 @@ describe "atom.themes", ->
|
||||
|
||||
afterEach ->
|
||||
atom.themes.deactivateThemes()
|
||||
temp.cleanupSync()
|
||||
|
||||
describe "theme getters and setters", ->
|
||||
beforeEach ->
|
||||
@@ -170,7 +171,7 @@ describe "atom.themes", ->
|
||||
expect(styleElementAddedHandler).toHaveBeenCalled()
|
||||
|
||||
element = document.querySelector('head style[source-path*="css.css"]')
|
||||
expect(element.getAttribute('source-path')).toEqualPath atom.themes.stringToId(cssPath)
|
||||
expect(element.getAttribute('source-path')).toEqualPath cssPath
|
||||
expect(element.textContent).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
|
||||
# doesn't append twice
|
||||
@@ -189,7 +190,7 @@ describe "atom.themes", ->
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
|
||||
element = document.querySelector('head style[source-path*="sample.less"]')
|
||||
expect(element.getAttribute('source-path')).toEqualPath atom.themes.stringToId(lessPath)
|
||||
expect(element.getAttribute('source-path')).toEqualPath lessPath
|
||||
expect(element.textContent).toBe """
|
||||
#header {
|
||||
color: #4d926f;
|
||||
@@ -208,9 +209,9 @@ describe "atom.themes", ->
|
||||
|
||||
it "supports requiring css and less stylesheets without an explicit extension", ->
|
||||
atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'css')
|
||||
expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toEqualPath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('css.css'))
|
||||
expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toEqualPath atom.project.getDirectories()[0]?.resolve('css.css')
|
||||
atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'sample')
|
||||
expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toEqualPath atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less'))
|
||||
expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toEqualPath atom.project.getDirectories()[0]?.resolve('sample.less')
|
||||
|
||||
document.querySelector('head style[source-path*="css.css"]').remove()
|
||||
document.querySelector('head style[source-path*="sample.less"]').remove()
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{CompositeDisposable} = require 'atom'
|
||||
TooltipManager = require '../src/tooltip-manager'
|
||||
Tooltip = require '../src/tooltip'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
describe "TooltipManager", ->
|
||||
@@ -9,17 +11,27 @@ describe "TooltipManager", ->
|
||||
|
||||
beforeEach ->
|
||||
manager = new TooltipManager(keymapManager: atom.keymaps, viewRegistry: atom.views)
|
||||
element = document.createElement('div')
|
||||
element.classList.add('foo')
|
||||
jasmine.attachToDOM(element)
|
||||
element = createElement 'foo'
|
||||
|
||||
hover = (element, fn) ->
|
||||
createElement = (className) ->
|
||||
el = document.createElement('div')
|
||||
el.classList.add(className)
|
||||
jasmine.attachToDOM(el)
|
||||
el
|
||||
|
||||
mouseEnter = (element) ->
|
||||
element.dispatchEvent(new CustomEvent('mouseenter', bubbles: false))
|
||||
element.dispatchEvent(new CustomEvent('mouseover', bubbles: true))
|
||||
advanceClock(manager.hoverDefaults.delay.show)
|
||||
fn()
|
||||
|
||||
mouseLeave = (element) ->
|
||||
element.dispatchEvent(new CustomEvent('mouseleave', bubbles: false))
|
||||
element.dispatchEvent(new CustomEvent('mouseout', bubbles: true))
|
||||
|
||||
hover = (element, fn) ->
|
||||
mouseEnter(element)
|
||||
advanceClock(manager.hoverDefaults.delay.show)
|
||||
fn()
|
||||
mouseLeave(element)
|
||||
advanceClock(manager.hoverDefaults.delay.hide)
|
||||
|
||||
describe "::add(target, options)", ->
|
||||
@@ -29,6 +41,32 @@ describe "TooltipManager", ->
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
|
||||
it "displays tooltips immediately when hovering over new elements once a tooltip has been displayed once", ->
|
||||
disposables = new CompositeDisposable
|
||||
element1 = createElement('foo')
|
||||
disposables.add(manager.add element1, title: 'Title')
|
||||
element2 = createElement('bar')
|
||||
disposables.add(manager.add element2, title: 'Title')
|
||||
element3 = createElement('baz')
|
||||
disposables.add(manager.add element3, title: 'Title')
|
||||
|
||||
hover element1, ->
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
mouseEnter(element2)
|
||||
expect(document.body.querySelector(".tooltip")).not.toBeNull()
|
||||
mouseLeave(element2)
|
||||
advanceClock(manager.hoverDefaults.delay.hide)
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
advanceClock(Tooltip.FOLLOW_THROUGH_DURATION)
|
||||
mouseEnter(element3)
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
advanceClock(manager.hoverDefaults.delay.show)
|
||||
expect(document.body.querySelector(".tooltip")).not.toBeNull()
|
||||
|
||||
disposables.dispose()
|
||||
|
||||
describe "when the trigger is 'manual'", ->
|
||||
it "creates a tooltip immediately and only hides it on dispose", ->
|
||||
disposable = manager.add element, title: "Title", trigger: "manual"
|
||||
@@ -147,8 +185,29 @@ describe "TooltipManager", ->
|
||||
|
||||
describe "when the window is resized", ->
|
||||
it "hides the tooltips", ->
|
||||
manager.add element, title: "Title"
|
||||
disposable = manager.add element, title: "Title"
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).toBeDefined()
|
||||
expect(document.body.querySelector(".tooltip")).not.toBeNull()
|
||||
window.dispatchEvent(new CustomEvent('resize'))
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
disposable.dispose()
|
||||
|
||||
describe "findTooltips", ->
|
||||
it "adds and remove tooltips correctly", ->
|
||||
expect(manager.findTooltips(element).length).toBe(0)
|
||||
disposable1 = manager.add element, title: "elem1"
|
||||
expect(manager.findTooltips(element).length).toBe(1)
|
||||
disposable2 = manager.add element, title: "elem2"
|
||||
expect(manager.findTooltips(element).length).toBe(2)
|
||||
disposable1.dispose()
|
||||
expect(manager.findTooltips(element).length).toBe(1)
|
||||
disposable2.dispose()
|
||||
expect(manager.findTooltips(element).length).toBe(0)
|
||||
|
||||
it "lets us hide tooltips programatically", ->
|
||||
disposable = manager.add element, title: "Title"
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).not.toBeNull()
|
||||
manager.findTooltips(element)[0].hide()
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
disposable.dispose()
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
/** @babel */
|
||||
/* eslint-env jasmine */
|
||||
|
||||
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
|
||||
import path from 'path'
|
||||
import temp from 'temp'
|
||||
import child_process from 'child_process'
|
||||
import childProcess from 'child_process'
|
||||
import {updateProcessEnv, shouldGetEnvFromShell} from '../src/update-process-env'
|
||||
import dedent from 'dedent'
|
||||
import {EventEmitter} from 'events'
|
||||
import mockSpawn from 'mock-spawn'
|
||||
const temp = require('temp').track()
|
||||
|
||||
describe('updateProcessEnv(launchEnv)', function () {
|
||||
let originalProcessEnv, originalProcessPlatform
|
||||
let originalProcessEnv, originalProcessPlatform, originalSpawn, spawn
|
||||
|
||||
beforeEach(function () {
|
||||
originalSpawn = childProcess.spawn
|
||||
spawn = mockSpawn()
|
||||
childProcess.spawn = spawn
|
||||
originalProcessEnv = process.env
|
||||
originalProcessPlatform = process.platform
|
||||
process.env = {}
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
if (originalSpawn) {
|
||||
childProcess.spawn = originalSpawn
|
||||
}
|
||||
process.env = originalProcessEnv
|
||||
process.platform = originalProcessPlatform
|
||||
temp.cleanupSync()
|
||||
})
|
||||
|
||||
describe('when the launch environment appears to come from a shell', function () {
|
||||
it('updates process.env to match the launch environment', function () {
|
||||
it('updates process.env to match the launch environment', async function () {
|
||||
process.env = {
|
||||
WILL_BE_DELETED: 'hi',
|
||||
NODE_ENV: 'the-node-env',
|
||||
@@ -32,7 +42,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
|
||||
const initialProcessEnv = process.env
|
||||
|
||||
updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', TERM: 'xterm-something', KEY1: 'value1', KEY2: 'value2'})
|
||||
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', TERM: 'xterm-something', KEY1: 'value1', KEY2: 'value2'})
|
||||
expect(process.env).toEqual({
|
||||
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
|
||||
PWD: '/the/dir',
|
||||
@@ -50,7 +60,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
expect(process.env).toBe(initialProcessEnv)
|
||||
})
|
||||
|
||||
it('allows ATOM_HOME to be overwritten only if the new value is a valid path', function () {
|
||||
it('allows ATOM_HOME to be overwritten only if the new value is a valid path', async function () {
|
||||
let newAtomHomePath = temp.mkdirSync('atom-home')
|
||||
|
||||
process.env = {
|
||||
@@ -60,7 +70,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
ATOM_HOME: '/the/atom/home'
|
||||
}
|
||||
|
||||
updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir'})
|
||||
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir'})
|
||||
expect(process.env).toEqual({
|
||||
PWD: '/the/dir',
|
||||
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
|
||||
@@ -69,7 +79,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
ATOM_HOME: '/the/atom/home'
|
||||
})
|
||||
|
||||
updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', ATOM_HOME: path.join(newAtomHomePath, 'non-existent')})
|
||||
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', ATOM_HOME: path.join(newAtomHomePath, 'non-existent')})
|
||||
expect(process.env).toEqual({
|
||||
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
|
||||
PWD: '/the/dir',
|
||||
@@ -78,7 +88,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
ATOM_HOME: '/the/atom/home'
|
||||
})
|
||||
|
||||
updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', ATOM_HOME: newAtomHomePath})
|
||||
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', ATOM_HOME: newAtomHomePath})
|
||||
expect(process.env).toEqual({
|
||||
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
|
||||
PWD: '/the/dir',
|
||||
@@ -88,7 +98,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('allows ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT to be preserved if set', function () {
|
||||
it('allows ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT to be preserved if set', async function () {
|
||||
process.env = {
|
||||
WILL_BE_DELETED: 'hi',
|
||||
NODE_ENV: 'the-node-env',
|
||||
@@ -96,7 +106,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
ATOM_HOME: '/the/atom/home'
|
||||
}
|
||||
|
||||
updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', NODE_ENV: 'the-node-env', NODE_PATH: '/the/node/path', ATOM_HOME: '/the/atom/home'})
|
||||
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', NODE_ENV: 'the-node-env', NODE_PATH: '/the/node/path', ATOM_HOME: '/the/atom/home'})
|
||||
expect(process.env).toEqual({
|
||||
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
|
||||
PWD: '/the/dir',
|
||||
@@ -105,7 +115,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
ATOM_HOME: '/the/atom/home'
|
||||
})
|
||||
|
||||
updateProcessEnv({PWD: '/the/dir', NODE_ENV: 'the-node-env', NODE_PATH: '/the/node/path', ATOM_HOME: '/the/atom/home'})
|
||||
await updateProcessEnv({PWD: '/the/dir', NODE_ENV: 'the-node-env', NODE_PATH: '/the/node/path', ATOM_HOME: '/the/atom/home'})
|
||||
expect(process.env).toEqual({
|
||||
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
|
||||
PWD: '/the/dir',
|
||||
@@ -115,7 +125,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('allows an existing env variable to be updated', function () {
|
||||
it('allows an existing env variable to be updated', async function () {
|
||||
process.env = {
|
||||
WILL_BE_UPDATED: 'old-value',
|
||||
NODE_ENV: 'the-node-env',
|
||||
@@ -123,7 +133,7 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
ATOM_HOME: '/the/atom/home'
|
||||
}
|
||||
|
||||
updateProcessEnv(process.env)
|
||||
await updateProcessEnv(process.env)
|
||||
expect(process.env).toEqual(process.env)
|
||||
|
||||
let updatedEnv = {
|
||||
@@ -135,27 +145,27 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
PWD: '/the/dir'
|
||||
}
|
||||
|
||||
updateProcessEnv(updatedEnv)
|
||||
await updateProcessEnv(updatedEnv)
|
||||
expect(process.env).toEqual(updatedEnv)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the launch environment does not come from a shell', function () {
|
||||
describe('on osx', function () {
|
||||
it('updates process.env to match the environment in the user\'s login shell', function () {
|
||||
describe('on macOS', function () {
|
||||
it('updates process.env to match the environment in the user\'s login shell', async function () {
|
||||
if (process.platform === 'win32') return // TestsThatFailOnWin32
|
||||
|
||||
process.platform = 'darwin'
|
||||
process.env.SHELL = '/my/custom/bash'
|
||||
|
||||
spyOn(child_process, 'spawnSync').andReturn({
|
||||
stdout: dedent`
|
||||
FOO=BAR=BAZ=QUUX
|
||||
TERM=xterm-something
|
||||
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path
|
||||
`
|
||||
})
|
||||
|
||||
updateProcessEnv(process.env)
|
||||
expect(child_process.spawnSync.mostRecentCall.args[0]).toBe('/my/custom/bash')
|
||||
spawn.setDefault(spawn.simple(0, dedent`
|
||||
FOO=BAR=BAZ=QUUX
|
||||
TERM=xterm-something
|
||||
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path
|
||||
`))
|
||||
await updateProcessEnv(process.env)
|
||||
expect(spawn.calls.length).toBe(1)
|
||||
expect(spawn.calls[0].command).toBe('/my/custom/bash')
|
||||
expect(spawn.calls[0].args).toEqual(['-ilc', 'command env'])
|
||||
expect(process.env).toEqual({
|
||||
FOO: 'BAR=BAZ=QUUX',
|
||||
TERM: 'xterm-something',
|
||||
@@ -163,25 +173,25 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
})
|
||||
|
||||
// Doesn't error
|
||||
updateProcessEnv(null)
|
||||
await updateProcessEnv(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('on linux', function () {
|
||||
it('updates process.env to match the environment in the user\'s login shell', function () {
|
||||
it('updates process.env to match the environment in the user\'s login shell', async function () {
|
||||
if (process.platform === 'win32') return // TestsThatFailOnWin32
|
||||
|
||||
process.platform = 'linux'
|
||||
process.env.SHELL = '/my/custom/bash'
|
||||
|
||||
spyOn(child_process, 'spawnSync').andReturn({
|
||||
stdout: dedent`
|
||||
FOO=BAR=BAZ=QUUX
|
||||
TERM=xterm-something
|
||||
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path
|
||||
`
|
||||
})
|
||||
|
||||
updateProcessEnv(process.env)
|
||||
expect(child_process.spawnSync.mostRecentCall.args[0]).toBe('/my/custom/bash')
|
||||
spawn.setDefault(spawn.simple(0, dedent`
|
||||
FOO=BAR=BAZ=QUUX
|
||||
TERM=xterm-something
|
||||
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path
|
||||
`))
|
||||
await updateProcessEnv(process.env)
|
||||
expect(spawn.calls.length).toBe(1)
|
||||
expect(spawn.calls[0].command).toBe('/my/custom/bash')
|
||||
expect(spawn.calls[0].args).toEqual(['-ilc', 'command env'])
|
||||
expect(process.env).toEqual({
|
||||
FOO: 'BAR=BAZ=QUUX',
|
||||
TERM: 'xterm-something',
|
||||
@@ -189,24 +199,26 @@ describe('updateProcessEnv(launchEnv)', function () {
|
||||
})
|
||||
|
||||
// Doesn't error
|
||||
updateProcessEnv(null)
|
||||
await updateProcessEnv(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('on windows', function () {
|
||||
it('does not update process.env', function () {
|
||||
it('does not update process.env', async function () {
|
||||
process.platform = 'win32'
|
||||
spyOn(child_process, 'spawnSync')
|
||||
spyOn(childProcess, 'spawn')
|
||||
process.env = {FOO: 'bar'}
|
||||
|
||||
updateProcessEnv(process.env)
|
||||
expect(child_process.spawnSync).not.toHaveBeenCalled()
|
||||
await updateProcessEnv(process.env)
|
||||
expect(childProcess.spawn).not.toHaveBeenCalled()
|
||||
expect(process.env).toEqual({FOO: 'bar'})
|
||||
})
|
||||
})
|
||||
|
||||
describe('shouldGetEnvFromShell()', function () {
|
||||
it('indicates when the environment should be fetched from the shell', function () {
|
||||
if (process.platform === 'win32') return // TestsThatFailOnWin32
|
||||
|
||||
process.platform = 'darwin'
|
||||
expect(shouldGetEnvFromShell({SHELL: '/bin/sh'})).toBe(true)
|
||||
expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/sh'})).toBe(true)
|
||||
|
||||
@@ -23,6 +23,7 @@ describe "WindowEventHandler", ->
|
||||
|
||||
describe "when the window is loaded", ->
|
||||
it "doesn't have .is-blurred on the body tag", ->
|
||||
return if process.platform is 'win32' #Win32TestFailures - can not steal focus
|
||||
expect(document.body.className).not.toMatch("is-blurred")
|
||||
|
||||
describe "when the window is blurred", ->
|
||||
|
||||
@@ -4,6 +4,9 @@ temp = require('temp').track()
|
||||
{Disposable} = require 'event-kit'
|
||||
|
||||
describe "WorkspaceElement", ->
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
describe "when the workspace element is focused", ->
|
||||
it "transfers focus to the active pane", ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
temp = require('temp').track()
|
||||
TextEditor = require '../src/text-editor'
|
||||
Workspace = require '../src/workspace'
|
||||
Project = require '../src/project'
|
||||
@@ -19,6 +19,9 @@ describe "Workspace", ->
|
||||
atom.project.setPaths([atom.project.getDirectories()[0]?.resolve('dir')])
|
||||
waits(1)
|
||||
|
||||
afterEach ->
|
||||
temp.cleanupSync()
|
||||
|
||||
describe "serialization", ->
|
||||
simulateReload = ->
|
||||
workspaceState = atom.workspace.serialize()
|
||||
@@ -489,6 +492,7 @@ describe "Workspace", ->
|
||||
expect(item).toEqual {bar: "bar://baz"}
|
||||
|
||||
it "adds the file to the application's recent documents list", ->
|
||||
return unless process.platform is 'darwin' # Feature only supported on macOS
|
||||
spyOn(atom.applicationDelegate, 'addRecentDocument')
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -881,12 +885,44 @@ describe "Workspace", ->
|
||||
expect(coffeePackage.loadGrammarsSync.callCount).toBe 1
|
||||
|
||||
describe "document.title", ->
|
||||
describe "when the project has no path", ->
|
||||
describe "when there is no item open", ->
|
||||
it "sets the title to 'untitled'", ->
|
||||
atom.project.setPaths([])
|
||||
expect(document.title).toMatch ///^untitled///
|
||||
|
||||
describe "when the project has a path", ->
|
||||
describe "when the active pane item's path is not inside a project path", ->
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b').then ->
|
||||
atom.project.setPaths([])
|
||||
|
||||
it "sets the title to the pane item's title plus the item's path", ->
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
pathEscaped = fs.tildify(escapeStringRegex(path.dirname(item.getPath())))
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editor = atom.workspace.getActivePaneItem()
|
||||
editor.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
pathEscaped = fs.tildify(escapeStringRegex(path.dirname(editor.getPath())))
|
||||
expect(document.title).toMatch ///^#{editor.getTitle()}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
atom.workspace.getActivePane().activateNextItem()
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
pathEscaped = fs.tildify(escapeStringRegex(path.dirname(item.getPath())))
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
pane = atom.workspace.getActivePane()
|
||||
pane.splitRight()
|
||||
initialTitle = document.title
|
||||
pane.activateNextItem()
|
||||
expect(document.title).toBe initialTitle
|
||||
|
||||
describe "when the active pane item is inside a project path", ->
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b')
|
||||
@@ -900,7 +936,7 @@ describe "Workspace", ->
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editor = atom.workspace.getActivePaneItem()
|
||||
editor.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
editor.buffer.setPath(path.join(atom.project.getPaths()[0], 'hi'))
|
||||
pathEscaped = fs.tildify(escapeStringRegex(atom.project.getPaths()[0]))
|
||||
expect(document.title).toMatch ///^#{editor.getTitle()}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
@@ -912,11 +948,10 @@ describe "Workspace", ->
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{pathEscaped}///
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
it "updates the title to be untitled", ->
|
||||
atom.workspace.getActivePane().destroy()
|
||||
expect(atom.workspace.getActivePaneItem()).toBeUndefined()
|
||||
pathEscaped = fs.tildify(escapeStringRegex(atom.project.getPaths()[0]))
|
||||
expect(document.title).toMatch ///^#{pathEscaped}///
|
||||
expect(document.title).toMatch ///^untitled///
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
@@ -1139,6 +1174,7 @@ describe "Workspace", ->
|
||||
range: [[2, 6], [2, 11]]
|
||||
|
||||
it "works on evil filenames", ->
|
||||
atom.config.set('core.excludeVcsIgnoredPaths', false)
|
||||
platform.generateEvilFiles()
|
||||
atom.project.setPaths([path.join(__dirname, 'fixtures', 'evil-files')])
|
||||
paths = []
|
||||
@@ -1224,7 +1260,7 @@ describe "Workspace", ->
|
||||
expect(matches.length).toBe 1
|
||||
|
||||
it "includes files and folders that begin with a '.'", ->
|
||||
projectPath = temp.mkdirSync()
|
||||
projectPath = temp.mkdirSync('atom-spec-workspace')
|
||||
filePath = path.join(projectPath, '.text')
|
||||
fs.writeFileSync(filePath, 'match this')
|
||||
atom.project.setPaths([projectPath])
|
||||
|
||||
@@ -112,6 +112,7 @@ class ApplicationDelegate
|
||||
loadSettings = getWindowLoadSettings()
|
||||
loadSettings['initialPaths'] = paths
|
||||
setWindowLoadSettings(loadSettings)
|
||||
ipcRenderer.send("did-change-paths")
|
||||
|
||||
setAutoHideWindowMenuBar: (autoHide) ->
|
||||
ipcHelpers.call('window-method', 'setAutoHideMenuBar', autoHide)
|
||||
@@ -244,6 +245,17 @@ class ApplicationDelegate
|
||||
didCancelWindowUnload: ->
|
||||
ipcRenderer.send('did-cancel-window-unload')
|
||||
|
||||
onDidChangeHistoryManager: (callback) ->
|
||||
outerCallback = (event, message) ->
|
||||
callback(event)
|
||||
|
||||
ipcRenderer.on('did-change-history-manager', outerCallback)
|
||||
new Disposable ->
|
||||
ipcRenderer.removeListener('did-change-history-manager', outerCallback)
|
||||
|
||||
didChangeHistoryManager: ->
|
||||
ipcRenderer.send('did-change-history-manager')
|
||||
|
||||
openExternal: (url) ->
|
||||
shell.openExternal(url)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ StateStore = require './state-store'
|
||||
StorageFolder = require './storage-folder'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
registerDefaultCommands = require './register-default-commands'
|
||||
{updateProcessEnv} = require './update-process-env'
|
||||
|
||||
DeserializerManager = require './deserializer-manager'
|
||||
ViewRegistry = require './view-registry'
|
||||
@@ -22,6 +23,8 @@ KeymapManager = require './keymap-extensions'
|
||||
TooltipManager = require './tooltip-manager'
|
||||
CommandRegistry = require './command-registry'
|
||||
GrammarRegistry = require './grammar-registry'
|
||||
{HistoryManager, HistoryProject} = require './history-manager'
|
||||
ReopenProjectMenuManager = require './reopen-project-menu-manager'
|
||||
StyleManager = require './style-manager'
|
||||
PackageManager = require './package-manager'
|
||||
ThemeManager = require './theme-manager'
|
||||
@@ -94,6 +97,9 @@ class AtomEnvironment extends Model
|
||||
# Public: A {GrammarRegistry} instance
|
||||
grammars: null
|
||||
|
||||
# Public: A {HistoryManager} instance
|
||||
history: null
|
||||
|
||||
# Public: A {PackageManager} instance
|
||||
packages: null
|
||||
|
||||
@@ -226,15 +232,13 @@ class AtomEnvironment extends Model
|
||||
|
||||
@observeAutoHideMenuBar()
|
||||
|
||||
checkPortableHomeWritable = =>
|
||||
responseChannel = "check-portable-home-writable-response"
|
||||
ipcRenderer.on responseChannel, (event, response) ->
|
||||
ipcRenderer.removeAllListeners(responseChannel)
|
||||
@notifications.addWarning("#{response.message.replace(/([\\\.+\\-_#!])/g, '\\$1')}") if not response.writable
|
||||
@disposables.add new Disposable -> ipcRenderer.removeAllListeners(responseChannel)
|
||||
ipcRenderer.send('check-portable-home-writable', responseChannel)
|
||||
@history = new HistoryManager({@project, @commands, localStorage})
|
||||
# Keep instances of HistoryManager in sync
|
||||
@history.onDidChangeProjects (e) =>
|
||||
@applicationDelegate.didChangeHistoryManager() unless e.reloaded
|
||||
@disposables.add @applicationDelegate.onDidChangeHistoryManager(=> @history.loadState())
|
||||
|
||||
checkPortableHomeWritable()
|
||||
new ReopenProjectMenuManager({@menu, @commands, @history, @config, open: (paths) => @open(pathsToOpen: paths)})
|
||||
|
||||
attachSaveStateListeners: ->
|
||||
saveState = _.debounce((=>
|
||||
@@ -280,13 +284,13 @@ class AtomEnvironment extends Model
|
||||
@workspace.addOpener (uri) =>
|
||||
switch uri
|
||||
when 'atom://.atom/stylesheet'
|
||||
@workspace.open(@styles.getUserStyleSheetPath())
|
||||
@workspace.openTextFile(@styles.getUserStyleSheetPath())
|
||||
when 'atom://.atom/keymap'
|
||||
@workspace.open(@keymaps.getUserKeymapPath())
|
||||
@workspace.openTextFile(@keymaps.getUserKeymapPath())
|
||||
when 'atom://.atom/config'
|
||||
@workspace.open(@config.getUserConfigPath())
|
||||
@workspace.openTextFile(@config.getUserConfigPath())
|
||||
when 'atom://.atom/init-script'
|
||||
@workspace.open(@getUserInitScriptPath())
|
||||
@workspace.openTextFile(@getUserInitScriptPath())
|
||||
|
||||
registerDefaultTargetForKeymaps: ->
|
||||
@keymaps.defaultTarget = @views.getView(@workspace)
|
||||
@@ -643,7 +647,7 @@ class AtomEnvironment extends Model
|
||||
restoreWindowDimensions: ->
|
||||
unless @windowDimensions? and @isValidDimensions(@windowDimensions)
|
||||
@windowDimensions = @getDefaultWindowDimensions()
|
||||
@setWindowDimensions(@windowDimensions).then -> @windowDimensions
|
||||
@setWindowDimensions(@windowDimensions).then => @windowDimensions
|
||||
|
||||
restoreWindowBackground: ->
|
||||
if backgroundColor = window.localStorage.getItem('atom:window-background-color')
|
||||
@@ -662,7 +666,11 @@ class AtomEnvironment extends Model
|
||||
# Call this method when establishing a real application window.
|
||||
startEditorWindow: ->
|
||||
@unloaded = false
|
||||
@loadState().then (state) =>
|
||||
updateProcessEnvPromise = updateProcessEnv(@getLoadSettings().env)
|
||||
updateProcessEnvPromise.then =>
|
||||
@packages.triggerActivationHook('core:loaded-shell-environment')
|
||||
|
||||
loadStatePromise = @loadState().then (state) =>
|
||||
@windowDimensions = state?.windowDimensions
|
||||
@displayWindow().then =>
|
||||
@commandInstaller.installAtomCommand false, (error) ->
|
||||
@@ -706,6 +714,8 @@ class AtomEnvironment extends Model
|
||||
|
||||
@openInitialEmptyEditorIfNecessary()
|
||||
|
||||
Promise.all([loadStatePromise, updateProcessEnvPromise])
|
||||
|
||||
serialize: (options) ->
|
||||
version: @constructor.version
|
||||
project: @project.serialize(options)
|
||||
|
||||
62
src/atom-paths.js
Normal file
62
src/atom-paths.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/** @babel */
|
||||
|
||||
const fs = require('fs-plus')
|
||||
const path = require('path')
|
||||
|
||||
const hasWriteAccess = (dir) => {
|
||||
const testFilePath = path.join(dir, 'write.test')
|
||||
try {
|
||||
fs.writeFileSync(testFilePath, new Date().toISOString(), { flag: 'w+' })
|
||||
fs.unlinkSync(testFilePath)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const getAppDirectory = () => {
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return path.join(process.execPath.substring(0, process.execPath.indexOf('.app')), '..')
|
||||
case 'linux':
|
||||
case 'win32':
|
||||
return path.join(process.execPath, '..')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setAtomHome: (homePath) => {
|
||||
// When a read-writeable .atom folder exists above app use that
|
||||
const portableHomePath = path.join(getAppDirectory(), '.atom')
|
||||
if (fs.existsSync(portableHomePath)) {
|
||||
if (hasWriteAccess(portableHomePath)) {
|
||||
process.env.ATOM_HOME = portableHomePath
|
||||
} else {
|
||||
// A path exists so it was intended to be used but we didn't have rights, so warn.
|
||||
console.log(`Insufficient permission to portable Atom home "${portableHomePath}".`)
|
||||
}
|
||||
}
|
||||
|
||||
// Check ATOM_HOME environment variable next
|
||||
if (process.env.ATOM_HOME !== undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
// Fall back to default .atom folder in users home folder
|
||||
process.env.ATOM_HOME = path.join(homePath, '.atom')
|
||||
},
|
||||
|
||||
setUserData: (app) => {
|
||||
const electronUserDataPath = path.join(process.env.ATOM_HOME, 'electronUserData')
|
||||
if (fs.existsSync(electronUserDataPath)) {
|
||||
if (hasWriteAccess(electronUserDataPath)) {
|
||||
app.setPath('userData', electronUserDataPath)
|
||||
} else {
|
||||
// A path exists so it was intended to be used but we didn't have rights, so warn.
|
||||
console.log(`Insufficient permission to Electron user data "${electronUserDataPath}".`)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getAppDirectory: getAppDirectory
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
BufferedProcess = require './buffered-process'
|
||||
path = require 'path'
|
||||
|
||||
# Extended: Like {BufferedProcess}, but accepts a Node script as the command
|
||||
# to run.
|
||||
#
|
||||
# This is necessary on Windows since it doesn't support shebang `#!` lines.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# ```coffee
|
||||
# {BufferedNodeProcess} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class BufferedNodeProcess extends BufferedProcess
|
||||
|
||||
# Public: Runs the given Node script by spawning a new child process.
|
||||
#
|
||||
# * `options` An {Object} with the following keys:
|
||||
# * `command` The {String} path to the JavaScript script to execute.
|
||||
# * `args` The {Array} of arguments to pass to the script (optional).
|
||||
# * `options` The options {Object} to pass to Node's `ChildProcess.spawn`
|
||||
# method (optional).
|
||||
# * `stdout` The callback {Function} that receives a single argument which
|
||||
# contains the standard output from the command. The callback is
|
||||
# called as data is received but it's buffered to ensure only
|
||||
# complete lines are passed until the source stream closes. After
|
||||
# the source stream has closed all remaining data is sent in a
|
||||
# final call (optional).
|
||||
# * `stderr` The callback {Function} that receives a single argument which
|
||||
# contains the standard error output from the command. The
|
||||
# callback is called as data is received but it's buffered to
|
||||
# ensure only complete lines are passed until the source stream
|
||||
# closes. After the source stream has closed all remaining data
|
||||
# is sent in a final call (optional).
|
||||
# * `exit` The callback {Function} which receives a single argument
|
||||
# containing the exit status (optional).
|
||||
constructor: ({command, args, options, stdout, stderr, exit}) ->
|
||||
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)
|
||||
args.unshift('--no-deprecation')
|
||||
|
||||
super({command: process.execPath, args, options, stdout, stderr, exit})
|
||||
56
src/buffered-node-process.js
Normal file
56
src/buffered-node-process.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/** @babel */
|
||||
|
||||
import BufferedProcess from './buffered-process'
|
||||
|
||||
// Extended: Like {BufferedProcess}, but accepts a Node script as the command
|
||||
// to run.
|
||||
//
|
||||
// This is necessary on Windows since it doesn't support shebang `#!` lines.
|
||||
//
|
||||
// ## Examples
|
||||
//
|
||||
// ```js
|
||||
// const {BufferedNodeProcess} = require('atom')
|
||||
// ```
|
||||
export default class BufferedNodeProcess extends BufferedProcess {
|
||||
|
||||
// Public: Runs the given Node script by spawning a new child process.
|
||||
//
|
||||
// * `options` An {Object} with the following keys:
|
||||
// * `command` The {String} path to the JavaScript script to execute.
|
||||
// * `args` The {Array} of arguments to pass to the script (optional).
|
||||
// * `options` The options {Object} to pass to Node's `ChildProcess.spawn`
|
||||
// method (optional).
|
||||
// * `stdout` The callback {Function} that receives a single argument which
|
||||
// contains the standard output from the command. The callback is
|
||||
// called as data is received but it's buffered to ensure only
|
||||
// complete lines are passed until the source stream closes. After
|
||||
// the source stream has closed all remaining data is sent in a
|
||||
// final call (optional).
|
||||
// * `stderr` The callback {Function} that receives a single argument which
|
||||
// contains the standard error output from the command. The
|
||||
// callback is called as data is received but it's buffered to
|
||||
// ensure only complete lines are passed until the source stream
|
||||
// closes. After the source stream has closed all remaining data
|
||||
// is sent in a final call (optional).
|
||||
// * `exit` The callback {Function} which receives a single argument
|
||||
// containing the exit status (optional).
|
||||
constructor ({command, args, options = {}, stdout, stderr, exit}) {
|
||||
options.env = options.env || Object.create(process.env)
|
||||
options.env.ELECTRON_RUN_AS_NODE = 1
|
||||
options.env.ELECTRON_NO_ATTACH_CONSOLE = 1
|
||||
|
||||
args = args ? args.slice() : []
|
||||
args.unshift(command)
|
||||
args.unshift('--no-deprecation')
|
||||
|
||||
super({
|
||||
command: process.execPath,
|
||||
args,
|
||||
options,
|
||||
stdout,
|
||||
stderr,
|
||||
exit
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
_ = require 'underscore-plus'
|
||||
ChildProcess = require 'child_process'
|
||||
{Emitter} = require 'event-kit'
|
||||
path = require 'path'
|
||||
|
||||
# Extended: A wrapper which provides standard error/output line buffering for
|
||||
# Node's ChildProcess.
|
||||
#
|
||||
# ## Examples
|
||||
#
|
||||
# ```coffee
|
||||
# {BufferedProcess} = require 'atom'
|
||||
#
|
||||
# command = 'ps'
|
||||
# args = ['-ef']
|
||||
# stdout = (output) -> console.log(output)
|
||||
# exit = (code) -> console.log("ps -ef exited with #{code}")
|
||||
# process = new BufferedProcess({command, args, stdout, exit})
|
||||
# ```
|
||||
module.exports =
|
||||
class BufferedProcess
|
||||
###
|
||||
Section: Construction
|
||||
###
|
||||
|
||||
# Public: Runs the given command by spawning a new child process.
|
||||
#
|
||||
# * `options` An {Object} with the following keys:
|
||||
# * `command` The {String} command to execute.
|
||||
# * `args` The {Array} of arguments to pass to the command (optional).
|
||||
# * `options` {Object} (optional) The options {Object} to pass to Node's
|
||||
# `ChildProcess.spawn` method.
|
||||
# * `stdout` {Function} (optional) The callback that receives a single
|
||||
# argument which contains the standard output from the command. The
|
||||
# callback is called as data is received but it's buffered to ensure only
|
||||
# complete lines are passed until the source stream closes. After the
|
||||
# source stream has closed all remaining data is sent in a final call.
|
||||
# * `data` {String}
|
||||
# * `stderr` {Function} (optional) The callback that receives a single
|
||||
# argument which contains the standard error output from the command. The
|
||||
# callback is called as data is received but it's buffered to ensure only
|
||||
# complete lines are passed until the source stream closes. After the
|
||||
# source stream has closed all remaining data is sent in a final call.
|
||||
# * `data` {String}
|
||||
# * `exit` {Function} (optional) The callback which receives a single
|
||||
# argument containing the exit status.
|
||||
# * `code` {Number}
|
||||
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
|
||||
@emitter = new Emitter
|
||||
options ?= {}
|
||||
@command = command
|
||||
# Related to joyent/node#2318
|
||||
if process.platform is 'win32' and not options.shell?
|
||||
# Quote all arguments and escapes inner quotes
|
||||
if args?
|
||||
cmdArgs = args.filter (arg) -> arg?
|
||||
cmdArgs = cmdArgs.map (arg) =>
|
||||
if @isExplorerCommand(command) and /^\/[a-zA-Z]+,.*$/.test(arg)
|
||||
# Don't wrap /root,C:\folder style arguments to explorer calls in
|
||||
# quotes since they will not be interpreted correctly if they are
|
||||
arg
|
||||
else
|
||||
"\"#{arg.toString().replace(/"/g, '\\"')}\""
|
||||
else
|
||||
cmdArgs = []
|
||||
if /\s/.test(command)
|
||||
cmdArgs.unshift("\"#{command}\"")
|
||||
else
|
||||
cmdArgs.unshift(command)
|
||||
cmdArgs = ['/s', '/d', '/c', "\"#{cmdArgs.join(' ')}\""]
|
||||
cmdOptions = _.clone(options)
|
||||
cmdOptions.windowsVerbatimArguments = true
|
||||
@spawn(@getCmdPath(), cmdArgs, cmdOptions)
|
||||
else
|
||||
@spawn(command, args, options)
|
||||
|
||||
@killed = false
|
||||
@handleEvents(stdout, stderr, exit)
|
||||
|
||||
###
|
||||
Section: Event Subscription
|
||||
###
|
||||
|
||||
# Public: Will call your callback when an error will be raised by the process.
|
||||
# Usually this is due to the command not being available or not on the PATH.
|
||||
# You can call `handle()` on the object passed to your callback to indicate
|
||||
# that you have handled this error.
|
||||
#
|
||||
# * `callback` {Function} callback
|
||||
# * `errorObject` {Object}
|
||||
# * `error` {Object} the error object
|
||||
# * `handle` {Function} call this to indicate you have handled the error.
|
||||
# The error will not be thrown if this function is called.
|
||||
#
|
||||
# Returns a {Disposable}
|
||||
onWillThrowError: (callback) ->
|
||||
@emitter.on 'will-throw-error', callback
|
||||
|
||||
###
|
||||
Section: Helper Methods
|
||||
###
|
||||
|
||||
# Helper method to pass data line by line.
|
||||
#
|
||||
# * `stream` The Stream to read from.
|
||||
# * `onLines` The callback to call with each line of data.
|
||||
# * `onDone` The callback to call when the stream has closed.
|
||||
bufferStream: (stream, onLines, onDone) ->
|
||||
stream.setEncoding('utf8')
|
||||
buffered = ''
|
||||
|
||||
stream.on 'data', (data) =>
|
||||
return if @killed
|
||||
bufferedLength = buffered.length
|
||||
buffered += data
|
||||
lastNewlineIndex = data.lastIndexOf('\n')
|
||||
if lastNewlineIndex isnt -1
|
||||
lineLength = lastNewlineIndex + bufferedLength + 1
|
||||
onLines(buffered.substring(0, lineLength))
|
||||
buffered = buffered.substring(lineLength)
|
||||
|
||||
stream.on 'close', =>
|
||||
return if @killed
|
||||
onLines(buffered) if buffered.length > 0
|
||||
onDone()
|
||||
|
||||
# Kill all child processes of the spawned cmd.exe process on Windows.
|
||||
#
|
||||
# This is required since killing the cmd.exe does not terminate child
|
||||
# processes.
|
||||
killOnWindows: ->
|
||||
return unless @process?
|
||||
|
||||
parentPid = @process.pid
|
||||
cmd = 'wmic'
|
||||
args = [
|
||||
'process'
|
||||
'where'
|
||||
"(ParentProcessId=#{parentPid})"
|
||||
'get'
|
||||
'processid'
|
||||
]
|
||||
|
||||
try
|
||||
wmicProcess = ChildProcess.spawn(cmd, args)
|
||||
catch spawnError
|
||||
@killProcess()
|
||||
return
|
||||
|
||||
wmicProcess.on 'error', -> # ignore errors
|
||||
output = ''
|
||||
wmicProcess.stdout.on 'data', (data) -> output += data
|
||||
wmicProcess.stdout.on 'close', =>
|
||||
pidsToKill = output.split(/\s+/)
|
||||
.filter (pid) -> /^\d+$/.test(pid)
|
||||
.map (pid) -> parseInt(pid)
|
||||
.filter (pid) -> pid isnt parentPid and 0 < pid < Infinity
|
||||
|
||||
for pid in pidsToKill
|
||||
try
|
||||
process.kill(pid)
|
||||
@killProcess()
|
||||
|
||||
killProcess: ->
|
||||
@process?.kill()
|
||||
@process = null
|
||||
|
||||
isExplorerCommand: (command) ->
|
||||
if command is 'explorer.exe' or command is 'explorer'
|
||||
true
|
||||
else if process.env.SystemRoot
|
||||
command is path.join(process.env.SystemRoot, 'explorer.exe') or command is path.join(process.env.SystemRoot, 'explorer')
|
||||
else
|
||||
false
|
||||
|
||||
getCmdPath: ->
|
||||
if process.env.comspec
|
||||
process.env.comspec
|
||||
else if process.env.SystemRoot
|
||||
path.join(process.env.SystemRoot, 'System32', 'cmd.exe')
|
||||
else
|
||||
'cmd.exe'
|
||||
|
||||
# Public: Terminate the process.
|
||||
kill: ->
|
||||
return if @killed
|
||||
|
||||
@killed = true
|
||||
if process.platform is 'win32'
|
||||
@killOnWindows()
|
||||
else
|
||||
@killProcess()
|
||||
|
||||
undefined
|
||||
|
||||
spawn: (command, args, options) ->
|
||||
try
|
||||
@process = ChildProcess.spawn(command, args, options)
|
||||
catch spawnError
|
||||
process.nextTick => @handleError(spawnError)
|
||||
|
||||
handleEvents: (stdout, stderr, exit) ->
|
||||
return unless @process?
|
||||
|
||||
stdoutClosed = true
|
||||
stderrClosed = true
|
||||
processExited = true
|
||||
exitCode = 0
|
||||
triggerExitCallback = ->
|
||||
return if @killed
|
||||
if stdoutClosed and stderrClosed and processExited
|
||||
exit?(exitCode)
|
||||
|
||||
if stdout
|
||||
stdoutClosed = false
|
||||
@bufferStream @process.stdout, stdout, ->
|
||||
stdoutClosed = true
|
||||
triggerExitCallback()
|
||||
|
||||
if stderr
|
||||
stderrClosed = false
|
||||
@bufferStream @process.stderr, stderr, ->
|
||||
stderrClosed = true
|
||||
triggerExitCallback()
|
||||
|
||||
if exit
|
||||
processExited = false
|
||||
@process.on 'exit', (code) ->
|
||||
exitCode = code
|
||||
processExited = true
|
||||
triggerExitCallback()
|
||||
|
||||
@process.on 'error', (error) => @handleError(error)
|
||||
return
|
||||
|
||||
handleError: (error) ->
|
||||
handled = false
|
||||
handle = -> handled = true
|
||||
|
||||
@emitter.emit 'will-throw-error', {error, handle}
|
||||
|
||||
if error.code is 'ENOENT' and error.syscall.indexOf('spawn') is 0
|
||||
error = new Error("Failed to spawn command `#{@command}`. Make sure `#{@command}` is installed and on your PATH", error.path)
|
||||
error.name = 'BufferedProcessError'
|
||||
|
||||
throw error unless handled
|
||||
298
src/buffered-process.js
Normal file
298
src/buffered-process.js
Normal file
@@ -0,0 +1,298 @@
|
||||
/** @babel */
|
||||
|
||||
import _ from 'underscore-plus'
|
||||
import ChildProcess from 'child_process'
|
||||
import {Emitter} from 'event-kit'
|
||||
import path from 'path'
|
||||
|
||||
// Extended: A wrapper which provides standard error/output line buffering for
|
||||
// Node's ChildProcess.
|
||||
//
|
||||
// ## Examples
|
||||
//
|
||||
// ```js
|
||||
// {BufferedProcess} = require('atom')
|
||||
//
|
||||
// const command = 'ps'
|
||||
// const args = ['-ef']
|
||||
// const stdout = (output) => console.log(output)
|
||||
// const exit = (code) => console.log("ps -ef exited with #{code}")
|
||||
// const process = new BufferedProcess({command, args, stdout, exit})
|
||||
// ```
|
||||
export default class BufferedProcess {
|
||||
/*
|
||||
Section: Construction
|
||||
*/
|
||||
|
||||
// Public: Runs the given command by spawning a new child process.
|
||||
//
|
||||
// * `options` An {Object} with the following keys:
|
||||
// * `command` The {String} command to execute.
|
||||
// * `args` The {Array} of arguments to pass to the command (optional).
|
||||
// * `options` {Object} (optional) The options {Object} to pass to Node's
|
||||
// `ChildProcess.spawn` method.
|
||||
// * `stdout` {Function} (optional) The callback that receives a single
|
||||
// argument which contains the standard output from the command. The
|
||||
// callback is called as data is received but it's buffered to ensure only
|
||||
// complete lines are passed until the source stream closes. After the
|
||||
// source stream has closed all remaining data is sent in a final call.
|
||||
// * `data` {String}
|
||||
// * `stderr` {Function} (optional) The callback that receives a single
|
||||
// argument which contains the standard error output from the command. The
|
||||
// callback is called as data is received but it's buffered to ensure only
|
||||
// complete lines are passed until the source stream closes. After the
|
||||
// source stream has closed all remaining data is sent in a final call.
|
||||
// * `data` {String}
|
||||
// * `exit` {Function} (optional) The callback which receives a single
|
||||
// argument containing the exit status.
|
||||
// * `code` {Number}
|
||||
constructor ({command, args, options = {}, stdout, stderr, exit} = {}) {
|
||||
this.emitter = new Emitter()
|
||||
this.command = command
|
||||
// Related to joyent/node#2318
|
||||
if (process.platform === 'win32' && options.shell === undefined) {
|
||||
this.spawnWithEscapedWindowsArgs(command, args, options)
|
||||
} else {
|
||||
this.spawn(command, args, options)
|
||||
}
|
||||
|
||||
this.killed = false
|
||||
this.handleEvents(stdout, stderr, exit)
|
||||
}
|
||||
|
||||
// Windows has a bunch of special rules that node still doesn't take care of for you
|
||||
spawnWithEscapedWindowsArgs (command, args, options) {
|
||||
let cmdArgs = []
|
||||
// Quote all arguments and escapes inner quotes
|
||||
if (args) {
|
||||
cmdArgs = args.filter((arg) => arg != null)
|
||||
.map((arg) => {
|
||||
if (this.isExplorerCommand(command) && /^\/[a-zA-Z]+,.*$/.test(arg)) {
|
||||
// Don't wrap /root,C:\folder style arguments to explorer calls in
|
||||
// quotes since they will not be interpreted correctly if they are
|
||||
return arg
|
||||
} else {
|
||||
// Escape double quotes by putting a backslash in front of them
|
||||
return `\"${arg.toString().replace(/"/g, '\\"')}\"`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// The command itself is quoted if it contains spaces, &, ^, | or # chars
|
||||
cmdArgs.unshift(/\s|&|\^|\(|\)|\||#/.test(command) ? `\"${command}\"` : command)
|
||||
|
||||
const cmdOptions = _.clone(options)
|
||||
cmdOptions.windowsVerbatimArguments = true
|
||||
|
||||
this.spawn(this.getCmdPath(), ['/s', '/d', '/c', `\"${cmdArgs.join(' ')}\"`], cmdOptions)
|
||||
}
|
||||
|
||||
/*
|
||||
Section: Event Subscription
|
||||
*/
|
||||
|
||||
// Public: Will call your callback when an error will be raised by the process.
|
||||
// Usually this is due to the command not being available or not on the PATH.
|
||||
// You can call `handle()` on the object passed to your callback to indicate
|
||||
// that you have handled this error.
|
||||
//
|
||||
// * `callback` {Function} callback
|
||||
// * `errorObject` {Object}
|
||||
// * `error` {Object} the error object
|
||||
// * `handle` {Function} call this to indicate you have handled the error.
|
||||
// The error will not be thrown if this function is called.
|
||||
//
|
||||
// Returns a {Disposable}
|
||||
onWillThrowError (callback) {
|
||||
return this.emitter.on('will-throw-error', callback)
|
||||
}
|
||||
|
||||
/*
|
||||
Section: Helper Methods
|
||||
*/
|
||||
|
||||
// Helper method to pass data line by line.
|
||||
//
|
||||
// * `stream` The Stream to read from.
|
||||
// * `onLines` The callback to call with each line of data.
|
||||
// * `onDone` The callback to call when the stream has closed.
|
||||
bufferStream (stream, onLines, onDone) {
|
||||
stream.setEncoding('utf8')
|
||||
let buffered = ''
|
||||
|
||||
stream.on('data', (data) => {
|
||||
if (this.killed) return
|
||||
|
||||
let bufferedLength = buffered.length
|
||||
buffered += data
|
||||
let lastNewlineIndex = data.lastIndexOf('\n')
|
||||
|
||||
if (lastNewlineIndex !== -1) {
|
||||
let lineLength = lastNewlineIndex + bufferedLength + 1
|
||||
onLines(buffered.substring(0, lineLength))
|
||||
buffered = buffered.substring(lineLength)
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('close', () => {
|
||||
if (this.killed) return
|
||||
if (buffered.length > 0) onLines(buffered)
|
||||
onDone()
|
||||
})
|
||||
}
|
||||
|
||||
// Kill all child processes of the spawned cmd.exe process on Windows.
|
||||
//
|
||||
// This is required since killing the cmd.exe does not terminate child
|
||||
// processes.
|
||||
killOnWindows () {
|
||||
if (!this.process) return
|
||||
|
||||
const parentPid = this.process.pid
|
||||
const cmd = 'wmic'
|
||||
const args = [
|
||||
'process',
|
||||
'where',
|
||||
`(ParentProcessId=${parentPid})`,
|
||||
'get',
|
||||
'processid'
|
||||
]
|
||||
|
||||
let wmicProcess
|
||||
|
||||
try {
|
||||
wmicProcess = ChildProcess.spawn(cmd, args)
|
||||
} catch (spawnError) {
|
||||
this.killProcess()
|
||||
return
|
||||
}
|
||||
|
||||
wmicProcess.on('error', () => {}) // ignore errors
|
||||
|
||||
let output = ''
|
||||
wmicProcess.stdout.on('data', (data) => {
|
||||
output += data
|
||||
})
|
||||
wmicProcess.stdout.on('close', () => {
|
||||
const pidsToKill = output.split(/\s+/)
|
||||
.filter((pid) => /^\d+$/.test(pid))
|
||||
.map((pid) => parseInt(pid))
|
||||
.filter((pid) => pid !== parentPid && pid > 0 && pid < Infinity)
|
||||
|
||||
for (let pid of pidsToKill) {
|
||||
try {
|
||||
process.kill(pid)
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
this.killProcess()
|
||||
})
|
||||
}
|
||||
|
||||
killProcess () {
|
||||
if (this.process) this.process.kill()
|
||||
this.process = null
|
||||
}
|
||||
|
||||
isExplorerCommand (command) {
|
||||
if (command === 'explorer.exe' || command === 'explorer') {
|
||||
return true
|
||||
} else if (process.env.SystemRoot) {
|
||||
return command === path.join(process.env.SystemRoot, 'explorer.exe') || command === path.join(process.env.SystemRoot, 'explorer')
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
getCmdPath () {
|
||||
if (process.env.comspec) {
|
||||
return process.env.comspec
|
||||
} else if (process.env.SystemRoot) {
|
||||
return path.join(process.env.SystemRoot, 'System32', 'cmd.exe')
|
||||
} else {
|
||||
return 'cmd.exe'
|
||||
}
|
||||
}
|
||||
|
||||
// Public: Terminate the process.
|
||||
kill () {
|
||||
if (this.killed) return
|
||||
|
||||
this.killed = true
|
||||
if (process.platform === 'win32') {
|
||||
this.killOnWindows()
|
||||
} else {
|
||||
this.killProcess()
|
||||
}
|
||||
}
|
||||
|
||||
spawn (command, args, options) {
|
||||
try {
|
||||
this.process = ChildProcess.spawn(command, args, options)
|
||||
} catch (spawnError) {
|
||||
process.nextTick(() => this.handleError(spawnError))
|
||||
}
|
||||
}
|
||||
|
||||
handleEvents (stdout, stderr, exit) {
|
||||
if (!this.process) return
|
||||
|
||||
const triggerExitCallback = () => {
|
||||
if (this.killed) return
|
||||
if (stdoutClosed && stderrClosed && processExited && typeof exit === 'function') {
|
||||
exit(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
let stdoutClosed = true
|
||||
let stderrClosed = true
|
||||
let processExited = true
|
||||
let exitCode = 0
|
||||
|
||||
if (stdout) {
|
||||
stdoutClosed = false
|
||||
this.bufferStream(this.process.stdout, stdout, () => {
|
||||
stdoutClosed = true
|
||||
triggerExitCallback()
|
||||
})
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
stderrClosed = false
|
||||
this.bufferStream(this.process.stderr, stderr, () => {
|
||||
stderrClosed = true
|
||||
triggerExitCallback()
|
||||
})
|
||||
}
|
||||
|
||||
if (exit) {
|
||||
processExited = false
|
||||
this.process.on('exit', (code) => {
|
||||
exitCode = code
|
||||
processExited = true
|
||||
triggerExitCallback()
|
||||
})
|
||||
}
|
||||
|
||||
this.process.on('error', (error) => {
|
||||
this.handleError(error)
|
||||
})
|
||||
}
|
||||
|
||||
handleError (error) {
|
||||
let handled = false
|
||||
|
||||
const handle = () => {
|
||||
handled = true
|
||||
}
|
||||
|
||||
this.emitter.emit('will-throw-error', {error, handle})
|
||||
|
||||
if (error.code === 'ENOENT' && error.syscall.indexOf('spawn') === 0) {
|
||||
error = new Error(`Failed to spawn command \`${this.command}\`. Make sure \`${this.command}\` is installed and on your PATH`, error.path)
|
||||
error.name = 'BufferedProcessError'
|
||||
}
|
||||
|
||||
if (!handled) throw error
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,26 @@
|
||||
|
||||
var path = require('path')
|
||||
var fs = require('fs-plus')
|
||||
|
||||
var PackageTranspilationRegistry = require('./package-transpilation-registry')
|
||||
var CSON = null
|
||||
|
||||
var packageTranspilationRegistry = new PackageTranspilationRegistry()
|
||||
|
||||
var COMPILERS = {
|
||||
'.js': require('./babel'),
|
||||
'.ts': require('./typescript'),
|
||||
'.coffee': require('./coffee-script')
|
||||
'.js': packageTranspilationRegistry.wrapTranspiler(require('./babel')),
|
||||
'.ts': packageTranspilationRegistry.wrapTranspiler(require('./typescript')),
|
||||
'.coffee': packageTranspilationRegistry.wrapTranspiler(require('./coffee-script'))
|
||||
}
|
||||
|
||||
exports.addTranspilerConfigForPath = function (packagePath, packageName, packageMeta, config) {
|
||||
packagePath = fs.realpathSync(packagePath)
|
||||
packageTranspilationRegistry.addTranspilerConfigForPath(packagePath, packageName, packageMeta, config)
|
||||
}
|
||||
|
||||
exports.removeTranspilerConfigForPath = function (packagePath) {
|
||||
packagePath = fs.realpathSync(packagePath)
|
||||
packageTranspilationRegistry.removeTranspilerConfigForPath(packagePath)
|
||||
}
|
||||
|
||||
var cacheStats = {}
|
||||
@@ -118,6 +132,7 @@ require('source-map-support').install({
|
||||
}
|
||||
|
||||
var compiler = COMPILERS[path.extname(filePath)]
|
||||
if (!compiler) compiler = COMPILERS['.js']
|
||||
|
||||
try {
|
||||
var fileData = readCachedJavascript(compiler.getCachePath(sourceCode, filePath))
|
||||
|
||||
@@ -12,7 +12,7 @@ const configSchema = {
|
||||
properties: {
|
||||
ignoredNames: {
|
||||
type: 'array',
|
||||
default: ['.git', '.hg', '.svn', '.DS_Store', '._*', 'Thumbs.db'],
|
||||
default: ['.git', '.hg', '.svn', '.DS_Store', '._*', 'Thumbs.db', 'desktop.ini'],
|
||||
items: {
|
||||
type: 'string'
|
||||
},
|
||||
@@ -85,6 +85,8 @@ const configSchema = {
|
||||
default: 'utf8',
|
||||
enum: [
|
||||
'cp437',
|
||||
'cp850',
|
||||
'cp866',
|
||||
'eucjp',
|
||||
'euckr',
|
||||
'gbk',
|
||||
@@ -117,15 +119,24 @@ const configSchema = {
|
||||
'windows1255',
|
||||
'windows1256',
|
||||
'windows1257',
|
||||
'windows1258',
|
||||
'windows866'
|
||||
'windows1258'
|
||||
]
|
||||
},
|
||||
openEmptyEditorOnStart: {
|
||||
description: 'Automatically open an empty editor on startup.',
|
||||
description: 'When checked opens an untitled editor when loading a blank environment (such as with _File > New Window_ or when "Restore Previous Windows On Start" is unchecked); otherwise no editor is opened when loading a blank environment. This setting has no effect when restoring a previous state.',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
restorePreviousWindowsOnStart: {
|
||||
description: 'When checked restores the last state of all Atom windows when started from the icon or `atom` by itself from the command line; otherwise a blank environment is loaded.',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
reopenProjectMenuCount: {
|
||||
description: 'How many recent projects to show in the Reopen Project menu.',
|
||||
type: 'integer',
|
||||
default: 15
|
||||
},
|
||||
automaticallyUpdate: {
|
||||
description: 'Automatically update Atom when a new release is available.',
|
||||
type: 'boolean',
|
||||
@@ -159,7 +170,7 @@ const configSchema = {
|
||||
warnOnLargeFileLimit: {
|
||||
description: 'Warn before opening files larger than this number of megabytes.',
|
||||
type: 'number',
|
||||
default: 20
|
||||
default: 40
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -653,9 +653,6 @@ class Cursor extends Model
|
||||
fn()
|
||||
@autoscroll() if options.autoscroll ? @isLastCursor()
|
||||
|
||||
getPixelRect: ->
|
||||
@editor.pixelRectForScreenRange(@getScreenRange())
|
||||
|
||||
getScreenRange: ->
|
||||
{row, column} = @getScreenPosition()
|
||||
new Range(new Point(row, column), new Point(row, column + 1))
|
||||
|
||||
@@ -238,6 +238,7 @@ class GitRepository
|
||||
|
||||
# Public: Returns the git configuration value specified by the key.
|
||||
#
|
||||
# * `key` The {String} key for the configuration to lookup.
|
||||
# * `path` An optional {String} path in the repository to get this information
|
||||
# for, only needed if the repository has submodules.
|
||||
getConfigValue: (key, path) -> @getRepo(path).getConfigValue(key)
|
||||
|
||||
@@ -103,6 +103,7 @@ class GutterContainerComponent
|
||||
@domNode.appendChild(gutterComponent.getDomNode())
|
||||
else
|
||||
@domNode.insertBefore(gutterComponent.getDomNode(), @domNode.children[indexInOldGutters])
|
||||
indexInOldGutters += 1
|
||||
|
||||
# Remove any gutters that were not present in the new gutters state.
|
||||
for gutterComponentDescription in @gutterComponents
|
||||
|
||||
150
src/history-manager.js
Normal file
150
src/history-manager.js
Normal file
@@ -0,0 +1,150 @@
|
||||
/** @babel */
|
||||
|
||||
import {Emitter} from 'event-kit'
|
||||
|
||||
// Extended: History manager for remembering which projects have been opened.
|
||||
//
|
||||
// An instance of this class is always available as the `atom.history` global.
|
||||
//
|
||||
// The project history is used to enable the 'Reopen Project' menu.
|
||||
export class HistoryManager {
|
||||
constructor ({project, commands, localStorage}) {
|
||||
this.localStorage = localStorage
|
||||
commands.add('atom-workspace', {'application:clear-project-history': this.clearProjects.bind(this)})
|
||||
this.emitter = new Emitter()
|
||||
this.loadState()
|
||||
project.onDidChangePaths((projectPaths) => this.addProject(projectPaths))
|
||||
}
|
||||
|
||||
// Public: Obtain a list of previously opened projects.
|
||||
//
|
||||
// Returns an {Array} of {HistoryProject} objects, most recent first.
|
||||
getProjects () {
|
||||
return this.projects.map(p => new HistoryProject(p.paths, p.lastOpened))
|
||||
}
|
||||
|
||||
// Public: Clear all projects from the history.
|
||||
//
|
||||
// Note: This is not a privacy function - other traces will still exist,
|
||||
// e.g. window state.
|
||||
clearProjects () {
|
||||
this.projects = []
|
||||
this.saveState()
|
||||
this.didChangeProjects()
|
||||
}
|
||||
|
||||
// Public: Invoke the given callback when the list of projects changes.
|
||||
//
|
||||
// * `callback` {Function}
|
||||
//
|
||||
// Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeProjects (callback) {
|
||||
return this.emitter.on('did-change-projects', callback)
|
||||
}
|
||||
|
||||
didChangeProjects (args) {
|
||||
this.emitter.emit('did-change-projects', args || { reloaded: false })
|
||||
}
|
||||
|
||||
addProject (paths, lastOpened) {
|
||||
if (paths.length === 0) return
|
||||
|
||||
let project = this.getProject(paths)
|
||||
if (!project) {
|
||||
project = new HistoryProject(paths)
|
||||
this.projects.push(project)
|
||||
}
|
||||
project.lastOpened = lastOpened || new Date()
|
||||
this.projects.sort((a, b) => b.lastOpened - a.lastOpened)
|
||||
|
||||
this.saveState()
|
||||
this.didChangeProjects()
|
||||
}
|
||||
|
||||
getProject (paths) {
|
||||
for (var i = 0; i < this.projects.length; i++) {
|
||||
if (arrayEquivalent(paths, this.projects[i].paths)) {
|
||||
return this.projects[i]
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
loadState () {
|
||||
const state = JSON.parse(this.localStorage.getItem('history'))
|
||||
if (state && state.projects) {
|
||||
this.projects = state.projects.filter(p => Array.isArray(p.paths) && p.paths.length > 0).map(p => new HistoryProject(p.paths, new Date(p.lastOpened)))
|
||||
this.didChangeProjects({ reloaded: true })
|
||||
} else {
|
||||
this.projects = []
|
||||
}
|
||||
}
|
||||
|
||||
saveState () {
|
||||
const state = JSON.stringify({
|
||||
projects: this.projects.map(p => ({
|
||||
paths: p.paths, lastOpened: p.lastOpened
|
||||
}))
|
||||
})
|
||||
this.localStorage.setItem('history', state)
|
||||
}
|
||||
|
||||
async importProjectHistory () {
|
||||
for (let project of await HistoryImporter.getAllProjects()) {
|
||||
this.addProject(project.paths, project.lastOpened)
|
||||
}
|
||||
this.saveState()
|
||||
this.didChangeProjects()
|
||||
}
|
||||
}
|
||||
|
||||
function arrayEquivalent (a, b) {
|
||||
if (a.length !== b.length) return false
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export class HistoryProject {
|
||||
constructor (paths, lastOpened) {
|
||||
this.paths = paths
|
||||
this.lastOpened = lastOpened || new Date()
|
||||
}
|
||||
|
||||
set paths (paths) { this._paths = paths }
|
||||
get paths () { return this._paths }
|
||||
|
||||
set lastOpened (lastOpened) { this._lastOpened = lastOpened }
|
||||
get lastOpened () { return this._lastOpened }
|
||||
}
|
||||
|
||||
class HistoryImporter {
|
||||
static async getStateStoreCursor () {
|
||||
const db = await atom.stateStore.dbPromise
|
||||
const store = db.transaction(['states']).objectStore('states')
|
||||
return store.openCursor()
|
||||
}
|
||||
|
||||
static async getAllProjects (stateStore) {
|
||||
const request = await HistoryImporter.getStateStoreCursor()
|
||||
return new Promise((resolve, reject) => {
|
||||
const rows = []
|
||||
request.onerror = reject
|
||||
request.onsuccess = event => {
|
||||
const cursor = event.target.result
|
||||
if (cursor) {
|
||||
let project = cursor.value.value.project
|
||||
let storedAt = cursor.value.storedAt
|
||||
if (project && project.paths && storedAt) {
|
||||
rows.push(new HistoryProject(project.paths, new Date(Date.parse(storedAt))))
|
||||
}
|
||||
cursor.continue()
|
||||
} else {
|
||||
resolve(rows)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,7 @@ module.exports = ({blobStore}) ->
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
{ipcRenderer} = require 'electron'
|
||||
{resourcePath, devMode, env} = getWindowLoadSettings()
|
||||
require '../src/electron-shims'
|
||||
|
||||
updateProcessEnv(env)
|
||||
require './electron-shims'
|
||||
|
||||
# Add application-specific exports to module search path.
|
||||
exportsPath = path.join(resourcePath, 'exports')
|
||||
|
||||
@@ -13,6 +13,7 @@ export default async function () {
|
||||
const ApplicationDelegate = require('../src/application-delegate')
|
||||
const AtomEnvironment = require('../src/atom-environment')
|
||||
const TextEditor = require('../src/text-editor')
|
||||
require('./electron-shims')
|
||||
|
||||
const exportsPath = path.join(resourcePath, 'exports')
|
||||
require('module').globalPaths.push(exportsPath) // Add 'exports' to module search path.
|
||||
|
||||
@@ -19,11 +19,12 @@ module.exports = ({blobStore}) ->
|
||||
path = require 'path'
|
||||
{ipcRenderer} = require 'electron'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
CompileCache = require './compile-cache'
|
||||
AtomEnvironment = require '../src/atom-environment'
|
||||
ApplicationDelegate = require '../src/application-delegate'
|
||||
Clipboard = require '../src/clipboard'
|
||||
TextEditor = require '../src/text-editor'
|
||||
require '../src/electron-shims'
|
||||
require './electron-shims'
|
||||
|
||||
{testRunnerPath, legacyTestRunnerPath, headless, logFile, testPaths} = getWindowLoadSettings()
|
||||
|
||||
@@ -58,6 +59,13 @@ module.exports = ({blobStore}) ->
|
||||
require('module').globalPaths.push(exportsPath)
|
||||
process.env.NODE_PATH = exportsPath # Set NODE_PATH env variable since tasks may need it.
|
||||
|
||||
# Set up optional transpilation for packages under test if any
|
||||
FindParentDir = require 'find-parent-dir'
|
||||
if packageRoot = FindParentDir.sync(testPaths[0], 'package.json')
|
||||
packageMetadata = require(path.join(packageRoot, 'package.json'))
|
||||
if packageMetadata.atomTranspilers
|
||||
CompileCache.addTranspilerConfigForPath(packageRoot, packageMetadata.name, packageMetadata, packageMetadata.atomTranspilers)
|
||||
|
||||
document.title = "Spec Suite"
|
||||
|
||||
clipboard = new Clipboard
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
module.exports =
|
||||
class InputComponent
|
||||
constructor: ->
|
||||
@domNode = document.createElement('input')
|
||||
@domNode.classList.add('hidden-input')
|
||||
@domNode.setAttribute('tabindex', -1)
|
||||
@domNode.setAttribute('data-react-skip-selection-restoration', true)
|
||||
@domNode.style['-webkit-transform'] = 'translateZ(0)'
|
||||
@domNode.addEventListener 'paste', (event) -> event.preventDefault()
|
||||
|
||||
getDomNode: ->
|
||||
@domNode
|
||||
constructor: (@domNode) ->
|
||||
|
||||
updateSync: (state) ->
|
||||
@oldState ?= {}
|
||||
|
||||
@@ -8,6 +8,9 @@ bundledKeymaps = require('../package.json')?._atomKeymaps
|
||||
KeymapManager::onDidLoadBundledKeymaps = (callback) ->
|
||||
@emitter.on 'did-load-bundled-keymaps', callback
|
||||
|
||||
KeymapManager::onDidLoadUserKeymap = (callback) ->
|
||||
@emitter.on 'did-load-user-keymap', callback
|
||||
|
||||
KeymapManager::loadBundledKeymaps = ->
|
||||
keymapsPath = path.join(@resourcePath, 'keymaps')
|
||||
if bundledKeymaps?
|
||||
@@ -49,6 +52,9 @@ KeymapManager::loadUserKeymap = ->
|
||||
stack = error.stack
|
||||
@notificationManager.addFatalError(error.message, {detail, stack, dismissable: true})
|
||||
|
||||
@emitter.emit 'did-load-user-keymap'
|
||||
|
||||
|
||||
KeymapManager::subscribeToFileReadFailure = ->
|
||||
@onDidFailToReadFile (error) =>
|
||||
userKeymapPath = @getUserKeymapPath()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user