Merge branch 'master' into sm-hidden-all

This commit is contained in:
simurai
2017-01-14 10:09:07 +09:00
140 changed files with 3437 additions and 1485 deletions

View File

@@ -49,3 +49,5 @@ addons:
- git
- libgnome-keyring-dev
- rpm
- libx11-dev
- libxkbfile-dev

View File

@@ -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**
![Screenshots and GIFs which follow reproduction steps to demonstrate the problem](url)
**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**
![Screenshots and GIFs which demonstrate the steps or part of Atom the enhancement suggestion is related to](url)
**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

View File

@@ -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.

View File

@@ -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
View 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 -->

View File

@@ -1,6 +1,6 @@
![Atom](https://cloud.githubusercontent.com/assets/72919/2874231/3af1db48-d3dd-11e3-98dc-6066f8bc766f.png)
[![macOS Build Status](https://circleci.com/gh/atom/atom.svg?style=svg)](https://circleci.com/gh/atom/atom) [![Linux Build Status](https://travis-ci.org/atom/atom.svg?branch=master)](https://travis-ci.org/atom/atom) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1tkktwh654w07eim?svg=true)](https://ci.appveyor.com/project/Atom/atom)
[![macOS Build Status](https://circleci.com/gh/atom/atom/tree/master.svg?style=shield)](https://circleci.com/gh/atom/atom) [![Linux Build Status](https://travis-ci.org/atom/atom.svg?branch=master)](https://travis-ci.org/atom/atom) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1tkktwh654w07eim?svg=true)](https://ci.appveyor.com/project/Atom/atom)
[![Dependency Status](https://david-dm.org/atom/atom.svg)](https://david-dm.org/atom/atom)
[![Join the Atom Community on Slack](http://atom-slack.herokuapp.com/badge.svg)](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).

View File

@@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "1.13.0"
"atom-package-manager": "1.15.1"
}
}

View File

@@ -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
View File

@@ -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

View File

@@ -1,112 +1,114 @@
# Atom build status
| System | macOS | Windows | Dependencies |
|--------|------|---------|--------------|
| Atom | [![macOS Build Status](https://travis-ci.org/atom/atom.svg?branch=master)](https://travis-ci.org/atom/atom) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1tkktwh654w07eim?svg=true)](https://ci.appveyor.com/project/Atom/atom) | [![Dependency Status](https://david-dm.org/atom/atom.svg)](https://david-dm.org/atom/atom) |
| APM | [![macOS Build Status](https://travis-ci.org/atom/apm.svg?branch=master)](https://travis-ci.org/atom/apm) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/j6ixw374a397ugkb/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/apm/branch/master) | [![Dependency Status](https://david-dm.org/atom/apm.svg)](https://david-dm.org/atom/apm) |
| Electron | [![macOS Build Status](https://travis-ci.org/electron/electron.svg?branch=master)](https://travis-ci.org/electron/electron) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/kvxe4byi7jcxbe26/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/electron) | [![Dependency Status](https://david-dm.org/electron/electron/dev-status.svg)](https://david-dm.org/electron/electron)
| System | Travis | AppVeyor/Win | Circle/Mac | Dependencies |
|--------|--------|--------------|------------|--------------|
| [Atom](https://github.com/atom/atom) | [![Travis Build Status](https://travis-ci.org/atom/atom.svg?branch=master)](https://travis-ci.org/atom/atom) | [![AppVeyor/Wi Build Status](https://ci.appveyor.com/api/projects/status/1tkktwh654w07eim?svg=true)](https://ci.appveyor.com/project/Atom/atom) | [![Circle/Mac Build Status](https://circleci.com/gh/atom/atom.svg?style=shield)](https://circleci.com/gh/atom/atom) | [![Dependency Status](https://david-dm.org/atom/atom.svg)](https://david-dm.org/atom/atom) |
| [APM](https://github.com/atom/apm) | [![Travis Build Status](https://travis-ci.org/atom/apm.svg?branch=master)](https://travis-ci.org/atom/apm) | [![AppVeyor/Wi Build Status](https://ci.appveyor.com/api/projects/status/j6ixw374a397ugkb/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/apm/branch/master) | | [![Dependency Status](https://david-dm.org/atom/apm.svg)](https://david-dm.org/atom/apm) |
| [Electron](https://github.com/electron/electron) | [![Travis Build Status](https://travis-ci.org/electron/electron.svg?branch=master)](https://travis-ci.org/electron/electron) | [![AppVeyor/Wi Build Status](https://ci.appveyor.com/api/projects/status/kvxe4byi7jcxbe26/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/electron) | | [![Dependency Status](https://david-dm.org/electron/electron/dev-status.svg)](https://david-dm.org/electron/electron)
## Packages
| Package | macOS | Windows | Dependencies |
|---------|------|---------|--------------|
| About | [![macOS Build Status](https://travis-ci.org/atom/about.svg?branch=master)](https://travis-ci.org/atom/about) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/msprea3vq47l8oce/branch/master?svg=true)](https://ci.appveyor.com/project/atom/about/branch/master) | [![Dependency Status](https://david-dm.org/atom/about.svg)](https://david-dm.org/atom/about) |
| Archive View | [![macOS Build Status](https://travis-ci.org/atom/archive-view.svg?branch=master)](https://travis-ci.org/atom/archive-view) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/u3qfgaod4lhriqlj/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/archive-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/archive-view.svg)](https://david-dm.org/atom/archive-view) |
| AutoComplete Atom API | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-atom-api.svg?branch=master)](https://travis-ci.org/atom/autocomplete-atom-api) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1x3uqd9ddchpe555/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-atom-api/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-atom-api.svg)](https://david-dm.org/atom/autocomplete-atom-api) |
| Atom Space Pen Views | [![macOS Build Status](https://travis-ci.org/atom/atom-space-pen-views.svg?branch=master)](https://travis-ci.org/atom/atom-space-pen-views) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5lgv47has6n8uhuv/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/atom-space-pen-views/branch/master) | [![Dependency Status](https://david-dm.org/atom/atom-space-pen-views.svg)](https://david-dm.org/atom/atom-space-pen-views) |
| AutoComplete CSS | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-css.svg?branch=master)](https://travis-ci.org/atom/autocomplete-css) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/k3e5uvpmpc5bkja9/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-css/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-css.svg)](https://david-dm.org/atom/autocomplete-css) |
| AutoComplete HTML | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-html.svg?branch=master)](https://travis-ci.org/atom/autocomplete-html) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/bsaqbg1fljpd9q1b/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-html/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-html.svg)](https://david-dm.org/atom/autocomplete-html) |
| AutoComplete+ | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-plus.svg?branch=master)](https://travis-ci.org/atom/autocomplete-plus) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/9bpokrud2apgqsq0/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-plus/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-plus.svg)](https://david-dm.org/atom/autocomplete-plus) |
| AutoComplete Snippets | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-snippets.svg)](https://travis-ci.org/atom/autocomplete-snippets) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/72kfi83l6cw90joy/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-snippets/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-snippets.svg)](https://david-dm.org/atom/autocomplete-snippets) |
| AutoFlow | [![macOS Build Status](https://travis-ci.org/atom/autoflow.svg?branch=master)](https://travis-ci.org/atom/autoflow) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/kpmsnkbooa29x907/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autoflow/branch/master) | [![Dependency Status](https://david-dm.org/atom/autoflow.svg)](https://david-dm.org/atom/autoflow) |
| AutoSave | [![macOS Build Status](https://travis-ci.org/atom/autosave.svg?branch=master)](https://travis-ci.org/atom/autosave) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/3aktr9updp722fqx/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autosave/branch/master) | [![Dependency Status](https://david-dm.org/atom/autosave.svg)](https://david-dm.org/atom/autosave) |
| Background Tips | [![macOS Build Status](https://travis-ci.org/atom/background-tips.svg?branch=master)](https://travis-ci.org/atom/background-tips) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/2utcugietl5vjc7w/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/background-tips/branch/master) | [![Dependency Status](https://david-dm.org/atom/background-tips.svg)](https://david-dm.org/atom/background-tips) |
| Bookmarks | [![macOS Build Status](https://travis-ci.org/atom/bookmarks.svg?branch=master)](https://travis-ci.org/atom/bookmarks) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/vjsf78pj4rw6ibcw/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/bookmarks/branch/master) | [![Dependency Status](https://david-dm.org/atom/bookmarks.svg)](https://david-dm.org/atom/bookmarks) |
| Bracket Matcher | [![macOS Build Status](https://travis-ci.org/atom/bracket-matcher.svg?branch=master)](https://travis-ci.org/atom/bracket-matcher) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/rrsl2h7e0od26k54/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/bracket-matcher/branch/master) | [![Dependency Status](https://david-dm.org/atom/bracket-matcher.svg)](https://david-dm.org/atom/bracket-matcher) |
| Command Palette | [![macOS Build Status](https://travis-ci.org/atom/command-palette.svg?branch=master)](https://travis-ci.org/atom/command-palette) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/jqgwetayr0enorun/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/command-palette/branch/master) | [![Dependency Status](https://david-dm.org/atom/command-palette.svg)](https://david-dm.org/atom/command-palette) |
| Deprecation Cop | [![macOS Build Status](https://travis-ci.org/atom/deprecation-cop.svg?branch=master)](https://travis-ci.org/atom/deprecation-cop) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/0s870q5fj3vwihjx/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/deprecation-cop/branch/master) | [![Dependency Status](https://david-dm.org/atom/deprecation-cop.svg)](https://david-dm.org/atom/deprecation-cop) |
| Dev Live Reload | [![macOS Build Status](https://travis-ci.org/atom/dev-live-reload.svg?branch=master)](https://travis-ci.org/atom/dev-live-reload) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/g3sd27ylba1fun1v/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/dev-live-reload/branch/master) | [![Dependency Status](https://david-dm.org/atom/dev-live-reload.svg)](https://david-dm.org/atom/dev-live-reload) |
| Encoding Selector | [![macOS Build Status](https://travis-ci.org/atom/encoding-selector.svg?branch=master)](https://travis-ci.org/atom/encoding-selector) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/e08x6k2b68wpwxxc/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/encoding-selector/branch/master) | [![Dependency Status](https://david-dm.org/atom/encoding-selector.svg)](https://david-dm.org/atom/encoding-selector) |
| Exception Reporting | [![macOS Build Status](https://travis-ci.org/atom/exception-reporting.svg?branch=master)](https://travis-ci.org/atom/exception-reporting) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/i0pla7qbpv7celg2/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/exception-reporting/branch/master) | [![Dependency Status](https://david-dm.org/atom/exception-reporting.svg)](https://david-dm.org/atom/exception-reporting) |
| Find and Replace | [![macOS Build Status](https://travis-ci.org/atom/find-and-replace.svg?branch=master)](https://travis-ci.org/atom/find-and-replace) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/6w4baiiq5mw4nxky/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/find-and-replace/branch/master) | [![Dependency Status](https://david-dm.org/atom/find-and-replace.svg)](https://david-dm.org/atom/find-and-replace) |
| Fuzzy Finder | [![macOS Build Status](https://travis-ci.org/atom/fuzzy-finder.svg?branch=master)](https://travis-ci.org/atom/fuzzy-finder) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/b4b2dg5n9r1wdqad/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/fuzzy-finder/branch/master) | [![Dependency Status](https://david-dm.org/atom/fuzzy-finder.svg)](https://david-dm.org/atom/fuzzy-finder) |
| Git Diff | [![macOS Build Status](https://travis-ci.org/atom/git-diff.svg?branch=master)](https://travis-ci.org/atom/git-diff) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/9auj52cs0vso66nv/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/git-diff/branch/master) | [![Dependency Status](https://david-dm.org/atom/git-diff.svg)](https://david-dm.org/atom/git-diff) |
| Go to Line | [![macOS Build Status](https://travis-ci.org/atom/go-to-line.svg?branch=master)](https://travis-ci.org/atom/go-to-line) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/qf0isc8ulw4wxi0b/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/go-to-line/branch/master) | [![Dependency Status](https://david-dm.org/atom/go-to-line.svg)](https://david-dm.org/atom/go-to-line) |
| Grammar Selector | [![macOS Build Status](https://travis-ci.org/atom/grammar-selector.svg?branch=master)](https://travis-ci.org/atom/grammar-selector) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/pg8qss03bfh4ngqm/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/grammar-selector/branch/master) | [![Dependency Status](https://david-dm.org/atom/grammar-selector.svg)](https://david-dm.org/atom/grammar-selector) |
| Image View | [![macOS Build Status](https://travis-ci.org/atom/image-view.svg?branch=master)](https://travis-ci.org/atom/image-view) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/notavaawrswk0g10/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/image-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/image-view.svg)](https://david-dm.org/atom/image-view) |
| Incompatible Packages | [![macOS Build Status](https://travis-ci.org/atom/incompatible-packages.svg?branch=master)](https://travis-ci.org/atom/incompatible-packages) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/neet595s038x7w70/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/incompatible-packages/branch/master) | [![Dependency Status](https://david-dm.org/atom/incompatible-packages.svg)](https://david-dm.org/atom/incompatible-packages) |
| Keybinding Resolver | [![macOS Build Status](https://travis-ci.org/atom/keybinding-resolver.svg?branch=master)](https://travis-ci.org/atom/keybinding-resolver) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/9jf31itx01hnn4nh/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/keybinding-resolver/branch/master) | [![Dependency Status](https://david-dm.org/atom/keybinding-resolver.svg)](https://david-dm.org/atom/keybinding-resolver) |
| Line Ending Selector | [![macOS Build Status](https://travis-ci.org/atom/line-ending-selector.svg?branch=master)](https://travis-ci.org/atom/line-ending-selector) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/b3743n9ojomlpn1g/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/line-ending-selector/branch/master) | [![Dependency Status](https://david-dm.org/atom/line-ending-selector.svg)](https://david-dm.org/atom/line-ending-selector) |
| Link | [![macOS Build Status](https://travis-ci.org/atom/link.svg?branch=master)](https://travis-ci.org/atom/link) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1d3cb8ktd48k9vnl/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/link/branch/master) | [![Dependency Status](https://david-dm.org/atom/link.svg)](https://david-dm.org/atom/link) |
| Markdown Preview | [![macOS Build Status](https://travis-ci.org/atom/markdown-preview.svg?branch=master)](https://travis-ci.org/atom/markdown-preview) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/bvh0evhh4v6w9b29/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/markdown-preview/branch/master) | [![Dependency Status](https://david-dm.org/atom/markdown-preview.svg)](https://david-dm.org/atom/markdown-preview) |
| Metrics | [![macOS Build Status](https://travis-ci.org/atom/metrics.svg?branch=master)](https://travis-ci.org/atom/metrics) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/b5doi205xl3iex04/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/metrics/branch/master) | [![Dependency Status](https://david-dm.org/atom/metrics.svg)](https://david-dm.org/atom/metrics) |
| Notifications | [![macOS Build Status](https://travis-ci.org/atom/notifications.svg?branch=master)](https://travis-ci.org/atom/notifications) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ps3p8tj2okw57x0e/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/notifications/branch/master) | [![Dependency Status](https://david-dm.org/atom/notifications.svg)](https://david-dm.org/atom/notifications) |
| Open on Github | [![macOS Build Status](https://travis-ci.org/atom/open-on-github.svg?branch=master)](https://travis-ci.org/atom/open-on-github) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ccl6na4qsna5wncr/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/open-on-github/branch/master) | [![Dependency Status](https://david-dm.org/atom/open-on-github.svg)](https://david-dm.org/atom/open-on-github) |
| Package Generator | [![macOS Build Status](https://travis-ci.org/atom/package-generator.svg?branch=master)](https://travis-ci.org/atom/package-generator)| [![Windows Build Status](https://ci.appveyor.com/api/projects/status/7t1i4hdmljhigp9u/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/package-generator/branch/master) | [![Dependency Status](https://david-dm.org/atom/package-generator.svg)](https://david-dm.org/atom/package-generator) |
| Settings View | [![macOS Build Status](https://travis-ci.org/atom/settings-view.svg?branch=master)](https://travis-ci.org/atom/settings-view) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/hatgxg6k2g3grafq/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/settings-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/settings-view.svg)](https://david-dm.org/atom/settings-view) |
| Snippets | [![macOS Build Status](https://travis-ci.org/atom/snippets.svg?branch=master)](https://travis-ci.org/atom/snippets) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/8hlc0onofkgbxw53/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/snippets/branch/master) | [![Dependency Status](https://david-dm.org/atom/snippets.svg)](https://david-dm.org/atom/snippets) |
| Spell Check | [![macOS Build Status](https://travis-ci.org/atom/spell-check.svg?branch=master)](https://travis-ci.org/atom/spell-check) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1620a5reqw6kdolv/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/spell-check/branch/master) | [![Dependency Status](https://david-dm.org/atom/spell-check.svg)](https://david-dm.org/atom/spell-check) |
| Status Bar | [![macOS Build Status](https://travis-ci.org/atom/status-bar.svg?branch=master)](https://travis-ci.org/atom/status-bar) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/gu8tv4h6cnpeesg2/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/status-bar/branch/master) | [![Dependency Status](https://david-dm.org/atom/status-bar.svg)](https://david-dm.org/atom/status-bar) |
| Styleguide | [![macOS Build Status](https://travis-ci.org/atom/styleguide.svg?branch=master)](https://travis-ci.org/atom/styleguide) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/88dt9jxexkpindhw/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/styleguide/branch/master) | [![Dependency Status](https://david-dm.org/atom/styleguide.svg)](https://david-dm.org/atom/styleguide) |
| Symbols View | [![macOS Build Status](https://travis-ci.org/atom/symbols-view.svg?branch=master)](https://travis-ci.org/atom/symbols-view) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/al68vtv83x49eu5d/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/symbols-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/symbols-view.svg)](https://david-dm.org/atom/symbols-view) |
| Tabs | [![macOS Build Status](https://travis-ci.org/atom/tabs.svg?branch=master)](https://travis-ci.org/atom/tabs) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/nf4hdmuk4i9xkfmb/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/tabs/branch/master) | [![Dependency Status](https://david-dm.org/atom/tabs.svg)](https://david-dm.org/atom/tabs) |
| Timecop | [![macOS Build Status](https://travis-ci.org/atom/timecop.svg?branch=master)](https://travis-ci.org/atom/timecop) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/37fhichmvx90sd97/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/timecop/branch/master) | [![Dependency Status](https://david-dm.org/atom/timecop.svg)](https://david-dm.org/atom/timecop) |
| Tree View | [![macOS Build Status](https://travis-ci.org/atom/tree-view.svg?branch=master)](https://travis-ci.org/atom/tree-view) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/com793ehi0hajrkd/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/tree-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/tree-view.svg)](https://david-dm.org/atom/tree-view) |
| Update Package Dependencies | [![macOS Build Status](https://travis-ci.org/atom/update-package-dependencies.svg?branch=master)](https://travis-ci.org/atom/update-package-dependencies) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5xqtoc3xk1e7lt2y/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/update-package-dependencies/branch/master) | [![Dependency Status](https://david-dm.org/atom/update-package-dependencies.svg)](https://david-dm.org/atom/update-package-dependencies) |
| Welcome | [![macOS Build Status](https://travis-ci.org/atom/welcome.svg?branch=master)](https://travis-ci.org/atom/welcome) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/c3ssyte35ivvnt62/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/welcome/branch/master) | [![Dependency Status](https://david-dm.org/atom/welcome.svg)](https://david-dm.org/atom/welcome) |
| Whitespace | [![macOS Build Status](https://travis-ci.org/atom/whitespace.svg?branch=master)](https://travis-ci.org/atom/whitespace) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/sf8pdb3ausdk1vtb/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/whitespace/branch/master) | [![Dependency Status](https://david-dm.org/atom/whitespace.svg)](https://david-dm.org/atom/whitespace) |
| Wrap Guide | [![macOS Build Status](https://travis-ci.org/atom/wrap-guide.svg?branch=master)](https://travis-ci.org/atom/wrap-guide) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5qk1io3uar5j8hol/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/wrap-guide/branch/master) | [![Dependency Status](https://david-dm.org/atom/wrap-guide.svg)](https://david-dm.org/atom/wrap-guide) |
| Package | Travis | AppVeyor/Win | Dependencies |
|---------|--------|--------------|--------------|
| [About](https://github.com/atom/about) | [![macOS Build Status](https://travis-ci.org/atom/about.svg?branch=master)](https://travis-ci.org/atom/about) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/msprea3vq47l8oce/branch/master?svg=true)](https://ci.appveyor.com/project/atom/about/branch/master) | [![Dependency Status](https://david-dm.org/atom/about.svg)](https://david-dm.org/atom/about) |
| [Archive View](https://github.com/atom/archive-view) | [![macOS Build Status](https://travis-ci.org/atom/archive-view.svg?branch=master)](https://travis-ci.org/atom/archive-view) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/u3qfgaod4lhriqlj/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/archive-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/archive-view.svg)](https://david-dm.org/atom/archive-view) |
| [AutoComplete Atom API](https://github.com/atom/autocomplete-atom-api) | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-atom-api.svg?branch=master)](https://travis-ci.org/atom/autocomplete-atom-api) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1x3uqd9ddchpe555/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-atom-api/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-atom-api.svg)](https://david-dm.org/atom/autocomplete-atom-api) |
| [AutoComplete CSS](https://github.com/atom/autocomplete-css) | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-css.svg?branch=master)](https://travis-ci.org/atom/autocomplete-css) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/k3e5uvpmpc5bkja9/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-css/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-css.svg)](https://david-dm.org/atom/autocomplete-css) |
| [AutoComplete HTML](https://github.com/atom/autocomplete-html) | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-html.svg?branch=master)](https://travis-ci.org/atom/autocomplete-html) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/bsaqbg1fljpd9q1b/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-html/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-html.svg)](https://david-dm.org/atom/autocomplete-html) |
| [AutoComplete+](https://github.com/atom/autocomplete-plus) | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-plus.svg?branch=master)](https://travis-ci.org/atom/autocomplete-plus) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/9bpokrud2apgqsq0/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-plus/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-plus.svg)](https://david-dm.org/atom/autocomplete-plus) |
| [AutoComplete Snippets](https://github.com/atom/autocomplete-snippets) | [![macOS Build Status](https://travis-ci.org/atom/autocomplete-snippets.svg)](https://travis-ci.org/atom/autocomplete-snippets) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/72kfi83l6cw90joy/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-snippets/branch/master) | [![Dependency Status](https://david-dm.org/atom/autocomplete-snippets.svg)](https://david-dm.org/atom/autocomplete-snippets) |
| [AutoFlow](https://github.com/atom/autoflow) | [![macOS Build Status](https://travis-ci.org/atom/autoflow.svg?branch=master)](https://travis-ci.org/atom/autoflow) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/kpmsnkbooa29x907/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autoflow/branch/master) | [![Dependency Status](https://david-dm.org/atom/autoflow.svg)](https://david-dm.org/atom/autoflow) |
| [AutoSave](https://github.com/atom/autosave) | [![macOS Build Status](https://travis-ci.org/atom/autosave.svg?branch=master)](https://travis-ci.org/atom/autosave) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/3aktr9updp722fqx/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autosave/branch/master) | [![Dependency Status](https://david-dm.org/atom/autosave.svg)](https://david-dm.org/atom/autosave) |
| [Background Tips](https://github.com/atom/background-tips) | [![macOS Build Status](https://travis-ci.org/atom/background-tips.svg?branch=master)](https://travis-ci.org/atom/background-tips) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/2utcugietl5vjc7w/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/background-tips/branch/master) | [![Dependency Status](https://david-dm.org/atom/background-tips.svg)](https://david-dm.org/atom/background-tips) |
| [Bookmarks](https://github.com/atom/bookmarks) | [![macOS Build Status](https://travis-ci.org/atom/bookmarks.svg?branch=master)](https://travis-ci.org/atom/bookmarks) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/vjsf78pj4rw6ibcw/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/bookmarks/branch/master) | [![Dependency Status](https://david-dm.org/atom/bookmarks.svg)](https://david-dm.org/atom/bookmarks) |
| [Bracket Matcher](https://github.com/atom/bracket-matcher) | [![macOS Build Status](https://travis-ci.org/atom/bracket-matcher.svg?branch=master)](https://travis-ci.org/atom/bracket-matcher) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/rrsl2h7e0od26k54/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/bracket-matcher/branch/master) | [![Dependency Status](https://david-dm.org/atom/bracket-matcher.svg)](https://david-dm.org/atom/bracket-matcher) |
| [Command Palette](https://github.com/atom/command-palette) | [![macOS Build Status](https://travis-ci.org/atom/command-palette.svg?branch=master)](https://travis-ci.org/atom/command-palette) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/jqgwetayr0enorun/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/command-palette/branch/master) | [![Dependency Status](https://david-dm.org/atom/command-palette.svg)](https://david-dm.org/atom/command-palette) |
| [Deprecation Cop](https://github.com/atom/deprecation-cop) | [![macOS Build Status](https://travis-ci.org/atom/deprecation-cop.svg?branch=master)](https://travis-ci.org/atom/deprecation-cop) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/0s870q5fj3vwihjx/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/deprecation-cop/branch/master) | [![Dependency Status](https://david-dm.org/atom/deprecation-cop.svg)](https://david-dm.org/atom/deprecation-cop) |
| [Dev Live Reload](https://github.com/atom/dev-live-reload) | [![macOS Build Status](https://travis-ci.org/atom/dev-live-reload.svg?branch=master)](https://travis-ci.org/atom/dev-live-reload) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/g3sd27ylba1fun1v/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/dev-live-reload/branch/master) | [![Dependency Status](https://david-dm.org/atom/dev-live-reload.svg)](https://david-dm.org/atom/dev-live-reload) |
| [Encoding Selector](https://github.com/atom/encoding-selector) | [![macOS Build Status](https://travis-ci.org/atom/encoding-selector.svg?branch=master)](https://travis-ci.org/atom/encoding-selector) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/e08x6k2b68wpwxxc/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/encoding-selector/branch/master) | [![Dependency Status](https://david-dm.org/atom/encoding-selector.svg)](https://david-dm.org/atom/encoding-selector) |
| [Exception Reporting](https://github.com/atom/exception-reporting) | [![macOS Build Status](https://travis-ci.org/atom/exception-reporting.svg?branch=master)](https://travis-ci.org/atom/exception-reporting) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/i0pla7qbpv7celg2/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/exception-reporting/branch/master) | [![Dependency Status](https://david-dm.org/atom/exception-reporting.svg)](https://david-dm.org/atom/exception-reporting) |
| [Find and Replace](https://github.com/atom/find-and-replace) | [![macOS Build Status](https://travis-ci.org/atom/find-and-replace.svg?branch=master)](https://travis-ci.org/atom/find-and-replace) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/6w4baiiq5mw4nxky/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/find-and-replace/branch/master) | [![Dependency Status](https://david-dm.org/atom/find-and-replace.svg)](https://david-dm.org/atom/find-and-replace) |
| [Fuzzy Finder](https://github.com/atom/fuzzy-finder) | [![macOS Build Status](https://travis-ci.org/atom/fuzzy-finder.svg?branch=master)](https://travis-ci.org/atom/fuzzy-finder) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/b4b2dg5n9r1wdqad/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/fuzzy-finder/branch/master) | [![Dependency Status](https://david-dm.org/atom/fuzzy-finder.svg)](https://david-dm.org/atom/fuzzy-finder) |
| [Git Diff](https://github.com/atom/git-diff) | [![macOS Build Status](https://travis-ci.org/atom/git-diff.svg?branch=master)](https://travis-ci.org/atom/git-diff) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/9auj52cs0vso66nv/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/git-diff/branch/master) | [![Dependency Status](https://david-dm.org/atom/git-diff.svg)](https://david-dm.org/atom/git-diff) |
| [Go to Line](https://github.com/atom/go-to-line) | [![macOS Build Status](https://travis-ci.org/atom/go-to-line.svg?branch=master)](https://travis-ci.org/atom/go-to-line) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/qf0isc8ulw4wxi0b/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/go-to-line/branch/master) | [![Dependency Status](https://david-dm.org/atom/go-to-line.svg)](https://david-dm.org/atom/go-to-line) |
| [Grammar Selector](https://github.com/atom/grammar-selector) | [![macOS Build Status](https://travis-ci.org/atom/grammar-selector.svg?branch=master)](https://travis-ci.org/atom/grammar-selector) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/pg8qss03bfh4ngqm/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/grammar-selector/branch/master) | [![Dependency Status](https://david-dm.org/atom/grammar-selector.svg)](https://david-dm.org/atom/grammar-selector) |
| [Image View](https://github.com/atom/image-view) | [![macOS Build Status](https://travis-ci.org/atom/image-view.svg?branch=master)](https://travis-ci.org/atom/image-view) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/notavaawrswk0g10/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/image-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/image-view.svg)](https://david-dm.org/atom/image-view) |
| [Incompatible Packages](https://github.com/atom/incompatible-packages) | [![macOS Build Status](https://travis-ci.org/atom/incompatible-packages.svg?branch=master)](https://travis-ci.org/atom/incompatible-packages) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/neet595s038x7w70/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/incompatible-packages/branch/master) | [![Dependency Status](https://david-dm.org/atom/incompatible-packages.svg)](https://david-dm.org/atom/incompatible-packages) |
| [Keybinding Resolver](https://github.com/atom/keybinding-resolver) | [![macOS Build Status](https://travis-ci.org/atom/keybinding-resolver.svg?branch=master)](https://travis-ci.org/atom/keybinding-resolver) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/9jf31itx01hnn4nh/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/keybinding-resolver/branch/master) | [![Dependency Status](https://david-dm.org/atom/keybinding-resolver.svg)](https://david-dm.org/atom/keybinding-resolver) |
| [Line Ending Selector](https://github.com/atom/line-ending-selector) | [![macOS Build Status](https://travis-ci.org/atom/line-ending-selector.svg?branch=master)](https://travis-ci.org/atom/line-ending-selector) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/b3743n9ojomlpn1g/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/line-ending-selector/branch/master) | [![Dependency Status](https://david-dm.org/atom/line-ending-selector.svg)](https://david-dm.org/atom/line-ending-selector) |
| [Link](https://github.com/atom/link) | [![macOS Build Status](https://travis-ci.org/atom/link.svg?branch=master)](https://travis-ci.org/atom/link) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1d3cb8ktd48k9vnl/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/link/branch/master) | [![Dependency Status](https://david-dm.org/atom/link.svg)](https://david-dm.org/atom/link) |
| [Markdown Preview](https://github.com/atom/markdown-preview) | [![macOS Build Status](https://travis-ci.org/atom/markdown-preview.svg?branch=master)](https://travis-ci.org/atom/markdown-preview) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/bvh0evhh4v6w9b29/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/markdown-preview/branch/master) | [![Dependency Status](https://david-dm.org/atom/markdown-preview.svg)](https://david-dm.org/atom/markdown-preview) |
| [Metrics](https://github.com/atom/metrics) | [![macOS Build Status](https://travis-ci.org/atom/metrics.svg?branch=master)](https://travis-ci.org/atom/metrics) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/b5doi205xl3iex04/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/metrics/branch/master) | [![Dependency Status](https://david-dm.org/atom/metrics.svg)](https://david-dm.org/atom/metrics) |
| [Notifications](https://github.com/atom/notifications) | [![macOS Build Status](https://travis-ci.org/atom/notifications.svg?branch=master)](https://travis-ci.org/atom/notifications) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ps3p8tj2okw57x0e/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/notifications/branch/master) | [![Dependency Status](https://david-dm.org/atom/notifications.svg)](https://david-dm.org/atom/notifications) |
| [Open on Github](https://github.com/atom/open-on-github) | [![macOS Build Status](https://travis-ci.org/atom/open-on-github.svg?branch=master)](https://travis-ci.org/atom/open-on-github) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ccl6na4qsna5wncr/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/open-on-github/branch/master) | [![Dependency Status](https://david-dm.org/atom/open-on-github.svg)](https://david-dm.org/atom/open-on-github) |
| [Package Generator](https://github.com/atom/package-generator) | [![macOS Build Status](https://travis-ci.org/atom/package-generator.svg?branch=master)](https://travis-ci.org/atom/package-generator)| [![Windows Build Status](https://ci.appveyor.com/api/projects/status/7t1i4hdmljhigp9u/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/package-generator/branch/master) | [![Dependency Status](https://david-dm.org/atom/package-generator.svg)](https://david-dm.org/atom/package-generator) |
| [Settings View](https://github.com/atom/settings-view) | [![macOS Build Status](https://travis-ci.org/atom/settings-view.svg?branch=master)](https://travis-ci.org/atom/settings-view) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/hatgxg6k2g3grafq/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/settings-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/settings-view.svg)](https://david-dm.org/atom/settings-view) |
| [Snippets](https://github.com/atom/snippets) | [![macOS Build Status](https://travis-ci.org/atom/snippets.svg?branch=master)](https://travis-ci.org/atom/snippets) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/8hlc0onofkgbxw53/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/snippets/branch/master) | [![Dependency Status](https://david-dm.org/atom/snippets.svg)](https://david-dm.org/atom/snippets) |
| [Spell Check](https://github.com/atom/spell-check) | [![macOS Build Status](https://travis-ci.org/atom/spell-check.svg?branch=master)](https://travis-ci.org/atom/spell-check) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1620a5reqw6kdolv/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/spell-check/branch/master) | [![Dependency Status](https://david-dm.org/atom/spell-check.svg)](https://david-dm.org/atom/spell-check) |
| [Status Bar](https://github.com/atom/status-bar) | [![macOS Build Status](https://travis-ci.org/atom/status-bar.svg?branch=master)](https://travis-ci.org/atom/status-bar) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/gu8tv4h6cnpeesg2/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/status-bar/branch/master) | [![Dependency Status](https://david-dm.org/atom/status-bar.svg)](https://david-dm.org/atom/status-bar) |
| [Styleguide](https://github.com/atom/styleguide) | [![macOS Build Status](https://travis-ci.org/atom/styleguide.svg?branch=master)](https://travis-ci.org/atom/styleguide) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/88dt9jxexkpindhw/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/styleguide/branch/master) | [![Dependency Status](https://david-dm.org/atom/styleguide.svg)](https://david-dm.org/atom/styleguide) |
| [Symbols View](https://github.com/atom/symbols-view) | [![macOS Build Status](https://travis-ci.org/atom/symbols-view.svg?branch=master)](https://travis-ci.org/atom/symbols-view) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/al68vtv83x49eu5d/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/symbols-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/symbols-view.svg)](https://david-dm.org/atom/symbols-view) |
| [Tabs](https://github.com/atom/tabs) | [![macOS Build Status](https://travis-ci.org/atom/tabs.svg?branch=master)](https://travis-ci.org/atom/tabs) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/nf4hdmuk4i9xkfmb/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/tabs/branch/master) | [![Dependency Status](https://david-dm.org/atom/tabs.svg)](https://david-dm.org/atom/tabs) |
| [Timecop](https://github.com/atom/timecop) | [![macOS Build Status](https://travis-ci.org/atom/timecop.svg?branch=master)](https://travis-ci.org/atom/timecop) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/37fhichmvx90sd97/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/timecop/branch/master) | [![Dependency Status](https://david-dm.org/atom/timecop.svg)](https://david-dm.org/atom/timecop) |
| [Tree View](https://github.com/atom/tree-view) | [![macOS Build Status](https://travis-ci.org/atom/tree-view.svg?branch=master)](https://travis-ci.org/atom/tree-view) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/com793ehi0hajrkd/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/tree-view/branch/master) | [![Dependency Status](https://david-dm.org/atom/tree-view.svg)](https://david-dm.org/atom/tree-view) |
| [Update Package Dependencies](https://github.com/atom/update-package-dependencies) | [![macOS Build Status](https://travis-ci.org/atom/update-package-dependencies.svg?branch=master)](https://travis-ci.org/atom/update-package-dependencies) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5xqtoc3xk1e7lt2y/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/update-package-dependencies/branch/master) | [![Dependency Status](https://david-dm.org/atom/update-package-dependencies.svg)](https://david-dm.org/atom/update-package-dependencies) |
| [Welcome](https://github.com/atom/welcome) | [![macOS Build Status](https://travis-ci.org/atom/welcome.svg?branch=master)](https://travis-ci.org/atom/welcome) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/c3ssyte35ivvnt62/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/welcome/branch/master) | [![Dependency Status](https://david-dm.org/atom/welcome.svg)](https://david-dm.org/atom/welcome) |
| [Whitespace](https://github.com/atom/whitespace) | [![macOS Build Status](https://travis-ci.org/atom/whitespace.svg?branch=master)](https://travis-ci.org/atom/whitespace) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/sf8pdb3ausdk1vtb/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/whitespace/branch/master) | [![Dependency Status](https://david-dm.org/atom/whitespace.svg)](https://david-dm.org/atom/whitespace) |
| [Wrap Guide](https://github.com/atom/wrap-guide) | [![macOS Build Status](https://travis-ci.org/atom/wrap-guide.svg?branch=master)](https://travis-ci.org/atom/wrap-guide) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5qk1io3uar5j8hol/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/wrap-guide/branch/master) | [![Dependency Status](https://david-dm.org/atom/wrap-guide.svg)](https://david-dm.org/atom/wrap-guide) |
## Libraries
| Library | macOS | Windows | Dependencies |
|---------|------|---------|--------------|
| Clear Cut | [![macOS Build Status](https://travis-ci.org/atom/clear-cut.svg?branch=master)](https://travis-ci.org/atom/clear-cut) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/civ54x89l06286m9/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/clear-cut/branch/master) | [![Dependency Status](https://david-dm.org/atom/clear-cut.svg)](https://david-dm.org/atom/clear-cut) |
| Event Kit | [![macOS Build Status](https://travis-ci.org/atom/event-kit.svg?branch=master)](https://travis-ci.org/atom/event-kit) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/lb32q70204lpmlxo/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/event-kit/branch/master) | [![Dependency Status](https://david-dm.org/atom/event-kit.svg)](https://david-dm.org/atom/event-kit) |
| Fs Plus | [![macOS Build Status](https://travis-ci.org/atom/fs-plus.svg?branch=master)](https://travis-ci.org/atom/fs-plus) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/gf2tleqp0hdek3o3/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/fs-plus/branch/master) | [![Dependency Status](https://david-dm.org/atom/fs-plus.svg)](https://david-dm.org/atom/fs-plus) |
| Grim | [![macOS Build Status](https://travis-ci.org/atom/grim.svg)](https://travis-ci.org/atom/grim) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/i4m37pol77vygrvb/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/grim/branch/master) | [![Dependency Status](https://david-dm.org/atom/grim.svg)](https://david-dm.org/atom/grim) |
| Jasmine Focused | [![macOS Build Status](https://travis-ci.org/atom/grim.svg)](https://travis-ci.org/atom/grim) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/af0ipfqqxn7aygoe/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/jasmine-focused/branch/master) | [![Dependency Status](https://david-dm.org/atom/jasmine-focused.svg)](https://david-dm.org/atom/jasmine-focused) |
| Property Accessors | [![macOS Build Status](https://travis-ci.org/atom/property-accessors.svg?branch=master)](https://travis-ci.org/atom/property-accessors) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ww4d10hi4v5h7kbp/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/property-accessors/branch/master) | [![Dependency Status](https://david-dm.org/atom/property-accessors.svg)](https://david-dm.org/atom/property-accessors) |
| TextBuffer | [![macOS Build Status](https://travis-ci.org/atom/text-buffer.svg?branch=master)](https://travis-ci.org/atom/text-buffer) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/48xl8do1sm2thf5p/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/text-buffer/branch/master) | [![Dependency Status](https://david-dm.org/atom/text-buffer.svg)](https://david-dm.org/atom/text-buffer) |
| Underscore-Plus | [![macOS Build Status](https://travis-ci.org/atom/underscore-plus.svg?branch=master)](https://travis-ci.org/atom/underscore-plus) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/c7l8009vgpaojxcd/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/underscore-plus/branch/master) | [![Dependency Status](https://david-dm.org/atom/underscore-plus.svg)](https://david-dm.org/atom/underscore-plus) |
| Library | Travis | AppVeyor/Win | Dependencies |
|---------|--------|--------------|--------------|
| [Clear Cut](https://github.com/atom/clear-cut) | [![macOS Build Status](https://travis-ci.org/atom/clear-cut.svg?branch=master)](https://travis-ci.org/atom/clear-cut) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/civ54x89l06286m9/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/clear-cut/branch/master) | [![Dependency Status](https://david-dm.org/atom/clear-cut.svg)](https://david-dm.org/atom/clear-cut) |
| [Event Kit](https://github.com/atom/event-kit) | [![macOS Build Status](https://travis-ci.org/atom/event-kit.svg?branch=master)](https://travis-ci.org/atom/event-kit) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/lb32q70204lpmlxo/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/event-kit/branch/master) | [![Dependency Status](https://david-dm.org/atom/event-kit.svg)](https://david-dm.org/atom/event-kit) |
| [First Mate](https://github.com/atom/first-mate) | [![macOS Build Status](https://travis-ci.org/atom/first-mate.svg?branch=master)](https://travis-ci.org/atom/first-mate) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/p5im21uq22cwgb6d/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/first-mate) | [![Dependency Status](https://david-dm.org/atom/first-mate/status.svg)](https://david-dm.org/atom/first-mate) |
| [Fs Plus](https://github.com/atom/fs-plus) | [![macOS Build Status](https://travis-ci.org/atom/fs-plus.svg?branch=master)](https://travis-ci.org/atom/fs-plus) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/gf2tleqp0hdek3o3/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/fs-plus/branch/master) | [![Dependency Status](https://david-dm.org/atom/fs-plus.svg)](https://david-dm.org/atom/fs-plus) |
| [Grim](https://github.com/atom/grim) | [![macOS Build Status](https://travis-ci.org/atom/grim.svg)](https://travis-ci.org/atom/grim) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/i4m37pol77vygrvb/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/grim/branch/master) | [![Dependency Status](https://david-dm.org/atom/grim.svg)](https://david-dm.org/atom/grim) |
| [Jasmine Focused](https://github.com/atom/jasmine-focused) | [![macOS Build Status](https://travis-ci.org/atom/grim.svg)](https://travis-ci.org/atom/grim) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/af0ipfqqxn7aygoe/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/jasmine-focused/branch/master) | [![Dependency Status](https://david-dm.org/atom/jasmine-focused.svg)](https://david-dm.org/atom/jasmine-focused) |
| [Keyboard Layout](https://github.com/atom/keyboard-layout) | [![macOS Build Status](https://travis-ci.org/atom/keyboard-layout.svg?branch=master)](https://travis-ci.org/atom/keyboard-layout) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/rk8wooeyh689apgd/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/keyboard-layout) | [![Dependency Status](https://david-dm.org/atom/keyboard-layout/status.svg)](https://david-dm.org/atom/keyboard-layout) |
| [Oniguruma](https://github.com/atom/node-oniguruma) | [![macOS Build Status](https://travis-ci.org/atom/node-oniguruma.svg?branch=master)](https://travis-ci.org/atom/node-oniguruma) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/s9twhi451ef2butr/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/node-oniguruma/branch/master) | [![Dependency Status](https://david-dm.org/atom/node-oniguruma.svg)](https://david-dm.org/atom/node-oniguruma) |
| [PathWatcher](https://github.com/atom/node-pathwatcher) | [![macOS Build Status](https://travis-ci.org/atom/node-pathwatcher.svg?branch=master)](https://travis-ci.org/atom/node-pathwatcher) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/li8dkoucdrc2ryts/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/node-pathwatcher) | [![Dependency Status](https://david-dm.org/atom/node-pathwatcher/status.svg)](https://david-dm.org/atom/node-pathwatcher) |
| [Property Accessors](https://github.com/atom/property-accessors) | [![macOS Build Status](https://travis-ci.org/atom/property-accessors.svg?branch=master)](https://travis-ci.org/atom/property-accessors) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ww4d10hi4v5h7kbp/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/property-accessors/branch/master) | [![Dependency Status](https://david-dm.org/atom/property-accessors.svg)](https://david-dm.org/atom/property-accessors) |
| [Season](https://github.com/atom/season) | [![macOS Build Status](https://travis-ci.org/atom/season.svg?branch=master)](https://travis-ci.org/atom/season) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/v3bth3ooq5q8k8lx/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/season) | [![Dependency Status](https://david-dm.org/atom/season.svg)](https://david-dm.org/atom/season) |
| [TextBuffer](https://github.com/atom/text-buffer) | [![macOS Build Status](https://travis-ci.org/atom/text-buffer.svg?branch=master)](https://travis-ci.org/atom/text-buffer) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/48xl8do1sm2thf5p/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/text-buffer/branch/master) | [![Dependency Status](https://david-dm.org/atom/text-buffer.svg)](https://david-dm.org/atom/text-buffer) |
| [Underscore-Plus](https://github.com/atom/underscore-plus) | [![macOS Build Status](https://travis-ci.org/atom/underscore-plus.svg?branch=master)](https://travis-ci.org/atom/underscore-plus) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/c7l8009vgpaojxcd/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/underscore-plus/branch/master) | [![Dependency Status](https://david-dm.org/atom/underscore-plus.svg)](https://david-dm.org/atom/underscore-plus) |
## Tools
| Language | macOS | Windows | Dependencies |
|----------|------|---------|--------------|
| AtomDoc | [![macOS Build Status](https://travis-ci.org/atom/atomdoc.svg?branch=master)](https://travis-ci.org/atom/atomdoc) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/chi2bmaafr3puyq2/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/atomdoc/branch/master) | [![Dependency Status](https://david-dm.org/atom/atomdoc.svg)](https://david-dm.org/atom/atomdoc)
| Language | Travis | AppVeyor/Win | Dependencies |
|----------|--------|--------------|--------------|
| [AtomDoc](https://github.com/atom/atomdoc) | [![macOS Build Status](https://travis-ci.org/atom/atomdoc.svg?branch=master)](https://travis-ci.org/atom/atomdoc) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/chi2bmaafr3puyq2/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/atomdoc/branch/master) | [![Dependency Status](https://david-dm.org/atom/atomdoc.svg)](https://david-dm.org/atom/atomdoc)
## Languages
| Language | macOS | Windows |
|----------|------|---------|
| C/C++ | [![macOS Build Status](https://travis-ci.org/atom/language-c.svg?branch=master)](https://travis-ci.org/atom/language-c) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/8oy1hmp4yrij7c32/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-c/branch/master) |
| C# | [![macOS Build Status](https://travis-ci.org/atom/language-csharp.svg?branch=master)](https://travis-ci.org/atom/language-csharp) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/j1as3753y5t90obn/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-csharp/branch/master) |
| Clojure | [![macOS Build Status](https://travis-ci.org/atom/language-clojure.svg?branch=master)](https://travis-ci.org/atom/language-clojure) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/6kd5fs48y5hixde6/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-clojure/branch/master) |
| CoffeeScript | [![macOS Build Status](https://travis-ci.org/atom/language-coffee-script.svg?branch=master)](https://travis-ci.org/atom/language-coffee-script) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/4j9aak7iwn2f2x7a/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-coffee-script/branch/master) |
| CSS | [![macOS Build Status](https://travis-ci.org/atom/language-css.svg?branch=master)](https://travis-ci.org/atom/language-css) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/v8rvm88dxp73ko2y/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-css/branch/master) |
| Git | [![macOS Build Status](https://travis-ci.org/atom/language-git.svg?branch=master)](https://travis-ci.org/atom/language-git) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/481319gyrr1feo8b/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-git/branch/master) |
| GitHub Flavored Markdown | [![macOS Build Status](https://travis-ci.org/atom/language-gfm.svg?branch=master)](https://travis-ci.org/atom/language-gfm) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rpub8qjyd8lt7wai/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-gfm/branch/master) |
| Go | [![macOS Build Status](https://travis-ci.org/atom/language-go.svg?branch=master)](https://travis-ci.org/atom/language-go) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/3fxxvv05p4hv92pn/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-go/branch/master) |
| HTML | [![macOS Build Status](https://travis-ci.org/atom/language-html.svg?branch=master)](https://travis-ci.org/atom/language-html) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/t6pk6mmdgcelfg85/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-html/branch/master) |
| Hyperlink | [![macOS Build Status](https://travis-ci.org/atom/language-hyperlink.svg?branch=master)](https://travis-ci.org/atom/language-hyperlink) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5tgvhph394r684l8/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-hyperlink/branch/master) |
| Java | [![macOS Build Status](https://travis-ci.org/atom/language-java.svg?branch=master)](https://travis-ci.org/atom/language-java) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/utoftje56n9u5x4h/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-java/branch/master) |
| JavaScript | [![macOS Build Status](https://travis-ci.org/atom/language-javascript.svg?branch=master)](https://travis-ci.org/atom/language-javascript) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ktooccwna96ssiyr/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-javascript-dijf8/branch/master) |
| JSON | [![macOS Build Status](https://travis-ci.org/atom/language-json.svg?branch=master)](https://travis-ci.org/atom/language-json) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5rx05vhdikk6c4cl/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-json/branch/master) |
| Less | [![macOS Build Status](https://travis-ci.org/atom/language-less.svg?branch=master)](https://travis-ci.org/atom/language-less) | [![Windows Build Sstatus](https://ci.appveyor.com/api/projects/status/aeina4fr4b0i7yay/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-less/branch/master) |
| Make | [![macOS Build Status](https://travis-ci.org/atom/language-make.svg?branch=master)](https://travis-ci.org/atom/language-make) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/vq1aascey21wxjh7/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-make/branch/master) |
| Mustache | [![macOS Build Status](https://travis-ci.org/atom/language-mustache.svg?branch=master)](https://travis-ci.org/atom/language-mustache) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/mbxnxaojqp0g7ldv/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-mustache/branch/master) |
| Objective-C | [![macOS Build Status](https://travis-ci.org/atom/language-objective-c.svg?branch=master)](https://travis-ci.org/atom/language-objective-c) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/27j8vfv5u95fjhkw/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-objective-c/branch/master) |
| Perl | [![macOS Build Status](https://travis-ci.org/atom/language-perl.svg?branch=master)](https://travis-ci.org/atom/language-perl) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/dfs9inkkg40hchf8/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-perl/branch/master) |
| PHP | [![macOS Build Status](https://travis-ci.org/atom/language-php.svg?branch=master)](https://travis-ci.org/atom/language-php) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/y9h45ag4b72726jy/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-php/branch/master) |
| Python | [![macOS Build Status](https://travis-ci.org/atom/language-python.svg?branch=master)](https://travis-ci.org/atom/language-python) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/hmxrb9jttjh41es9/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-python/branch/master) |
| Ruby | [![macOS Build Status](https://travis-ci.org/atom/language-ruby.svg?branch=master)](https://travis-ci.org/atom/language-ruby) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/71as182rm1adf2br/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-ruby/branch/master) |
| Ruby on Rails | [![macOS Build Status](https://travis-ci.org/atom/language-ruby-on-rails.svg?branch=master)](https://travis-ci.org/atom/language-ruby-on-rails) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5t4pa451fu5e0ghg/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-ruby-on-rails/branch/master) |
| Sass | [![macOS Build Status](https://travis-ci.org/atom/language-sass.svg?branch=master)](https://travis-ci.org/atom/language-sass) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/g7p16vainm4iuoot/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-sass/branch/master) |
| ShellScript | [![macOS Build Status](https://travis-ci.org/atom/language-shellscript.svg?branch=master)](https://travis-ci.org/atom/language-shellscript) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/p4um3lowgrg8y0ty/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-shellscript/branch/master) |
| SQL | [![macOS Build Status](https://travis-ci.org/atom/language-sql.svg?branch=master)](https://travis-ci.org/atom/language-sql) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ji31ouk5ehs4jdu1/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-sql/branch/master) |
| TODO | [![macOS Build Status](https://travis-ci.org/atom/language-todo.svg?branch=master)](https://travis-ci.org/atom/language-todo) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/gcgb9m7h146lv6qp/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-todo/branch/master) |
| TOML | [![macOS Build Status](https://travis-ci.org/atom/language-toml.svg?branch=master)](https://travis-ci.org/atom/language-toml) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/kohao3fjyk6xv0sc/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-toml/branch/master) |
| XML | [![macOS Build Status](https://travis-ci.org/atom/language-xml.svg?branch=master)](https://travis-ci.org/atom/language-xml) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/m5f6rn74a6h3q5uq/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-xml/branch/master) |
| YAML | [![macOS Build Status](https://travis-ci.org/atom/language-yaml.svg?branch=master)](https://travis-ci.org/atom/language-yaml) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/eaa4ql7kipgphc2n/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-yaml/branch/master) |
| Language | Travis | AppVeyor/Win |
|----------|--------|--------------|
| [C/C++](https://github.com/atom/language-c) | [![macOS Build Status](https://travis-ci.org/atom/language-c.svg?branch=master)](https://travis-ci.org/atom/language-c) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/8oy1hmp4yrij7c32/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-c/branch/master) |
| [C#](https://github.com/atom/language-csharp) | [![macOS Build Status](https://travis-ci.org/atom/language-csharp.svg?branch=master)](https://travis-ci.org/atom/language-csharp) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/j1as3753y5t90obn/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-csharp/branch/master) |
| [Clojure](https://github.com/atom/language-clojure) | [![macOS Build Status](https://travis-ci.org/atom/language-clojure.svg?branch=master)](https://travis-ci.org/atom/language-clojure) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/6kd5fs48y5hixde6/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-clojure/branch/master) |
| [CoffeeScript](https://github.com/atom/language-coffee-script) | [![macOS Build Status](https://travis-ci.org/atom/language-coffee-script.svg?branch=master)](https://travis-ci.org/atom/language-coffee-script) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/4j9aak7iwn2f2x7a/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-coffee-script/branch/master) |
| [CSS](https://github.com/atom/language-css) | [![macOS Build Status](https://travis-ci.org/atom/language-css.svg?branch=master)](https://travis-ci.org/atom/language-css) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/v8rvm88dxp73ko2y/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-css/branch/master) |
| [Git](https://github.com/atom/language-git) | [![macOS Build Status](https://travis-ci.org/atom/language-git.svg?branch=master)](https://travis-ci.org/atom/language-git) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/481319gyrr1feo8b/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-git/branch/master) |
| [GitHub Flavored Markdown](https://github.com/atom/language-gfm) | [![macOS Build Status](https://travis-ci.org/atom/language-gfm.svg?branch=master)](https://travis-ci.org/atom/language-gfm) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rpub8qjyd8lt7wai/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-gfm/branch/master) |
| [Go](https://github.com/atom/language-go) | [![macOS Build Status](https://travis-ci.org/atom/language-go.svg?branch=master)](https://travis-ci.org/atom/language-go) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/3fxxvv05p4hv92pn/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-go/branch/master) |
| [HTML](https://github.com/atom/language-html) | [![macOS Build Status](https://travis-ci.org/atom/language-html.svg?branch=master)](https://travis-ci.org/atom/language-html) | [![Windows Build status](https://ci.appveyor.com/api/projects/status/t6pk6mmdgcelfg85/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-html/branch/master) |
| [Hyperlink](https://github.com/atom/language-hyperlink) | [![macOS Build Status](https://travis-ci.org/atom/language-hyperlink.svg?branch=master)](https://travis-ci.org/atom/language-hyperlink) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5tgvhph394r684l8/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-hyperlink/branch/master) |
| [Java](https://github.com/atom/language-java) | [![macOS Build Status](https://travis-ci.org/atom/language-java.svg?branch=master)](https://travis-ci.org/atom/language-java) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/utoftje56n9u5x4h/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-java/branch/master) |
| [JavaScript](https://github.com/atom/language-javascript) | [![macOS Build Status](https://travis-ci.org/atom/language-javascript.svg?branch=master)](https://travis-ci.org/atom/language-javascript) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ktooccwna96ssiyr/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-javascript-dijf8/branch/master) |
| [JSON](https://github.com/atom/language-json) | [![macOS Build Status](https://travis-ci.org/atom/language-json.svg?branch=master)](https://travis-ci.org/atom/language-json) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5rx05vhdikk6c4cl/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-json/branch/master) |
| [Less](https://github.com/atom/language-less) | [![macOS Build Status](https://travis-ci.org/atom/language-less.svg?branch=master)](https://travis-ci.org/atom/language-less) | [![Windows Build Sstatus](https://ci.appveyor.com/api/projects/status/aeina4fr4b0i7yay/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-less/branch/master) |
| [Make](https://github.com/atom/language-make) | [![macOS Build Status](https://travis-ci.org/atom/language-make.svg?branch=master)](https://travis-ci.org/atom/language-make) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/vq1aascey21wxjh7/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-make/branch/master) |
| [Mustache](https://github.com/atom/language-mustache) | [![macOS Build Status](https://travis-ci.org/atom/language-mustache.svg?branch=master)](https://travis-ci.org/atom/language-mustache) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/mbxnxaojqp0g7ldv/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-mustache/branch/master) |
| [Objective-C](https://github.com/atom/language-objective-c) | [![macOS Build Status](https://travis-ci.org/atom/language-objective-c.svg?branch=master)](https://travis-ci.org/atom/language-objective-c) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/27j8vfv5u95fjhkw/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-objective-c/branch/master) |
| [Perl](https://github.com/atom/language-perl) | [![macOS Build Status](https://travis-ci.org/atom/language-perl.svg?branch=master)](https://travis-ci.org/atom/language-perl) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/dfs9inkkg40hchf8/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-perl/branch/master) |
| [PHP](https://github.com/atom/language-php) | [![macOS Build Status](https://travis-ci.org/atom/language-php.svg?branch=master)](https://travis-ci.org/atom/language-php) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/y9h45ag4b72726jy/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-php/branch/master) |
| [Python](https://github.com/atom/language-python) | [![macOS Build Status](https://travis-ci.org/atom/language-python.svg?branch=master)](https://travis-ci.org/atom/language-python) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/hmxrb9jttjh41es9/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-python/branch/master) |
| [Ruby](https://github.com/atom/language-ruby) | [![macOS Build Status](https://travis-ci.org/atom/language-ruby.svg?branch=master)](https://travis-ci.org/atom/language-ruby) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/71as182rm1adf2br/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-ruby/branch/master) |
| [Ruby on Rails](https://github.com/atom/language-ruby-on-rails) | [![macOS Build Status](https://travis-ci.org/atom/language-ruby-on-rails.svg?branch=master)](https://travis-ci.org/atom/language-ruby-on-rails) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/5t4pa451fu5e0ghg/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-ruby-on-rails/branch/master) |
| [Sass](https://github.com/atom/language-sass) | [![macOS Build Status](https://travis-ci.org/atom/language-sass.svg?branch=master)](https://travis-ci.org/atom/language-sass) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/g7p16vainm4iuoot/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-sass/branch/master) |
| [ShellScript](https://github.com/atom/language-shellscript) | [![macOS Build Status](https://travis-ci.org/atom/language-shellscript.svg?branch=master)](https://travis-ci.org/atom/language-shellscript) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/p4um3lowgrg8y0ty/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-shellscript/branch/master) |
| [SQL](https://github.com/atom/language-sql) | [![macOS Build Status](https://travis-ci.org/atom/language-sql.svg?branch=master)](https://travis-ci.org/atom/language-sql) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/ji31ouk5ehs4jdu1/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-sql/branch/master) |
| [TODO](https://github.com/atom/language-todo) | [![macOS Build Status](https://travis-ci.org/atom/language-todo.svg?branch=master)](https://travis-ci.org/atom/language-todo) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/gcgb9m7h146lv6qp/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-todo/branch/master) |
| [TOML](https://github.com/atom/language-toml) | [![macOS Build Status](https://travis-ci.org/atom/language-toml.svg?branch=master)](https://travis-ci.org/atom/language-toml) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/kohao3fjyk6xv0sc/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-toml/branch/master) |
| [XML](https://github.com/atom/language-xml) | [![macOS Build Status](https://travis-ci.org/atom/language-xml.svg?branch=master)](https://travis-ci.org/atom/language-xml) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/m5f6rn74a6h3q5uq/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-xml/branch/master) |
| [YAML](https://github/atom/language-yaml) | [![macOS Build Status](https://travis-ci.org/atom/language-yaml.svg?branch=master)](https://travis-ci.org/atom/language-yaml) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/eaa4ql7kipgphc2n/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-yaml/branch/master) |

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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!'

View File

@@ -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!'

View File

@@ -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!'

View File

@@ -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!'

View File

@@ -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' }

View File

@@ -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' }

View File

@@ -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' }

View File

@@ -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": {

View File

@@ -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 %>

View File

@@ -1,3 +1,3 @@
@echo off
"%~dp0\..\app\apm\bin\node.exe" "%~dp0\..\app\apm\lib\cli.js" %*
"%~dp0\..\app\apm\bin\apm.cmd" %*

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -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 => {

View File

@@ -866,6 +866,10 @@
"hasDeprecations": true,
"latestHasDeprecations": true
},
"language-nlf": {
"hasAlternative": true,
"alternative": "language-nsis"
},
"language-rspec": {
"version": "<=0.2.1",
"hasDeprecations": true,

View 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)}' ?`)
}
}

View File

@@ -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}"`)

View File

@@ -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
}
}

View File

@@ -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')
}
}

View File

@@ -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])
}

View File

@@ -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'],

View File

@@ -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)
}

View File

@@ -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.`)
}

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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
View 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)
})
})
})
})

View File

@@ -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"

View File

@@ -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(() => {

View File

@@ -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", ->

View File

@@ -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)

View File

@@ -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")

View File

@@ -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 ->

View File

@@ -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) ->

View File

@@ -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()

View File

@@ -1,4 +1,5 @@
module.exports = {
initialize() {},
activate () {},
deserializeMethod1 (state) {

View File

@@ -1 +1 @@
Some text.
Some textSome text.

View File

@@ -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", ->

View File

@@ -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()

View File

@@ -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 ->

View File

@@ -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)

View 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'])
})
})
})

View File

@@ -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

View File

@@ -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 ->

View 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()

View File

@@ -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]

View File

@@ -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 () {

View File

@@ -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)
}))
})

View File

@@ -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"},

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View 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')
})
})
})

View File

@@ -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()

View File

@@ -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] = []

View 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')
})
})
})
})

View File

@@ -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", ->

View File

@@ -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

View File

@@ -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')

View File

@@ -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'
])
})

View File

@@ -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'

View File

@@ -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

View File

@@ -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) ->

View File

@@ -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')

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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", ->

View File

@@ -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)

View File

@@ -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])

View File

@@ -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)

View File

@@ -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
View 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
}

View File

@@ -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})

View 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
})
}
}

View File

@@ -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
View 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
}
}

View File

@@ -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))

View File

@@ -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
}
}
},

View File

@@ -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))

View File

@@ -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)

View File

@@ -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
View 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)
}
}
})
}
}

View File

@@ -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')

View File

@@ -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.

View File

@@ -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

View File

@@ -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 ?= {}

View File

@@ -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