Merge branch 'master' into aw/watcher-up
@@ -15,7 +15,7 @@ matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
env: NODE_VERSION=6.9.4 DISPLAY=:99.0 CC=clang CXX=clang++ npm_config_clang=1
|
||||
env: NODE_VERSION=8.9.3 DISPLAY=:99.0 CC=clang CXX=clang++ npm_config_clang=1
|
||||
|
||||
sudo: required
|
||||
|
||||
|
||||
6563
apm/package-lock.json
generated
Normal file
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "1.19.0"
|
||||
"atom-package-manager": "2.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ environment:
|
||||
global:
|
||||
ATOM_DEV_RESOURCE_PATH: c:\projects\atom
|
||||
TEST_JUNIT_XML_ROOT: c:\projects\junit-test-results
|
||||
NODE_VERSION: 6.9.4
|
||||
NODE_VERSION: 8.9.3
|
||||
|
||||
matrix:
|
||||
- TASK: test
|
||||
@@ -36,7 +36,6 @@ install:
|
||||
- IF NOT EXIST %TEST_JUNIT_XML_ROOT% MKDIR %TEST_JUNIT_XML_ROOT%
|
||||
- SET PATH=C:\Program Files\Atom\resources\cli;%PATH%
|
||||
- ps: Install-Product node $env:NODE_VERSION $env:PLATFORM
|
||||
- npm install -g npm@5.3.0
|
||||
|
||||
build_script:
|
||||
- CD %APPVEYOR_BUILD_FOLDER%
|
||||
|
||||
49
atom.sh
@@ -13,6 +13,9 @@ case $(basename $0) in
|
||||
atom-beta)
|
||||
CHANNEL=beta
|
||||
;;
|
||||
atom-nightly)
|
||||
CHANNEL=nightly
|
||||
;;
|
||||
atom-dev)
|
||||
CHANNEL=dev
|
||||
;;
|
||||
@@ -59,6 +62,9 @@ if [ $REDIRECT_STDERR ]; then
|
||||
exec 2> /dev/null
|
||||
fi
|
||||
|
||||
ATOM_HOME="${ATOM_HOME:-$HOME/.atom}"
|
||||
mkdir -p "$ATOM_HOME"
|
||||
|
||||
if [ $OS == 'Mac' ]; then
|
||||
if [ -L "$0" ]; then
|
||||
SCRIPT="$(readlink "$0")"
|
||||
@@ -73,10 +79,20 @@ if [ $OS == 'Mac' ]; then
|
||||
ATOM_APP_NAME="$(basename "$ATOM_APP")"
|
||||
fi
|
||||
|
||||
if [ "$CHANNEL" == 'beta' ]; then
|
||||
ATOM_EXECUTABLE_NAME="Atom Beta"
|
||||
if [ ! -z "${ATOM_APP_NAME}" ]; then
|
||||
# If ATOM_APP_NAME is known, use it as the executable name
|
||||
ATOM_EXECUTABLE_NAME="${ATOM_APP_NAME%.*}"
|
||||
else
|
||||
ATOM_EXECUTABLE_NAME="Atom"
|
||||
# Else choose it from the inferred channel name
|
||||
if [ "$CHANNEL" == 'beta' ]; then
|
||||
ATOM_EXECUTABLE_NAME="Atom Beta"
|
||||
elif [ "$CHANNEL" == 'nightly' ]; then
|
||||
ATOM_EXECUTABLE_NAME="Atom Nightly"
|
||||
elif [ "$CHANNEL" == 'dev' ]; then
|
||||
ATOM_EXECUTABLE_NAME="Atom Dev"
|
||||
else
|
||||
ATOM_EXECUTABLE_NAME="Atom"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${ATOM_PATH}" ]; then
|
||||
@@ -111,6 +127,9 @@ elif [ $OS == 'Linux' ]; then
|
||||
beta)
|
||||
ATOM_PATH="$USR_DIRECTORY/share/atom-beta/atom"
|
||||
;;
|
||||
nightly)
|
||||
ATOM_PATH="$USR_DIRECTORY/share/atom-nightly/atom"
|
||||
;;
|
||||
dev)
|
||||
ATOM_PATH="$USR_DIRECTORY/share/atom-dev/atom"
|
||||
;;
|
||||
@@ -119,9 +138,6 @@ elif [ $OS == 'Linux' ]; then
|
||||
;;
|
||||
esac
|
||||
|
||||
ATOM_HOME="${ATOM_HOME:-$HOME/.atom}"
|
||||
mkdir -p "$ATOM_HOME"
|
||||
|
||||
: ${TMPDIR:=/tmp}
|
||||
|
||||
[ -x "$ATOM_PATH" ] || ATOM_PATH="$TMPDIR/atom-build/Atom/atom"
|
||||
@@ -146,17 +162,20 @@ on_die() {
|
||||
}
|
||||
trap 'on_die' SIGQUIT SIGTERM
|
||||
|
||||
# If the wait flag is set, don't exit this process until Atom tells it to.
|
||||
# If the wait flag is set, don't exit this process until Atom kills it.
|
||||
if [ $WAIT ]; then
|
||||
WAIT_FIFO="$ATOM_HOME/.wait_fifo"
|
||||
while true; do
|
||||
[ -f "$WAIT_FIFO" ] && rm "$WAIT_FIFO"
|
||||
[ ! -p "$WAIT_FIFO" ] && mkfifo "$WAIT_FIFO"
|
||||
read < "$WAIT_FIFO" || break
|
||||
sleep 1 # prevent a tight loop
|
||||
done
|
||||
|
||||
# fall back to sleep
|
||||
|
||||
if [ ! -p "$WAIT_FIFO" ]; then
|
||||
rm -f "$WAIT_FIFO"
|
||||
mkfifo "$WAIT_FIFO"
|
||||
fi
|
||||
|
||||
# Block endlessly by reading from a named pipe.
|
||||
exec 2>/dev/null
|
||||
read < "$WAIT_FIFO"
|
||||
|
||||
# If the read completes for some reason, fall back to sleeping in a loop.
|
||||
while true; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
@@ -17,9 +17,8 @@ general:
|
||||
dependencies:
|
||||
pre:
|
||||
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.3/install.sh | bash
|
||||
- nvm install 6.9.4
|
||||
- nvm use 6.9.4
|
||||
- npm install -g npm@5.3.0
|
||||
- nvm install 8.9.3
|
||||
- nvm use 8.9.3
|
||||
|
||||
override:
|
||||
- script/build --code-sign --compress-artifacts
|
||||
|
||||
37
docs/rfcs/000-template.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Feature title
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Summary
|
||||
|
||||
One paragraph explanation of the feature.
|
||||
|
||||
## Motivation
|
||||
|
||||
Why are we doing this? What use cases does it support? What is the expected outcome?
|
||||
|
||||
## Explanation
|
||||
|
||||
Explain the proposal as if it was already implemented and you were describing it to an Atom user. That generally means:
|
||||
|
||||
- Introducing new named concepts.
|
||||
- Explaining the feature largely in terms of examples.
|
||||
- Explaining any changes to existing workflows.
|
||||
|
||||
## Drawbacks
|
||||
|
||||
Why should we *not* do this?
|
||||
|
||||
## Rationale and alternatives
|
||||
|
||||
- Why is this approach the best in the space of possible approaches?
|
||||
- What other approaches have been considered and what is the rationale for not choosing them?
|
||||
- What is the impact of not doing this?
|
||||
|
||||
## Unresolved questions
|
||||
|
||||
- What unresolved questions do you expect to resolve through the RFC process before this gets merged?
|
||||
- What unresolved questions do you expect to resolve through the implementation of this feature before it is released in a new version of Atom?
|
||||
- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC?
|
||||
105
docs/rfcs/001-updatable-bundled-packages.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Updatable Bundled Packages
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Summary
|
||||
|
||||
This feature will enable an opt-in subset of bundled Atom packages to be updated with `apm` outside of the Atom release cycle. This will enable users to receive new functionality and bug fixes for some bundled packages as regularly as needed without waiting for them to be included in a new Atom release. This is especially important for packages like [GitHub](https://github.com/atom/github/) and [Teletype](https://github.com/atom/teletype/) which provide essential Atom functionality and could be improved independently of Atom.
|
||||
|
||||
## Motivation
|
||||
|
||||
Atom currently uses a monthly release cycle with staged Stable and Beta releases so that major issues get caught early in Beta before reaching the Stable release. Because Atom releases updates monthly, this means that a new feature merged into `master` right after a new Atom release could take one month to reach the next Beta and then another month to reach Stable.
|
||||
|
||||
Since a large part of Atom's built-in functionality is provided by bundled packages, it makes sense to allow some of those packages to be updated independently of Atom's monthly release cycle so that users can receive new features and fixes whenever they become available.
|
||||
|
||||
Bundled packages are treated differently than community packages that you can install using `apm`:
|
||||
|
||||
- You are not prompted to update them when new versions are released on `apm`
|
||||
- `apm` will warn you at the command line when you try to install or update a bundled package
|
||||
- If a user intentionally installs a bundled package from `apm` the [dalek package](https://github.com/atom/dalek/) will show a warning in the "deprecations" view asking the user to remove the offending package
|
||||
|
||||
Despite all this, if the user *does* manually install an update to a bundled package using `apm`, it will be loaded into the editor and updated dutifully as new releases occur. The only new functionality needed is to enable `apm` to check bundled packages for updates when those packages haven't yet been installed in the user's `~/.atom/packages` folder.
|
||||
|
||||
The primary use case for this improvement is enabling the GitHub package to ship improvements more frequently than Atom's release cycle since many of its improvements can be done without changes to Atom itself. If this approach is proven to work well for the GitHub package, we might also consider using it to ship Teletype as a bundled Atom package.
|
||||
|
||||
## Explanation
|
||||
|
||||
Any bundled Atom package can opt in to new updates released via `apm` by adding `"coreUpdatable": true` to its `package.json` file. This causes `apm` to consider it as part of the list of packages it checks for updates. If a community (non-bundled) package sets this field to `true` or `false` it will be ignored as it's only relevant to bundled packages.
|
||||
|
||||
Atom shows update notifications for Updatable bundled packages whenever they are available so long as those updates support the engine version of the current Atom build. Bundled package updates can also be found and installed in the Settings view's *Updates* tab.
|
||||
|
||||
The `dalek` package is aware of the new "Updatable" metadata and excludes updated bundled packages from its deprecation warnings.
|
||||
|
||||
### User Experience Examples
|
||||
|
||||
1. The user downloads and installs Atom 1.28.0 which includes GitHub package version 0.15.0. Two weeks later, GitHub package 0.16.0 is released with a few new features. The user is prompted to update to the new version and gets the new features even though Atom 1.29.0 hasn't been released yet.
|
||||
|
||||
2. The user downloads and installs Atom 1.28.0, including GitHub package 0.15.0, which was released two weeks prior. Since that release the GitHub package has been updated to version 0.15.1 on `apm`. When the user starts Atom for the first time they are prompted to update the GitHub package.
|
||||
|
||||
3. In the future, a user has an old install of Atom 1.28.0 and waits a long time between installing Atom updates. The GitHub package releases version 0.25.0 but the user is not prompted to install it because the GitHub package has set `engines` in `package.json` to restrict to Atom 1.32.0 and above.
|
||||
|
||||
### Rules for Updatable Bundled Packages
|
||||
|
||||
Any package that opts into this behavior must adhere to these rules:
|
||||
|
||||
1. **Each release must ensure that its `engines` field in `package.json` reflects the necessary Atom version for the Atom, Electron, and Node.js APIs used in the package**. This field defines the range of Atom versions in which the package is expected to work. The field should always be set to the lowest possible Atom version that the package supports.
|
||||
|
||||
2. **Any new update to a bundled package *must* support current Stable *and* Beta releases**. This enables the user to upgrade the package and continue to use it in side-by-side Stable and Beta installs on their machine. If a package wants to use API features of a newer version of Atom while still supporting older Atom versions, it must do so in a way that is aware of the user's version and adjust itself accordingly.
|
||||
|
||||
3. **Atom's `package.json` *must* stay up to date with the latest supported version of the package** in the `master` and Beta release branches. This ensures that the user always gets the latest version of the package in a new release and also benefits from its inclusion in Atom's snapshot.
|
||||
|
||||
For rule #3, it will be important to have automation to ensure that current Beta release and `master` are kept up to date with the latest compatible version of any updatable bundled package as it will be difficult for maintainers to do that manually. This could be accomplished by a nightly CI run which is focused explicitly on bumping package dependencies in this manner.
|
||||
|
||||
## Drawbacks
|
||||
|
||||
### Possible API incompatibility
|
||||
|
||||
The primary drawback of this approach is that Updatable bundled packages might exhibit problems on older Atom versions due to missing or changed APIs in Atom, Electron, or Node.js. The solution for these packages is to keep their `engines` field updated appropriately, but there's still a chance that some updates will slip through without the necessary engine version changes. If this does occur and users are affected by it, the solution is to publish a new update which rolls back the package to the functionality of its previous release and then publish another new update with the new functionality restored and the proper `engines` version in place.
|
||||
|
||||
### Increased Atom startup time
|
||||
|
||||
Another major drawback is that the snapshotted code for the bundled package will no longer be used since a newer version has been installed. This updated version of the package cannot be easily added back into Atom's snapshot so it could cause a noticable drag on Atom's startup time. Some quick measurements with Timecop show a 10x increase in GitHub package load time for bundled (snapshot) vs updated (non-snapshot) package code:
|
||||
|
||||
| GitHub Package Code | Load Time |
|
||||
|----------------------------------|-----------|
|
||||
| **Bundled** | 52 ms |
|
||||
| **Updated (first load)** | 5026 ms |
|
||||
| **Updated (subsequent loads)** | 591 ms |
|
||||
|
||||
There was no measurable effect on shell or window startup time, only package load time. It seems that the transpilation phase of the first load of the package incurs a 100x increase in load time. Pre-transpilation of the package code (either when shipped or when installed using `apm`) will be useful in mitigating this cost. Further investigation into snapshotting package code will be needed to understand if the load time increase can be mitigated.
|
||||
|
||||
There is a possibility that the GitHub package could load parts of its codebase on demand to mitigate the increased startup time when not loaded as part of Atom's snapshot. This approach is discussed in more detail at [atom/github#1522](https://github.com/atom/github/issues/1522).
|
||||
|
||||
### Incompatibility across Atom release channels
|
||||
|
||||
One other possible drawback is that an updated version of a bundled package might not be compatible across two different Atom channels. For example, if the user installs a new update to a bundled package that only supports the current Atom Beta release or higher, the user will no longer have access to that package if they open Atom Stable. However, this drawback is no different than what the user would face today installing a community package under the same circumstances, so this could be considered a general problem in the Atom package ecosystem.
|
||||
|
||||
Finally, one risk of this approach is that the Atom team forgets to update a bundled package to its latest appropriate version on `apm` just before a new release. If this happens, the user will install a new Atom update and then be prompted to update a package that should have been snapshotted and shipped in-box. To avoid this problem we could add some build automation that checks for the latest version of a bundled package to see if the current Atom build would be supported by it.
|
||||
|
||||
## Rationale and alternatives
|
||||
|
||||
This is the best approach for updating bundled packages because it allows those packages to take control of their own release cycle so long as they manage their Atom engine version correctly. It also does so in a way that allows us to decide which packages can be updated independently, reducing the likelihood of problems for users.
|
||||
|
||||
The primary alternative to this approach is to speed up the Atom release cycle so that bundled Atom package updates will reach users more frequently. This approach will be investigated independently of this RFC as it may still be valuable even with Updatable bundled packages.
|
||||
|
||||
## Unresolved questions
|
||||
|
||||
> - What unresolved questions do you expect to resolve through the RFC process before this gets merged?
|
||||
|
||||
Is it enough to just depend on the `engines` field of `package.json` to protect users from installing a package update that doesn't work with their version of Atom?
|
||||
|
||||
> - What unresolved questions do you expect to resolve through the implementation of this feature before it is released in a new version of Atom?
|
||||
|
||||
Is there any optimization we can use to reduce the performance hit of loading updated bundled packages?
|
||||
|
||||
> - What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC?
|
||||
|
||||
One issue that's out of scope for this RFC is how we ship new features and fixes to the core components of Atom (not its bundled packages) more frequently. There are two options we can investigate to accomplish this:
|
||||
|
||||
- **Ship Atom updates more frequently, possibly every two weeks**
|
||||
|
||||
- **Introduce a channel for nightly builds which surface the latest changes every day**
|
||||
|
||||
Both of these possibilities will be covered in future RFCs as they could be implemented independently of the feature described in this RFC.
|
||||
57
docs/rfcs/002-atom-nightly-releases.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Atom Nightly Releases
|
||||
|
||||
## Status
|
||||
|
||||
Implemented in PR [#17538](https://github.com/atom/atom/pull/17538)
|
||||
|
||||
## Summary
|
||||
|
||||
This RFC proposes that Atom add a third official release channel which delivers new builds of Atom nightly from the `master` branch. Nightly releases will allow new improvements to reach users long before a new Stable or Beta release is shipped. This effort will also give us the opportunity to experiment with new release automation strategies that could eventually be used to speed up the Stable and Beta release cadence.
|
||||
|
||||
## Motivation
|
||||
|
||||
Atom currently uses a monthly release cycle with staged Stable and Beta releases so that major issues get caught early in Beta before reaching the Stable release. Because Atom releases updates monthly, this means that a new feature merged into `master` right after a new Atom release could take one month to reach the next Beta and then another month to reach Stable.
|
||||
|
||||
This release process works well for delivering stable improvements to users on a regular basis but it results in friction for users who want to try out the latest Atom improvements and provide feedback. If we deliver a nightly release channel, it will be possible to deliver new features and bug fixes on a regular basis and get valuable feedback to guide our work.
|
||||
|
||||
Today, a bleeding-edge user must manually pull Atom's `master` branch and compile their own build. There is a source of `dev` builds from `master` across our CI services but those aren't made available to users as an official distribution.
|
||||
|
||||
## Explanation
|
||||
|
||||
A user who wants to use the latest improvements to Atom each day can go to atom.io, download the Atom Nightly release, and install it on their machine. This release can be installed alongside Atom Stable and Atom Beta.
|
||||
|
||||
Each night when there are new commits to Atom's `master` branch, a scheduled CI build creates a new Atom Nightly release with packages for Windows, macOS, and Linux. These packages are automatically uploaded to a new GitHub release on the `atom/atom-nightly-releases` repository using a monotonically-increasing nightly version based off of the version in `master` (e.g. `v1.29.0-nightly1`).
|
||||
|
||||
Every 4 hours, an Atom Nightly release installed on Windows or macOS checks for a new update by consulting Electron's [update.electronjs.org](update-electron) service. If a new update is available, it is downloaded in the background and the user is notified to restart Atom once it's complete. This update flow is the same as what users experience in Atom Stable or Beta releases but updates occur more frequently.
|
||||
|
||||
Linux users must manually download nightly releases for now as there isn't an easy way to automatically install new updates across the various Linux distributions. We may consider providing updatable [AppImage](http://appimage.org/) packages in the future; this will be proposed in a separate RFC.
|
||||
|
||||
## Drawbacks
|
||||
|
||||
There isn't a major downside to this effort since it would run in parallel to the existing Atom release process without affecting it.
|
||||
|
||||
## Rationale and alternatives
|
||||
|
||||
This is a useful approach because it allows us to achieve a much more rapid feedback loop with highly engaged users to ensure that Atom is improving regularly. It's the best approach because it allows us to get rapid feedback without sacrificing the stability of the Stable and Beta releases.
|
||||
|
||||
Another option is to speed up Atom's release cadence to ship Stable and Beta every two weeks (or more regularly). This approach could shorten our feedback loop but at the expense of greater instability since new improvements would not have as much time to be polished before release.
|
||||
|
||||
The impact of not taking this approach is that we continue to have to wait 1-2 months to get feedback from users about new features or bugs in Stable and Beta releases.
|
||||
|
||||
## Unresolved questions
|
||||
|
||||
- **What should we call this release channel?**
|
||||
|
||||
Some ideas:
|
||||
|
||||
- Atom Nightly
|
||||
- Atom Reactor
|
||||
- Atom Dev - Currently the name of dev builds but it might make sense to leave that for "normal" builds from `master`
|
||||
|
||||
According to a [Twitter poll](https://twitter.com/daviwil/status/1006545552987701248) with about 1,600 responses, 50% of the voters chose "Atom Nightly". The final name will be determined before launch.
|
||||
|
||||
- **Will Electron's new autoUpdate service work for all Atom releases?**
|
||||
|
||||
One outcome of this effort is to use the new [update.electronjs.org](update-electron) service for Atom's update checks so that we can deprecate on our own custom update service. Building the Nightly channel on this service will allow us to evaluate it to see if it meets the needs of the Stable and Beta channels.
|
||||
|
||||
[update-electron]: https://github.com/electron/update.electronjs.org
|
||||
345
docs/rfcs/003-consolidate-core-packages.md
Normal file
@@ -0,0 +1,345 @@
|
||||
# Consolidate Core Atom Packages
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Summary
|
||||
|
||||
Atom's official distribution is comprised of 92 core packages which provide its built-in functionality. These packages currently live in their own independent repositories in the Atom organization, all with their own separate issues, PRs, releases, and CI configurations. This RFC proposes that by consolidating most, if not all, of these core packages back into the `atom/atom` repo, we will see the following benefits:
|
||||
|
||||
- Less confusion for new contributors
|
||||
- Simpler core package contribution experience
|
||||
- Greatly reduced burden for maintainers
|
||||
|
||||
## Motivation
|
||||
|
||||
Let's cover each of the bullet points mentioned above:
|
||||
|
||||
### Less confusion for contributors
|
||||
|
||||
Imagine that a new contributor wants to add a small new feature to the `tree-view` package. The first place they are likely to look is the `atom/atom` repository. Scanning through the folders will lead to a dead end as nothing that looks like `tree-view` code can be found. They might take one of the following steps next:
|
||||
|
||||
- By reading README.md, maybe they will decide to click the link to the Atom Flight Manual and _maybe_ find the [Contributing to Official Atom Packages](https://flight-manual.atom.io/hacking-atom/sections/contributing-to-official-atom-packages/) page there
|
||||
- They could read the CONTRIBUTING.md file which [has a section](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages) that explains where to find the repos for core packages and how to contribute, but we don't really have a clear pointer to that in our README.md
|
||||
- If they don't happen to find that page, they might use Google to search for "atom tree view" and find the atom/tree-view repo and _maybe_ read the CONTRIBUTING.md file which sends them to Atom's overall contribution documentation
|
||||
- They might go to the Atom Forum or Slack community to ask how to contribute to a particular part of Atom and *hopefully* get a helpful response that points them in the right direction
|
||||
|
||||
Having all of the core Atom packages represented in a top-level `packages` folder, even if they all don't actually live in the repo, will go a long way to making the core package code more discoverable.
|
||||
|
||||
### Simpler core package contribution experience
|
||||
|
||||
Separating core Atom features out into individual repositories and delivering them to Atom builds via `apm` is a great idea in theory because it validates the Atom package ecosystem and gives developers many examples of how to develop an Atom package. It also gives Atom developers real-world experience working with Atom's APIs so that we ensure community package authors have the same hackability that Atom developers enjoy.
|
||||
|
||||
On the other hand, having these packages live in separate repositories and released "independently" introduces a great deal of overhead when adding new features. Here is a comparison of the current package development workflow contrasted to what we could achieve with consolidated packages:
|
||||
|
||||
#### Current Package Development Workflow
|
||||
|
||||
For example, to add a single feature to the `tree-view` package, one must:
|
||||
|
||||
1. Fork and clone the `tree-view` repository to their computer (making sure to pull the commit relevant to the version of Atom they are working with)
|
||||
1. Run `apm install` and `apm link` inside of the repo folder
|
||||
1. Make their desired changes to the code
|
||||
1. Open a PR to the `tree-view` repo and wait for CI to pass and a maintainer to review it
|
||||
1. Work with maintainers to get the PR approved and merged
|
||||
|
||||
After this is finished, an Atom maintainer must take the following steps:
|
||||
|
||||
1. Clone the `tree-view` repo
|
||||
2. Run `apm publish` to publish a new release of the package
|
||||
3. Edit `package.json` in the Atom repo to reflect the new version of `tree-view`
|
||||
4. Commit and push the changes to the relevant branch where the change belongs (`master` or `1.nn-releases`)
|
||||
|
||||
#### Simplified Package Development
|
||||
|
||||
If we were to move `tree-view` (or any other core Atom package) back into `atom/atom`, the development workflow would look more like this:
|
||||
|
||||
1. Fork and clone `atom/atom` and switch to a release branch if necessary
|
||||
1. Build Atom and launch it in dev mode
|
||||
1. Make desired changes to the code in `packages/tree-view`
|
||||
1. Open a PR on `atom/atom` and wait for CI to pass and a maintainer to review it
|
||||
1. Work with maintainers to get the PR approved and merged
|
||||
|
||||
At this point, the change is merged into Atom and ready for inclusion in the next release.
|
||||
|
||||
### Greatly reduced burden for maintainers
|
||||
|
||||
Since packages all have their own repositories, this means that we have to watch 91 different repos for issues and pull requests. This also means that we have to redirect issues filed on `atom/atom` to the appropriate repository when a user doesn't know where it belongs. Even more importantly, there's not an easy way to prioritize and track issues across the Atom organization without using GitHub Projects.
|
||||
|
||||
Also, as mentioned above, there's the added duty of doing the package "version dance" when we merge any new PRs to a package repository: publish the package update, update `package.json` in Atom. It's very easy to forget to do this and not have community contributions included in the next Atom release!
|
||||
|
||||
The more core packages live in `atom/atom`, the less work Atom maintainers have to do overall.
|
||||
|
||||
## Explanation
|
||||
|
||||
Many of Atom's core packages now live in the core `atom/atom` repository. To the Atom user, this change will be imperceptible as these packages still show up in the list of Core Packages in the Settings View. Users can still optionally disable these packages.
|
||||
|
||||
For maintainers and contributors, there will be less juggling of repositories and no more publishing of updates to these packages with `apm`:
|
||||
|
||||
Contributors now clone and build `atom/atom` to work on improvements to core packages. They will no longer have to use `apm link` in dev mode to test changes they make to packages in the repo's `packages` folder. Core packages that aren't consolidated still have folders under `packages` with README.md files that point to the home repository for that package.
|
||||
|
||||
When a contributor sends a PR to `atom/atom` that only affects files in a folder under `packages`, only the specs for the relevant package folders will be executed using Atom's CI scripts. This means that a full Atom build will not be required when no Atom Core code is changed in a PR. Package specs are also now run against all 3 OSes on Atom `master` and release builds.
|
||||
|
||||
Atom maintainers no longer have to publish new versions to consolidated core packages and then edit `package.json` to bump the package version in a particular Atom release branch (Stable, Beta, or `master`). When a PR against a consolidated core package in `atom/atom` is merged, no version number change is required and the changes will immediately be a part of the next release from that branch.
|
||||
|
||||
## Drawbacks
|
||||
|
||||
One possible drawback of this approach is that there might be some initial confusion where core Atom packages live, especially if some are consolidated into `atom/atom` and others still live in their own repositories. We will manage this confusion by doing the following:
|
||||
|
||||
- Include a `README.md` file in the `packages` folder which lists core packages that are not consolidated in the Atom repo. This will enable users to find the home repositories of those packages.
|
||||
|
||||
- Archive the repositories for consolidated core packages, but only after migrating existing issues, merging or closing existing PRs, and updating the README.md to point to the new home of the package code.
|
||||
|
||||
Also, contributors will now have to fork, clone, and build `atom/atom` to contribute to core packages where they would previously just need to clone the package repository. This might put added burden on them such as installing necessary build dependencies on their machine that they wouldn't otherwise need. It is very likely we could simplify this process for them, though.
|
||||
|
||||
One final drawback is that it will now be harder to have single-package maintainers. We currently have 7 core packages where there is a maintainer who isn't a part of the core Atom maintainers team. These maintainers generally are able to merge community PRs and make commits to those packages with their own judgement. If we get rid of individual package repositories, do we now make those maintainers full Atom maintainers?
|
||||
|
||||
## Rationale and alternatives
|
||||
|
||||
The Motivation section explains most of the rationale, so this section will focus on the process of consolidating packages back into `atom/atom`. The set of packages we've chosen to consolidate were evaluated based on a few factors:
|
||||
|
||||
- Number of open issues and PRs (exclude any with > 10 open PRs)
|
||||
- Time since last update (longer duration since last update is prioritized)
|
||||
- Number of package-only maintainers on the repo (exclude any with package maintainers for now)
|
||||
|
||||
Using this criteria, all 91 packages have been evaluated and categorized to determine whether they are good candidates for consolidation:
|
||||
|
||||
#### Initial Consolidation Candidates
|
||||
|
||||
| Package | Open Issues | Open PRs | Outside Maintainers | Last Updated |
|
||||
|---------|-------------|----------|---------------------| -------------|
|
||||
| **[about]** | 2 | 0 | 0 | 7/11/18 |
|
||||
| **[archive-view]** | 10 | 0 | 0 | 6/3/18 |
|
||||
| **[atom-dark-syntax]** | 5 | 0 | 0 | 12/6/17 |
|
||||
| **[atom-dark-ui]** | 1 | 2 | 0 | 2/13/18 |
|
||||
| **[atom-light-syntax]** | 1 | 0 | 0 | 10/17/16 |
|
||||
| **[atom-light-ui]** | 1 | 0 | 0 | 2/13/18 |
|
||||
| **[autoflow]** | 17 | 4 | 0 | 4/17/18 |
|
||||
| **[autosave]** | 13 | 0 | 0 | 9/16/17 |
|
||||
| **[background-tips]** | 3 | 2 | 0 | 2/17/18 |
|
||||
| **[base16-tomorrow-dark-theme]** | 5 | 0 | 0 | 1/10/17 |
|
||||
| **[base16-tomorrow-light-theme]** | 1 | 0 | 0 | 1/10/17 |
|
||||
| **[bookmarks]** | 19 | 4 | 0 | 12/10/17 |
|
||||
| **[bracket-matcher]** | 74 | 8 | 0 | 3/20/18 |
|
||||
| **[command-palette]** | 18 | 6 | 0 | 2/27/18 |
|
||||
| **[dalek]** | 2 | 0 | 0 | 2/28/18 |
|
||||
| **[deprecation-cop]** | 5 | 0 | 0 | 9/7/17 |
|
||||
| **[dev-live-reload]** | 4 | 0 | 0 | 11/14/17 |
|
||||
| **[encoding-selector]** | 11 | 2 | 0 | 4/19/18 |
|
||||
| **[exception-reporting]** | 5 | 0 | 0 | 2/6/18 |
|
||||
| **[git-diff]** | 38 | 1 | 0 | 1/18/18 |
|
||||
| **[go-to-line]** | 5 | 2 | 0 | 1/25/18 |
|
||||
| **[grammar-selector]** | 3 | 1 | 0 | 4/12/18 |
|
||||
| **[image-view]** | 4 | 4 | 0 | 7/9/18 |
|
||||
| **[incompatible-packages]** | 1 | 0 | 0 | 4/25/17 |
|
||||
| **[keybinding-resolver]** | 11 | 3 | 0 | 7/6/18 |
|
||||
| **[language-clojure]** | 13 | 3 | 0 | 1/26/18 |
|
||||
| **[language-coffee-script]** | 9 | 2 | 0 | 11/1/17 |
|
||||
| **[language-csharp]** | 1 | 1 | 0 | 4/27/18 |
|
||||
| **[language-css]** | 6 | 7 | 0 | 6/11/18 |
|
||||
| **[language-gfm]** | 52 | 9 | 0 | 6/15/18 |
|
||||
| **[language-git]** | 4 | 2 | 0 | 4/18/17 |
|
||||
| **[language-html]** | 11 | 4 | 0 | 7/5/18 |
|
||||
| **[language-hyperlink]** | 2 | 3 | 0 | 10/25/17 |
|
||||
| **[language-json]** | 1 | 0 | 0 | 5/11/18 |
|
||||
| **[language-less]** | 5 | 1 | 0 | 6/11/18 |
|
||||
| **[language-make]** | 7 | 3 | 0 | 11/26/16 |
|
||||
| **[language-mustache]** | 0 | 0 | 0 | 2/5/18 |
|
||||
| **[language-objective-c]** | 2 | 0 | 0 | 12/1/15 |
|
||||
| **[language-php]** | 25 | 7 | 0 | 6/11/18 |
|
||||
| **[language-property-list]** | 1 | 0 | 0 | 3/11/17 |
|
||||
| **[language-python]** | 33 | 4 | 0 | 6/18/18 |
|
||||
| **[language-ruby]** | 38 | 10 | 0 | 10/25/17 |
|
||||
| **[language-ruby-on-rails]** | 9 | 6 | 0 | 12/7/17 |
|
||||
| **[language-sass]** | 12 | 5 | 0 | 5/2/18 |
|
||||
| **[language-shellscript]** | 12 | 3 | 0 | 6/18/18 |
|
||||
| **[language-source]** | 0 | 0 | 0 | 1/6/15 |
|
||||
| **[language-sql]** | 6 | 4 | 0 | 1/26/18 |
|
||||
| **[language-text]** | 1 | 0 | 0 | 3/9/18 |
|
||||
| **[language-todo]** | 10 | 6 | 0 | 1/26/18 |
|
||||
| **[language-toml]** | 1 | 0 | 0 | 1/6/18 |
|
||||
| **[language-typescript]** | 6 | 0 | 0 | 6/18/18 |
|
||||
| **[language-xml]** | 2 | 1 | 0 | 6/12/17 |
|
||||
| **[language-yaml]** | 8 | 2 | 0 | 3/9/18 |
|
||||
| **[line-ending-selector]** | 10 | 0 | 0 | 5/18/18 |
|
||||
| **[link]** | 0 | 1 | 0 | 11/14/17 |
|
||||
| **[metrics]** | 1 | 2 | 0 | 7/5/18 |
|
||||
| **[notifications]** | 29 | 8 | 0 | 3/22/18 |
|
||||
| **[one-dark-syntax]** | 4 | 0 | 0 | 5/27/18 |
|
||||
| **[one-dark-ui]** | 13 | 1 | 0 | 5/1/18 |
|
||||
| **[one-light-syntax]** | 2 | 1 | 0 | 5/27/18 |
|
||||
| **[one-light-ui]** | 2 | 0 | 0 | 5/1/18 |
|
||||
| **[open-on-github]** | 8 | 3 | 0 | 11/21/17 |
|
||||
| **[package-generator]** | 10 | 2 | 0 | 11/16/17 |
|
||||
| **[status-bar]** | 25 | 3 | 0 | 11/6/17 |
|
||||
| **[styleguide]** | 12 | 2 | 0 | 4/12/18 |
|
||||
| **[tabs]** | 66 | 7 | 0 | 5/13/18 |
|
||||
| **[timecop]** | 5 | 0 | 0 | 11/4/17 |
|
||||
| **[update-package-dependencies]** | 0 | 0 | 0 | 12/10/17 |
|
||||
| **[welcome]** | 0 | 0 | 0 | 11/21/17 |
|
||||
| **[whitespace]** | 31 | 6 | 0 | 5/30/18 |
|
||||
| **[wrap-guide]** | 3 | 4 | 0 | 11/27/17 |
|
||||
|
||||
#### Packages to be Consolidated Later
|
||||
|
||||
The following packages will not be consolidated until the stated reasons can be resolved or we decide on a consolidation strategy for them:
|
||||
|
||||
| Package | Open Issues | Open PRs | Outside Maintainers | Last Updated | Reason |
|
||||
|---------|-------------|----------|---------------------|--------------|-------|
|
||||
| **[find-and-replace]** | 219 | 17 | 0 | 6/4/18 | Too many open PRs |
|
||||
| **[fuzzy-finder]** | 89 | 22 | 0 | 5/17/18 | Too many open PRs |
|
||||
| **[github]** | | | | | Independent project |
|
||||
| **[language-c]** | 53 | 15 | 0 | 7/10/18 | Too many open PRs |
|
||||
| **[language-go]** | 12 | 2 | **1** | 6/18/18 | Package maintainer, possibly inactive? |
|
||||
| **[language-java]** | 8 | 2 | **1** | 6/11/18 | Package maintainer |
|
||||
| **[language-javascript]** | 66 | 12 | 0 | 7/6/18 | Too many open PRs |
|
||||
| **[language-perl]** | 17 | 1 | **1** | 10/30/17 | Package maintainer, possibly inactive? |
|
||||
| **[markdown-preview]** | 139 | 12 | 0 | 1/8/18 | Too many open PRs |
|
||||
| **[settings-view]** | 137 | 18 | 0 | 5/17/18 | Too many open PRs |
|
||||
| **[snippets]** | 57 | 4 | **1** | 4/17/18 | Package maintainer |
|
||||
| **[solarized-dark-syntax]** | 8 | 3 | **1** | 5/27/18 | Package maintainer |
|
||||
| **[solarized-light-syntax]** | 2 | 3 | **1** | 5/27/18 | Package maintainer |
|
||||
| **[spell-check]** | 68 | 14 | **1** | 5/25/18 | Too many open PRs, package maintainer |
|
||||
| **[symbols-view]** | 86 | 13 | 0 | 12/10/17 | Too many open PRs |
|
||||
| **[tree-view]** | 210 | 36 | 0 | 3/21/18 | Too many open PRs |
|
||||
|
||||
#### Packages to Never Consolidate
|
||||
|
||||
These packages will not be consolidated because they would inhibit contributions from our friends in the Nuclide team at Facebook:
|
||||
|
||||
- **[autocomplete-atom-api]**
|
||||
- **[autocomplete-css]**
|
||||
- **[autocomplete-html]**
|
||||
- **[autocomplete-plus]**
|
||||
- **[autocomplete-snippets]**
|
||||
|
||||
### Consolidation Process
|
||||
|
||||
To consolidate a single core package repository back into `atom/atom`, the following steps will be taken:
|
||||
|
||||
1. All open pull requests on the package's repository must either be closed or merged before consolidation can proceed
|
||||
1. The package repository's code in `master` will be copied over to Atom's `packages` folder in a subfolder bearing that package's name.
|
||||
1. Atom's `package.json` file will be updated to change the package's `packageDependencies` entry to reference its local path with the following syntax: `"tree-view": "file:./packages/tree-view"`
|
||||
1. A test build will be created locally to manually verify that the package loads and works correctly at first glance
|
||||
1. The package specs for the newly-consolidated package will be run against the local Atom build
|
||||
1. A PR will be sent to `atom/atom` to verify that CI passes with the introduction of the consolidated package
|
||||
1. Once CI is clean and the PR is approved, the PR will be merged
|
||||
1. The package's original repository will have all of its existing issues moved over to `atom/atom` using a bulk issue mover tool, assigning a label to those issues relative to the package name, like `packages/tree-view`
|
||||
1. The package's original repository will have its README.md updated to point contributors to the code's new home in `atom/atom`
|
||||
1. The package's original repository will now be archived on GitHub
|
||||
|
||||
### Alternative Approaches
|
||||
|
||||
One alternative approach would be to break this core Atom functionality out of packages and put it directly in the Atom codebase without treating them as packages. This would simplify the development process even further but with the following drawbacks:
|
||||
|
||||
- The Atom team would have less regular exposure to Atom package development
|
||||
- Users would no longer be able to disable core packages to replace their behavior with other packages (different tree views, etc)
|
||||
|
||||
## Unresolved questions
|
||||
|
||||
- Is there a good reason to not move the `language-*` packages into `atom/atom`?
|
||||
|
||||
One concern here is that there exist projects which depend directly on these repositories for the TextMate syntax grammars they contain. Moving the code into `atom/atom` would require that we notify the consumers of the grammars so that they can redirect their requests to the `atom/atom` repo.
|
||||
|
||||
- Should we use `git subtree` to migrate the entire commit history of these packages over or just depend on the history from a package's original repository?
|
||||
|
||||
For now, we won't use `git subtree` due to the possibility that bringing over thousands of commits could cause unknown problems in the Atom repo. We may try this for newly consolidated packages in the future if we decide that not having the package commit history is a sufficient impediment to problem investigations.
|
||||
|
||||
- What are the criteria we might use to eventually decide to move larger packages like `tree-view`, `settings-view`, and `find-and-replace` back into `atom/atom`?
|
||||
|
||||
- Will we be losing any useful data about these packages if we don't have standalone repositories anymore?
|
||||
|
||||
- Should we use this as an opportunity to remove any unnecessary packages from the core Atom distribution?
|
||||
|
||||
[about]: https://github.com/atom/about
|
||||
[archive-view]: https://github.com/atom/archive-view
|
||||
[atom-dark-syntax]: https://github.com/atom/atom-dark-syntax
|
||||
[atom-dark-ui]: https://github.com/atom/atom-dark-ui
|
||||
[atom-light-syntax]: https://github.com/atom/atom-light-syntax
|
||||
[atom-light-ui]: https://github.com/atom/atom-light-ui
|
||||
[autocomplete-atom-api]: https://github.com/atom/autocomplete-atom-api
|
||||
[autocomplete-css]: https://github.com/atom/autocomplete-css
|
||||
[autocomplete-html]: https://github.com/atom/autocomplete-html
|
||||
[autocomplete-plus]: https://github.com/atom/autocomplete-plus
|
||||
[autocomplete-snippets]: https://github.com/atom/autocomplete-snippets
|
||||
[autoflow]: https://github.com/atom/autoflow
|
||||
[autosave]: https://github.com/atom/autosave
|
||||
[background-tips]: https://github.com/atom/background-tips
|
||||
[base16-tomorrow-dark-theme]: https://github.com/atom/base16-tomorrow-dark-theme
|
||||
[base16-tomorrow-light-theme]: https://github.com/atom/base16-tomorrow-light-theme
|
||||
[bookmarks]: https://github.com/atom/bookmarks
|
||||
[bracket-matcher]: https://github.com/atom/bracket-matcher
|
||||
[command-palette]: https://github.com/atom/command-palette
|
||||
[dalek]: https://github.com/atom/dalek
|
||||
[deprecation-cop]: https://github.com/atom/deprecation-cop
|
||||
[dev-live-reload]: https://github.com/atom/dev-live-reload
|
||||
[encoding-selector]: https://github.com/atom/encoding-selector
|
||||
[exception-reporting]: https://github.com/atom/exception-reporting
|
||||
[find-and-replace]: https://github.com/atom/find-and-replace
|
||||
[fuzzy-finder]: https://github.com/atom/fuzzy-finder
|
||||
[git-diff]: https://github.com/atom/git-diff
|
||||
[github]: https://github.com/atom/github
|
||||
[go-to-line]: https://github.com/atom/go-to-line
|
||||
[grammar-selector]: https://github.com/atom/grammar-selector
|
||||
[image-view]: https://github.com/atom/image-view
|
||||
[incompatible-packages]: https://github.com/atom/incompatible-packages
|
||||
[keybinding-resolver]: https://github.com/atom/keybinding-resolver
|
||||
[language-c]: https://github.com/atom/language-c
|
||||
[language-clojure]: https://github.com/atom/language-clojure
|
||||
[language-coffee-script]: https://github.com/atom/language-coffee-script
|
||||
[language-csharp]: https://github.com/atom/language-csharp
|
||||
[language-css]: https://github.com/atom/language-css
|
||||
[language-gfm]: https://github.com/atom/language-gfm
|
||||
[language-git]: https://github.com/atom/language-git
|
||||
[language-go]: https://github.com/atom/language-go
|
||||
[language-html]: https://github.com/atom/language-html
|
||||
[language-hyperlink]: https://github.com/atom/language-hyperlink
|
||||
[language-java]: https://github.com/atom/language-java
|
||||
[language-javascript]: https://github.com/atom/language-javascript
|
||||
[language-json]: https://github.com/atom/language-json
|
||||
[language-less]: https://github.com/atom/language-less
|
||||
[language-make]: https://github.com/atom/language-make
|
||||
[language-mustache]: https://github.com/atom/language-mustache
|
||||
[language-objective-c]: https://github.com/atom/language-objective-c
|
||||
[language-perl]: https://github.com/atom/language-perl
|
||||
[language-php]: https://github.com/atom/language-php
|
||||
[language-property-list]: https://github.com/atom/language-property-list
|
||||
[language-python]: https://github.com/atom/language-python
|
||||
[language-ruby]: https://github.com/atom/language-ruby
|
||||
[language-ruby-on-rails]: https://github.com/atom/language-ruby-on-rails
|
||||
[language-sass]: https://github.com/atom/language-sass
|
||||
[language-shellscript]: https://github.com/atom/language-shellscript
|
||||
[language-source]: https://github.com/atom/language-source
|
||||
[language-sql]: https://github.com/atom/language-sql
|
||||
[language-text]: https://github.com/atom/language-text
|
||||
[language-todo]: https://github.com/atom/language-todo
|
||||
[language-toml]: https://github.com/atom/language-toml
|
||||
[language-typescript]: https://github.com/atom/language-typescript
|
||||
[language-xml]: https://github.com/atom/language-xml
|
||||
[language-yaml]: https://github.com/atom/language-yaml
|
||||
[line-ending-selector]: https://github.com/atom/line-ending-selector
|
||||
[link]: https://github.com/atom/link
|
||||
[markdown-preview]: https://github.com/atom/markdown-preview
|
||||
[metrics]: https://github.com/atom/metrics
|
||||
[notifications]: https://github.com/atom/notifications
|
||||
[one-dark-syntax]: https://github.com/atom/one-dark-syntax
|
||||
[one-dark-ui]: https://github.com/atom/one-dark-ui
|
||||
[one-light-syntax]: https://github.com/atom/one-light-syntax
|
||||
[one-light-ui]: https://github.com/atom/one-light-ui
|
||||
[open-on-github]: https://github.com/atom/open-on-github
|
||||
[package-generator]: https://github.com/atom/package-generator
|
||||
[settings-view]: https://github.com/atom/settings-view
|
||||
[snippets]: https://github.com/atom/snippets
|
||||
[solarized-dark-syntax]: https://github.com/atom/solarized-dark-syntax
|
||||
[solarized-light-syntax]: https://github.com/atom/solarized-light-syntax
|
||||
[spell-check]: https://github.com/atom/spell-check
|
||||
[status-bar]: https://github.com/atom/status-bar
|
||||
[styleguide]: https://github.com/atom/styleguide
|
||||
[symbols-view]: https://github.com/atom/symbols-view
|
||||
[tabs]: https://github.com/atom/tabs
|
||||
[timecop]: https://github.com/atom/timecop
|
||||
[tree-view]: https://github.com/atom/tree-view
|
||||
[update-package-dependencies]: https://github.com/atom/update-package-dependencies
|
||||
[welcome]: https://github.com/atom/welcome
|
||||
[whitespace]: https://github.com/atom/whitespace
|
||||
[wrap-guide]: https://github.com/atom/wrap-guide
|
||||
@@ -79,8 +79,8 @@
|
||||
'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack'
|
||||
'cmd-=': 'window:increase-font-size'
|
||||
'cmd-+': 'window:increase-font-size'
|
||||
'cmd--': 'window:decrease-font-size'
|
||||
'cmd-_': 'window:decrease-font-size'
|
||||
'cmd--': 'window:decrease-font-size'
|
||||
'cmd-0': 'window:reset-font-size'
|
||||
|
||||
'cmd-k up': 'pane:split-up-and-copy-active-item' # Atom Specific
|
||||
|
||||
6379
package-lock.json
generated
Normal file
158
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.29.0-dev",
|
||||
"version": "1.31.0-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/main-process/main.js",
|
||||
"repository": {
|
||||
@@ -12,51 +12,128 @@
|
||||
"url": "https://github.com/atom/atom/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"electronVersion": "2.0.1",
|
||||
"electronVersion": "2.0.5",
|
||||
"dependencies": {
|
||||
"@atom/nsfw": "^1.0.18",
|
||||
"@atom/watcher": "1.0.8",
|
||||
"@atom/source-map-support": "^0.3.4",
|
||||
"@atom/watcher": "1.0.8",
|
||||
"about": "https://www.atom.io/api/packages/about/versions/1.10.0/tarball",
|
||||
"archive-view": "https://www.atom.io/api/packages/archive-view/versions/0.65.1/tarball",
|
||||
"async": "0.2.6",
|
||||
"atom-dark-syntax": "https://www.atom.io/api/packages/atom-dark-syntax/versions/0.29.0/tarball",
|
||||
"atom-dark-ui": "https://www.atom.io/api/packages/atom-dark-ui/versions/0.53.2/tarball",
|
||||
"atom-keymap": "8.2.10",
|
||||
"atom-light-syntax": "https://www.atom.io/api/packages/atom-light-syntax/versions/0.29.0/tarball",
|
||||
"atom-light-ui": "https://www.atom.io/api/packages/atom-light-ui/versions/0.46.2/tarball",
|
||||
"atom-select-list": "^0.7.0",
|
||||
"atom-ui": "0.4.1",
|
||||
"autocomplete-atom-api": "https://www.atom.io/api/packages/autocomplete-atom-api/versions/0.10.7/tarball",
|
||||
"autocomplete-css": "https://www.atom.io/api/packages/autocomplete-css/versions/0.17.5/tarball",
|
||||
"autocomplete-html": "https://www.atom.io/api/packages/autocomplete-html/versions/0.8.4/tarball",
|
||||
"autocomplete-plus": "https://www.atom.io/api/packages/autocomplete-plus/versions/2.40.6/tarball",
|
||||
"autocomplete-snippets": "https://www.atom.io/api/packages/autocomplete-snippets/versions/1.12.0/tarball",
|
||||
"autoflow": "https://www.atom.io/api/packages/autoflow/versions/0.29.4/tarball",
|
||||
"autosave": "https://www.atom.io/api/packages/autosave/versions/0.24.6/tarball",
|
||||
"babel-core": "5.8.38",
|
||||
"background-tips": "https://www.atom.io/api/packages/background-tips/versions/0.28.0/tarball",
|
||||
"base16-tomorrow-dark-theme": "https://www.atom.io/api/packages/base16-tomorrow-dark-theme/versions/1.5.0/tarball",
|
||||
"base16-tomorrow-light-theme": "https://www.atom.io/api/packages/base16-tomorrow-light-theme/versions/1.5.0/tarball",
|
||||
"bookmarks": "https://www.atom.io/api/packages/bookmarks/versions/0.45.1/tarball",
|
||||
"bracket-matcher": "https://www.atom.io/api/packages/bracket-matcher/versions/0.89.2/tarball",
|
||||
"cached-run-in-this-context": "0.4.1",
|
||||
"chai": "3.5.0",
|
||||
"chart.js": "^2.3.0",
|
||||
"clear-cut": "^2.0.2",
|
||||
"coffee-script": "1.12.7",
|
||||
"color": "^0.7.3",
|
||||
"command-palette": "https://www.atom.io/api/packages/command-palette/versions/0.43.5/tarball",
|
||||
"dalek": "https://www.atom.io/api/packages/dalek/versions/0.2.2/tarball",
|
||||
"dedent": "^0.7.0",
|
||||
"deprecation-cop": "https://www.atom.io/api/packages/deprecation-cop/versions/0.56.9/tarball",
|
||||
"dev-live-reload": "https://www.atom.io/api/packages/dev-live-reload/versions/0.48.1/tarball",
|
||||
"devtron": "1.3.0",
|
||||
"encoding-selector": "https://www.atom.io/api/packages/encoding-selector/versions/0.23.9/tarball",
|
||||
"etch": "^0.12.6",
|
||||
"event-kit": "^2.5.0",
|
||||
"exception-reporting": "https://www.atom.io/api/packages/exception-reporting/versions/0.43.1/tarball",
|
||||
"find-and-replace": "https://www.atom.io/api/packages/find-and-replace/versions/0.215.11/tarball",
|
||||
"find-parent-dir": "^0.3.0",
|
||||
"first-mate": "7.1.1",
|
||||
"focus-trap": "^2.3.0",
|
||||
"focus-trap": "2.4.5",
|
||||
"fs-admin": "^0.1.6",
|
||||
"fs-plus": "^3.0.1",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "^2.1",
|
||||
"fuzzy-finder": "https://www.atom.io/api/packages/fuzzy-finder/versions/1.8.2/tarball",
|
||||
"git-diff": "https://www.atom.io/api/packages/git-diff/versions/1.3.9/tarball",
|
||||
"git-utils": "5.4.0",
|
||||
"github": "https://www.atom.io/api/packages/github/versions/0.18.2/tarball",
|
||||
"glob": "^7.1.1",
|
||||
"go-to-line": "https://www.atom.io/api/packages/go-to-line/versions/0.33.0/tarball",
|
||||
"grammar-selector": "https://www.atom.io/api/packages/grammar-selector/versions/0.50.1/tarball",
|
||||
"grim": "1.5.0",
|
||||
"image-view": "https://www.atom.io/api/packages/image-view/versions/0.63.0/tarball",
|
||||
"incompatible-packages": "https://www.atom.io/api/packages/incompatible-packages/versions/0.27.3/tarball",
|
||||
"jasmine-json": "~0.0",
|
||||
"jasmine-reporters": "1.1.0",
|
||||
"jasmine-tagged": "^1.1.4",
|
||||
"key-path-helpers": "^0.4.0",
|
||||
"keybinding-resolver": "https://www.atom.io/api/packages/keybinding-resolver/versions/0.38.2/tarball",
|
||||
"language-c": "https://www.atom.io/api/packages/language-c/versions/0.60.0/tarball",
|
||||
"language-clojure": "https://www.atom.io/api/packages/language-clojure/versions/0.22.7/tarball",
|
||||
"language-coffee-script": "https://www.atom.io/api/packages/language-coffee-script/versions/0.49.3/tarball",
|
||||
"language-csharp": "https://www.atom.io/api/packages/language-csharp/versions/1.0.4/tarball",
|
||||
"language-css": "https://www.atom.io/api/packages/language-css/versions/0.42.11/tarball",
|
||||
"language-gfm": "https://www.atom.io/api/packages/language-gfm/versions/0.90.5/tarball",
|
||||
"language-git": "https://www.atom.io/api/packages/language-git/versions/0.19.1/tarball",
|
||||
"language-go": "https://www.atom.io/api/packages/language-go/versions/0.46.0/tarball",
|
||||
"language-html": "https://www.atom.io/api/packages/language-html/versions/0.51.1/tarball",
|
||||
"language-hyperlink": "https://www.atom.io/api/packages/language-hyperlink/versions/0.16.3/tarball",
|
||||
"language-java": "https://www.atom.io/api/packages/language-java/versions/0.30.0/tarball",
|
||||
"language-javascript": "https://www.atom.io/api/packages/language-javascript/versions/0.129.1/tarball",
|
||||
"language-json": "https://www.atom.io/api/packages/language-json/versions/0.19.2/tarball",
|
||||
"language-less": "https://www.atom.io/api/packages/language-less/versions/0.34.2/tarball",
|
||||
"language-make": "https://www.atom.io/api/packages/language-make/versions/0.22.3/tarball",
|
||||
"language-mustache": "https://www.atom.io/api/packages/language-mustache/versions/0.14.5/tarball",
|
||||
"language-objective-c": "https://www.atom.io/api/packages/language-objective-c/versions/0.15.1/tarball",
|
||||
"language-perl": "https://www.atom.io/api/packages/language-perl/versions/0.38.1/tarball",
|
||||
"language-php": "https://www.atom.io/api/packages/language-php/versions/0.44.0/tarball",
|
||||
"language-property-list": "https://www.atom.io/api/packages/language-property-list/versions/0.9.1/tarball",
|
||||
"language-python": "https://www.atom.io/api/packages/language-python/versions/0.51.0/tarball",
|
||||
"language-ruby": "https://www.atom.io/api/packages/language-ruby/versions/0.72.2/tarball",
|
||||
"language-ruby-on-rails": "https://www.atom.io/api/packages/language-ruby-on-rails/versions/0.25.3/tarball",
|
||||
"language-sass": "https://www.atom.io/api/packages/language-sass/versions/0.62.0/tarball",
|
||||
"language-shellscript": "https://www.atom.io/api/packages/language-shellscript/versions/0.27.0/tarball",
|
||||
"language-source": "https://www.atom.io/api/packages/language-source/versions/0.9.0/tarball",
|
||||
"language-sql": "https://www.atom.io/api/packages/language-sql/versions/0.25.10/tarball",
|
||||
"language-text": "https://www.atom.io/api/packages/language-text/versions/0.7.4/tarball",
|
||||
"language-todo": "https://www.atom.io/api/packages/language-todo/versions/0.29.4/tarball",
|
||||
"language-toml": "https://www.atom.io/api/packages/language-toml/versions/0.18.2/tarball",
|
||||
"language-typescript": "https://www.atom.io/api/packages/language-typescript/versions/0.4.0/tarball",
|
||||
"language-xml": "https://www.atom.io/api/packages/language-xml/versions/0.35.2/tarball",
|
||||
"language-yaml": "https://www.atom.io/api/packages/language-yaml/versions/0.32.0/tarball",
|
||||
"less-cache": "1.1.0",
|
||||
"line-ending-selector": "https://www.atom.io/api/packages/line-ending-selector/versions/0.7.7/tarball",
|
||||
"line-top-index": "0.3.1",
|
||||
"link": "https://www.atom.io/api/packages/link/versions/0.31.4/tarball",
|
||||
"markdown-preview": "https://www.atom.io/api/packages/markdown-preview/versions/0.159.20/tarball",
|
||||
"marked": "^0.3.12",
|
||||
"metrics": "https://www.atom.io/api/packages/metrics/versions/1.6.0/tarball",
|
||||
"minimatch": "^3.0.3",
|
||||
"mocha": "2.5.1",
|
||||
"mocha-junit-reporter": "^1.13.0",
|
||||
"mocha-multi-reporters": "^1.1.4",
|
||||
"mock-spawn": "^0.2.6",
|
||||
"normalize-package-data": "^2.0.0",
|
||||
"notifications": "https://www.atom.io/api/packages/notifications/versions/0.70.5/tarball",
|
||||
"nslog": "^3",
|
||||
"one-dark-syntax": "https://www.atom.io/api/packages/one-dark-syntax/versions/1.8.4/tarball",
|
||||
"one-dark-ui": "https://www.atom.io/api/packages/one-dark-ui/versions/1.12.4/tarball",
|
||||
"one-light-syntax": "https://www.atom.io/api/packages/one-light-syntax/versions/1.8.4/tarball",
|
||||
"one-light-ui": "https://www.atom.io/api/packages/one-light-ui/versions/1.12.4/tarball",
|
||||
"oniguruma": "6.2.1",
|
||||
"open-on-github": "https://www.atom.io/api/packages/open-on-github/versions/1.3.1/tarball",
|
||||
"package-generator": "https://www.atom.io/api/packages/package-generator/versions/1.3.0/tarball",
|
||||
"pathwatcher": "8.0.1",
|
||||
"postcss": "5.2.4",
|
||||
"postcss-selector-parser": "2.2.1",
|
||||
@@ -69,13 +146,28 @@
|
||||
"season": "^6.0.2",
|
||||
"semver": "^4.3.3",
|
||||
"service-hub": "^0.7.4",
|
||||
"settings-view": "https://www.atom.io/api/packages/settings-view/versions/0.255.0/tarball",
|
||||
"sinon": "1.17.4",
|
||||
"snippets": "https://www.atom.io/api/packages/snippets/versions/1.3.3/tarball",
|
||||
"solarized-dark-syntax": "https://www.atom.io/api/packages/solarized-dark-syntax/versions/1.1.5/tarball",
|
||||
"solarized-light-syntax": "https://www.atom.io/api/packages/solarized-light-syntax/versions/1.1.5/tarball",
|
||||
"spell-check": "https://www.atom.io/api/packages/spell-check/versions/0.74.0/tarball",
|
||||
"status-bar": "https://www.atom.io/api/packages/status-bar/versions/1.8.15/tarball",
|
||||
"styleguide": "https://www.atom.io/api/packages/styleguide/versions/0.49.11/tarball",
|
||||
"symbols-view": "https://www.atom.io/api/packages/symbols-view/versions/0.118.2/tarball",
|
||||
"tabs": "https://www.atom.io/api/packages/tabs/versions/0.109.2/tarball",
|
||||
"temp": "^0.8.3",
|
||||
"text-buffer": "13.14.3",
|
||||
"tree-sitter": "0.12.7",
|
||||
"text-buffer": "13.14.5",
|
||||
"timecop": "https://www.atom.io/api/packages/timecop/versions/0.36.2/tarball",
|
||||
"tree-sitter": "0.13.0",
|
||||
"tree-view": "https://www.atom.io/api/packages/tree-view/versions/0.222.0/tarball",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"underscore-plus": "^1.6.8",
|
||||
"update-package-dependencies": "https://www.atom.io/api/packages/update-package-dependencies/versions/0.13.1/tarball",
|
||||
"welcome": "https://www.atom.io/api/packages/welcome/versions/0.36.6/tarball",
|
||||
"whitespace": "https://www.atom.io/api/packages/whitespace/versions/0.37.6/tarball",
|
||||
"winreg": "^1.2.1",
|
||||
"wrap-guide": "https://www.atom.io/api/packages/wrap-guide/versions/0.40.3/tarball",
|
||||
"yargs": "^3.23.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
@@ -85,18 +177,18 @@
|
||||
"atom-light-ui": "0.46.2",
|
||||
"base16-tomorrow-dark-theme": "1.5.0",
|
||||
"base16-tomorrow-light-theme": "1.5.0",
|
||||
"one-dark-ui": "1.12.3",
|
||||
"one-light-ui": "1.12.3",
|
||||
"one-dark-syntax": "1.8.3",
|
||||
"one-light-syntax": "1.8.3",
|
||||
"one-dark-ui": "1.12.4",
|
||||
"one-light-ui": "1.12.4",
|
||||
"one-dark-syntax": "1.8.4",
|
||||
"one-light-syntax": "1.8.4",
|
||||
"solarized-dark-syntax": "1.1.5",
|
||||
"solarized-light-syntax": "1.1.5",
|
||||
"about": "1.9.1",
|
||||
"archive-view": "0.64.5",
|
||||
"about": "1.10.0",
|
||||
"archive-view": "0.65.1",
|
||||
"autocomplete-atom-api": "0.10.7",
|
||||
"autocomplete-css": "0.17.5",
|
||||
"autocomplete-html": "0.8.4",
|
||||
"autocomplete-plus": "2.40.6",
|
||||
"autocomplete-plus": "2.40.7",
|
||||
"autocomplete-snippets": "1.12.0",
|
||||
"autoflow": "0.29.4",
|
||||
"autosave": "0.24.6",
|
||||
@@ -109,25 +201,25 @@
|
||||
"dev-live-reload": "0.48.1",
|
||||
"encoding-selector": "0.23.9",
|
||||
"exception-reporting": "0.43.1",
|
||||
"find-and-replace": "0.215.10",
|
||||
"find-and-replace": "0.215.12",
|
||||
"fuzzy-finder": "1.8.2",
|
||||
"github": "0.15.2",
|
||||
"github": "0.18.2",
|
||||
"git-diff": "1.3.9",
|
||||
"go-to-line": "0.33.0",
|
||||
"grammar-selector": "0.50.1",
|
||||
"image-view": "0.62.4",
|
||||
"image-view": "0.63.0",
|
||||
"incompatible-packages": "0.27.3",
|
||||
"keybinding-resolver": "0.38.1",
|
||||
"keybinding-resolver": "0.38.2",
|
||||
"line-ending-selector": "0.7.7",
|
||||
"link": "0.31.4",
|
||||
"markdown-preview": "0.159.20",
|
||||
"metrics": "1.2.6",
|
||||
"metrics": "1.6.0",
|
||||
"notifications": "0.70.5",
|
||||
"open-on-github": "1.3.1",
|
||||
"package-generator": "1.3.0",
|
||||
"settings-view": "0.255.0",
|
||||
"snippets": "1.3.3",
|
||||
"spell-check": "0.73.5",
|
||||
"spell-check": "0.74.0",
|
||||
"status-bar": "1.8.15",
|
||||
"styleguide": "0.49.11",
|
||||
"symbols-view": "0.118.2",
|
||||
@@ -136,39 +228,39 @@
|
||||
"tree-view": "0.222.0",
|
||||
"update-package-dependencies": "0.13.1",
|
||||
"welcome": "0.36.6",
|
||||
"whitespace": "0.37.5",
|
||||
"whitespace": "0.37.6",
|
||||
"wrap-guide": "0.40.3",
|
||||
"language-c": "0.59.9",
|
||||
"language-c": "0.60.0",
|
||||
"language-clojure": "0.22.7",
|
||||
"language-coffee-script": "0.49.3",
|
||||
"language-csharp": "1.0.4",
|
||||
"language-css": "0.42.10",
|
||||
"language-gfm": "0.90.4",
|
||||
"language-css": "0.42.11",
|
||||
"language-gfm": "0.90.5",
|
||||
"language-git": "0.19.1",
|
||||
"language-go": "0.45.3",
|
||||
"language-html": "0.49.1",
|
||||
"language-go": "0.46.0",
|
||||
"language-html": "0.51.1",
|
||||
"language-hyperlink": "0.16.3",
|
||||
"language-java": "0.29.0",
|
||||
"language-javascript": "0.128.7",
|
||||
"language-java": "0.30.0",
|
||||
"language-javascript": "0.129.1",
|
||||
"language-json": "0.19.2",
|
||||
"language-less": "0.34.2",
|
||||
"language-make": "0.22.3",
|
||||
"language-mustache": "0.14.5",
|
||||
"language-objective-c": "0.15.1",
|
||||
"language-perl": "0.38.1",
|
||||
"language-php": "0.43.2",
|
||||
"language-php": "0.44.0",
|
||||
"language-property-list": "0.9.1",
|
||||
"language-python": "0.49.5",
|
||||
"language-ruby": "0.71.4",
|
||||
"language-python": "0.51.1",
|
||||
"language-ruby": "0.72.2",
|
||||
"language-ruby-on-rails": "0.25.3",
|
||||
"language-sass": "0.62.0",
|
||||
"language-shellscript": "0.26.4",
|
||||
"language-shellscript": "0.27.0",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.25.10",
|
||||
"language-text": "0.7.4",
|
||||
"language-todo": "0.29.4",
|
||||
"language-toml": "0.18.2",
|
||||
"language-typescript": "0.3.3",
|
||||
"language-typescript": "0.4.0",
|
||||
"language-xml": "0.35.2",
|
||||
"language-yaml": "0.32.0"
|
||||
},
|
||||
|
||||
BIN
resources/app-icons/nightly/atom.icns
Normal file
BIN
resources/app-icons/nightly/atom.ico
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
resources/app-icons/nightly/png/1024.png
Normal file
|
After Width: | Height: | Size: 348 KiB |
BIN
resources/app-icons/nightly/png/128.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
resources/app-icons/nightly/png/16.png
Normal file
|
After Width: | Height: | Size: 870 B |
BIN
resources/app-icons/nightly/png/24.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/app-icons/nightly/png/256.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
resources/app-icons/nightly/png/32.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
resources/app-icons/nightly/png/48.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
resources/app-icons/nightly/png/512.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
resources/app-icons/nightly/png/64.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
118
script/build
@@ -21,6 +21,7 @@ const argv = yargs
|
||||
.describe('create-debian-package', 'Create .deb package (Linux only)')
|
||||
.describe('create-rpm-package', 'Create .rpm package (Linux only)')
|
||||
.describe('compress-artifacts', 'Compress Atom binaries (and symbols on macOS)')
|
||||
.describe('generate-api-docs', 'Only build the API documentation')
|
||||
.describe('install', 'Install Atom')
|
||||
.string('install')
|
||||
.wrap(yargs.terminalWidth())
|
||||
@@ -28,7 +29,6 @@ const argv = yargs
|
||||
|
||||
const checkChromedriverVersion = require('./lib/check-chromedriver-version')
|
||||
const cleanOutputDirectory = require('./lib/clean-output-directory')
|
||||
const cleanPackageLock = require('./lib/clean-package-lock')
|
||||
const codeSignOnMac = require('./lib/code-sign-on-mac')
|
||||
const codeSignOnWindows = require('./lib/code-sign-on-windows')
|
||||
const compressArtifacts = require('./lib/compress-artifacts')
|
||||
@@ -59,7 +59,6 @@ const CONFIG = require('./config')
|
||||
let binariesPromise = Promise.resolve()
|
||||
|
||||
if (!argv.existingBinaries) {
|
||||
cleanPackageLock()
|
||||
checkChromedriverVersion()
|
||||
cleanOutputDirectory()
|
||||
copyAssets()
|
||||
@@ -72,65 +71,74 @@ if (!argv.existingBinaries) {
|
||||
prebuildLessCache()
|
||||
generateMetadata()
|
||||
generateAPIDocs()
|
||||
binariesPromise = dumpSymbols()
|
||||
if (!argv.generateApiDocs) {
|
||||
binariesPromise = dumpSymbols()
|
||||
}
|
||||
}
|
||||
|
||||
binariesPromise
|
||||
.then(packageApplication)
|
||||
.then(packagedAppPath => generateStartupSnapshot(packagedAppPath).then(() => packagedAppPath))
|
||||
.then(packagedAppPath => {
|
||||
switch (process.platform) {
|
||||
case 'darwin': {
|
||||
if (argv.codeSign) {
|
||||
codeSignOnMac(packagedAppPath)
|
||||
} else {
|
||||
console.log('Skipping code-signing. Specify the --code-sign option to perform code-signing'.gray)
|
||||
}
|
||||
}
|
||||
case 'win32': {
|
||||
if (argv.codeSign) {
|
||||
const executablesToSign = [ path.join(packagedAppPath, 'Atom.exe') ]
|
||||
if (argv.createWindowsInstaller) {
|
||||
executablesToSign.push(path.join(__dirname, 'node_modules', 'electron-winstaller', 'vendor', 'Update.exe'))
|
||||
if (!argv.generateApiDocs) {
|
||||
binariesPromise
|
||||
.then(packageApplication)
|
||||
.then(packagedAppPath => generateStartupSnapshot(packagedAppPath).then(() => packagedAppPath))
|
||||
.then(packagedAppPath => {
|
||||
switch (process.platform) {
|
||||
case 'darwin': {
|
||||
if (argv.codeSign) {
|
||||
codeSignOnMac(packagedAppPath)
|
||||
} else {
|
||||
console.log('Skipping code-signing. Specify the --code-sign option to perform code-signing'.gray)
|
||||
}
|
||||
codeSignOnWindows(executablesToSign)
|
||||
} else {
|
||||
console.log('Skipping code-signing. Specify the --code-sign option to perform code-signing'.gray)
|
||||
break
|
||||
}
|
||||
if (argv.createWindowsInstaller) {
|
||||
return createWindowsInstaller(packagedAppPath)
|
||||
.then(() => argv.codeSign && codeSignOnWindows([ path.join(CONFIG.buildOutputPath, 'AtomSetup.exe') ]))
|
||||
.then(() => packagedAppPath)
|
||||
} else {
|
||||
console.log('Skipping creating installer. Specify the --create-windows-installer option to create a Squirrel-based Windows installer.'.gray)
|
||||
case 'win32': {
|
||||
if (argv.codeSign) {
|
||||
const executablesToSign = [ path.join(packagedAppPath, 'Atom.exe') ]
|
||||
if (argv.createWindowsInstaller) {
|
||||
executablesToSign.push(path.join(__dirname, 'node_modules', 'electron-winstaller', 'vendor', 'Update.exe'))
|
||||
}
|
||||
codeSignOnWindows(executablesToSign)
|
||||
} else {
|
||||
console.log('Skipping code-signing. Specify the --code-sign option to perform code-signing'.gray)
|
||||
}
|
||||
if (argv.createWindowsInstaller) {
|
||||
return createWindowsInstaller(packagedAppPath)
|
||||
.then((installerPath) => {
|
||||
argv.codeSign && codeSignOnWindows([installerPath])
|
||||
return packagedAppPath
|
||||
})
|
||||
} else {
|
||||
console.log('Skipping creating installer. Specify the --create-windows-installer option to create a Squirrel-based Windows installer.'.gray)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'linux': {
|
||||
if (argv.createDebianPackage) {
|
||||
createDebianPackage(packagedAppPath)
|
||||
} else {
|
||||
console.log('Skipping creating debian package. Specify the --create-debian-package option to create it.'.gray)
|
||||
}
|
||||
|
||||
if (argv.createRpmPackage) {
|
||||
createRpmPackage(packagedAppPath)
|
||||
} else {
|
||||
console.log('Skipping creating rpm package. Specify the --create-rpm-package option to create it.'.gray)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
case 'linux': {
|
||||
if (argv.createDebianPackage) {
|
||||
createDebianPackage(packagedAppPath)
|
||||
} else {
|
||||
console.log('Skipping creating debian package. Specify the --create-debian-package option to create it.'.gray)
|
||||
}
|
||||
|
||||
if (argv.createRpmPackage) {
|
||||
createRpmPackage(packagedAppPath)
|
||||
} else {
|
||||
console.log('Skipping creating rpm package. Specify the --create-rpm-package option to create it.'.gray)
|
||||
}
|
||||
return Promise.resolve(packagedAppPath)
|
||||
}).then(packagedAppPath => {
|
||||
if (argv.compressArtifacts) {
|
||||
compressArtifacts(packagedAppPath)
|
||||
} else {
|
||||
console.log('Skipping artifacts compression. Specify the --compress-artifacts option to compress Atom binaries (and symbols on macOS)'.gray)
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(packagedAppPath)
|
||||
}).then(packagedAppPath => {
|
||||
if (argv.compressArtifacts) {
|
||||
compressArtifacts(packagedAppPath)
|
||||
} else {
|
||||
console.log('Skipping artifacts compression. Specify the --compress-artifacts option to compress Atom binaries (and symbols on macOS)'.gray)
|
||||
}
|
||||
|
||||
if (argv.install != null) {
|
||||
installApplication(packagedAppPath, argv.install)
|
||||
} else {
|
||||
console.log('Skipping installation. Specify the --install option to install Atom'.gray)
|
||||
}
|
||||
})
|
||||
if (argv.install != null) {
|
||||
installApplication(packagedAppPath, argv.install)
|
||||
} else {
|
||||
console.log('Skipping installation. Specify the --install option to install Atom'.gray)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const spawnSync = require('./lib/spawn-sync')
|
||||
|
||||
const repositoryRootPath = path.resolve(__dirname, '..')
|
||||
const apmRootPath = path.join(repositoryRootPath, 'apm')
|
||||
@@ -19,12 +20,16 @@ const atomHomeDirPath = process.env.ATOM_HOME || path.join(homeDirPath, '.atom')
|
||||
|
||||
const appMetadata = require(path.join(repositoryRootPath, 'package.json'))
|
||||
const apmMetadata = require(path.join(apmRootPath, 'package.json'))
|
||||
const channel = getChannel()
|
||||
const computedAppVersion = computeAppVersion(process.env.ATOM_RELEASE_VERSION || appMetadata.version)
|
||||
const channel = getChannel(computedAppVersion)
|
||||
const appName = getAppName(channel)
|
||||
|
||||
module.exports = {
|
||||
appMetadata,
|
||||
apmMetadata,
|
||||
channel,
|
||||
appName,
|
||||
computedAppVersion,
|
||||
repositoryRootPath,
|
||||
apmRootPath,
|
||||
scriptRootPath,
|
||||
@@ -40,14 +45,30 @@ module.exports = {
|
||||
snapshotAuxiliaryData: {}
|
||||
}
|
||||
|
||||
function getChannel () {
|
||||
if (appMetadata.version.match(/dev/)) {
|
||||
return 'dev'
|
||||
} else if (appMetadata.version.match(/beta/)) {
|
||||
return 'beta'
|
||||
} else {
|
||||
return 'stable'
|
||||
function getChannel (version) {
|
||||
const match = version.match(/\d+\.\d+\.\d+(-([a-z]+)(\d+|-\w{4,})?)?$/)
|
||||
if (!match) {
|
||||
throw new Error(`Found incorrectly formatted Atom version ${version}`)
|
||||
} else if (match[2]) {
|
||||
return match[2]
|
||||
}
|
||||
|
||||
return 'stable'
|
||||
}
|
||||
|
||||
function getAppName (channel) {
|
||||
return channel === 'stable'
|
||||
? 'Atom'
|
||||
: `Atom ${process.env.ATOM_CHANNEL_DISPLAY_NAME || channel.charAt(0).toUpperCase() + channel.slice(1)}`
|
||||
}
|
||||
|
||||
function computeAppVersion (version) {
|
||||
if (version.match(/-dev$/)) {
|
||||
const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {cwd: repositoryRootPath})
|
||||
const commitHash = result.stdout.toString().trim()
|
||||
version += '-' + commitHash
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
function getApmBinPath () {
|
||||
|
||||
@@ -14,6 +14,7 @@ module.exports = function () {
|
||||
path.join(CONFIG.atomHomeDirPath, '.apm'),
|
||||
path.join(CONFIG.atomHomeDirPath, '.npm'),
|
||||
path.join(CONFIG.atomHomeDirPath, 'compile-cache'),
|
||||
path.join(CONFIG.atomHomeDirPath, 'snapshot-cache'),
|
||||
path.join(CONFIG.atomHomeDirPath, 'atom-shell'),
|
||||
path.join(CONFIG.atomHomeDirPath, 'electron'),
|
||||
path.join(os.tmpdir(), 'atom-build'),
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// This module exports a function that deletes all `package-lock.json` files that do
|
||||
// not exist under a `node_modules` directory.
|
||||
|
||||
'use strict'
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const fs = require('fs-extra')
|
||||
const glob = require('glob')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = function () {
|
||||
console.log('Deleting problematic package-lock.json files')
|
||||
let paths = glob.sync(path.join(CONFIG.repositoryRootPath, '**', 'package-lock.json'), {ignore: path.join('**', 'node_modules', '**')})
|
||||
|
||||
for (let path of paths) {
|
||||
fs.unlinkSync(path)
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,36 @@ module.exports = function (packagedAppPath) {
|
||||
downloadFileFromGithub(process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL, certPath)
|
||||
}
|
||||
try {
|
||||
console.log(`Ensuring keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN} exists`)
|
||||
try {
|
||||
spawnSync('security', [
|
||||
'show-keychain-info',
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
], {stdio: 'inherit'})
|
||||
} catch (err) {
|
||||
console.log(`Creating keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`)
|
||||
// The keychain doesn't exist, try to create it
|
||||
spawnSync('security', [
|
||||
'create-keychain',
|
||||
'-p', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD,
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
], {stdio: 'inherit'})
|
||||
|
||||
// List the keychain to "activate" it. Somehow this seems
|
||||
// to be needed otherwise the signing operation fails
|
||||
spawnSync('security', [
|
||||
'list-keychains',
|
||||
'-s', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
], {stdio: 'inherit'})
|
||||
|
||||
// Make sure it doesn't time out before we use it
|
||||
spawnSync('security', [
|
||||
'set-keychain-settings',
|
||||
'-t', '3600',
|
||||
'-u', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
], {stdio: 'inherit'})
|
||||
}
|
||||
|
||||
console.log(`Unlocking keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`)
|
||||
const unlockArgs = ['unlock-keychain']
|
||||
// For signing on local workstations, password could be entered interactively
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const spawnSync = require('./spawn-sync')
|
||||
const { path7za } = require('7zip-bin')
|
||||
|
||||
const CONFIG = require('../config')
|
||||
|
||||
@@ -19,7 +20,7 @@ module.exports = function (packagedAppPath) {
|
||||
function getArchiveName () {
|
||||
switch (process.platform) {
|
||||
case 'darwin': return 'atom-mac.zip'
|
||||
case 'win32': return 'atom-windows.zip'
|
||||
case 'win32': return `atom-${process.arch === 'x64' ? 'x64-' : ''}windows.zip`
|
||||
default: return `atom-${getLinuxArchiveArch()}.tar.gz`
|
||||
}
|
||||
}
|
||||
@@ -44,7 +45,7 @@ function compress (inputDirPath, outputArchivePath) {
|
||||
compressCommand = 'zip'
|
||||
compressArguments = ['-r', '--symlinks']
|
||||
} else if (process.platform === 'win32') {
|
||||
compressCommand = '7z.exe'
|
||||
compressCommand = path7za
|
||||
compressArguments = ['a', '-r']
|
||||
} else {
|
||||
compressCommand = 'tar'
|
||||
|
||||
@@ -10,9 +10,8 @@ const CONFIG = require('../config')
|
||||
|
||||
module.exports = function (packagedAppPath) {
|
||||
console.log(`Creating Debian package for "${packagedAppPath}"`)
|
||||
const atomExecutableName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom'
|
||||
const apmExecutableName = CONFIG.channel === 'beta' ? 'apm-beta' : 'apm'
|
||||
const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
|
||||
const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}`
|
||||
const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}`
|
||||
const appDescription = CONFIG.appMetadata.description
|
||||
const appVersion = CONFIG.appMetadata.version
|
||||
let arch
|
||||
@@ -88,7 +87,7 @@ module.exports = function (packagedAppPath) {
|
||||
console.log(`Writing desktop entry file into "${debianPackageApplicationsDirPath}"`)
|
||||
const desktopEntryTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.desktop.in'))
|
||||
const desktopEntryContents = template(desktopEntryTemplate)({
|
||||
appName: appName,
|
||||
appName: CONFIG.appName,
|
||||
appFileName: atomExecutableName,
|
||||
description: appDescription,
|
||||
installDir: '/usr',
|
||||
|
||||
@@ -10,9 +10,9 @@ const CONFIG = require('../config')
|
||||
|
||||
module.exports = function (packagedAppPath) {
|
||||
console.log(`Creating rpm package for "${packagedAppPath}"`)
|
||||
const atomExecutableName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom'
|
||||
const apmExecutableName = CONFIG.channel === 'beta' ? 'apm-beta' : 'apm'
|
||||
const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
|
||||
const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}`
|
||||
const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}`
|
||||
const appName = CONFIG.appName
|
||||
const appDescription = CONFIG.appMetadata.description
|
||||
// RPM versions can't have dashes or tildes in them.
|
||||
// (Ref.: https://twiki.cern.ch/twiki/bin/view/Main/RPMAndDebVersioning)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const electronInstaller = require('electron-winstaller')
|
||||
const fs = require('fs-extra')
|
||||
const fs = require('fs')
|
||||
const glob = require('glob')
|
||||
const path = require('path')
|
||||
|
||||
@@ -16,17 +16,32 @@ module.exports = (packagedAppPath) => {
|
||||
loadingGif: path.join(CONFIG.repositoryRootPath, 'resources', 'win', 'loading.gif'),
|
||||
outputDirectory: CONFIG.buildOutputPath,
|
||||
noMsi: true,
|
||||
remoteReleases: `https://atom.io/api/updates${archSuffix}?version=${CONFIG.appMetadata.version}`,
|
||||
noDelta: CONFIG.channel === 'nightly', // Delta packages are broken for nightly versions past nightly9 due to Squirrel/NuGet limitations
|
||||
remoteReleases: `https://atom.io/api/updates${archSuffix}?version=${CONFIG.computedAppVersion}`,
|
||||
setupExe: `AtomSetup${process.arch === 'x64' ? '-x64' : ''}.exe`,
|
||||
setupIcon: path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.ico')
|
||||
}
|
||||
|
||||
const cleanUp = () => {
|
||||
for (let nupkgPath of glob.sync(`${CONFIG.buildOutputPath}/*.nupkg`)) {
|
||||
if (!nupkgPath.includes(CONFIG.appMetadata.version)) {
|
||||
const releasesPath = `${CONFIG.buildOutputPath}/RELEASES`
|
||||
if (process.arch === 'x64' && fs.existsSync(releasesPath)) {
|
||||
fs.renameSync(releasesPath, `${releasesPath}-x64`)
|
||||
}
|
||||
|
||||
for (let nupkgPath of glob.sync(`${CONFIG.buildOutputPath}/atom-*.nupkg`)) {
|
||||
if (!nupkgPath.includes(CONFIG.computedAppVersion)) {
|
||||
console.log(`Deleting downloaded nupkg for previous version at ${nupkgPath} to prevent it from being stored as an artifact`)
|
||||
fs.removeSync(nupkgPath)
|
||||
fs.unlinkSync(nupkgPath)
|
||||
} else {
|
||||
if (process.arch === 'x64') {
|
||||
// Use the original .nupkg filename to generate the `atom-x64` name by inserting `-x64` after `atom`
|
||||
const newNupkgPath = nupkgPath.replace('atom-', 'atom-x64-')
|
||||
fs.renameSync(nupkgPath, newNupkgPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return `${CONFIG.buildOutputPath}/${options.setupExe}`
|
||||
}
|
||||
|
||||
console.log(`Creating Windows Installer for ${packagedAppPath}`)
|
||||
|
||||
@@ -6,7 +6,6 @@ const fs = require('fs-plus')
|
||||
const normalizePackageData = require('normalize-package-data')
|
||||
const path = require('path')
|
||||
const semver = require('semver')
|
||||
const spawnSync = require('./spawn-sync')
|
||||
|
||||
const CONFIG = require('../config')
|
||||
|
||||
@@ -16,7 +15,7 @@ module.exports = function () {
|
||||
CONFIG.appMetadata._atomMenu = buildPlatformMenuMetadata()
|
||||
CONFIG.appMetadata._atomKeymaps = buildPlatformKeymapsMetadata()
|
||||
CONFIG.appMetadata._deprecatedPackages = deprecatedPackagesMetadata
|
||||
CONFIG.appMetadata.version = computeAppVersion()
|
||||
CONFIG.appMetadata.version = CONFIG.computedAppVersion
|
||||
checkDeprecatedPackagesMetadata()
|
||||
fs.writeFileSync(path.join(CONFIG.intermediateAppPath, 'package.json'), JSON.stringify(CONFIG.appMetadata))
|
||||
}
|
||||
@@ -162,13 +161,3 @@ function checkDeprecatedPackagesMetadata () {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function computeAppVersion () {
|
||||
let version = CONFIG.appMetadata.version
|
||||
if (CONFIG.channel === 'dev') {
|
||||
const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {cwd: CONFIG.repositoryRootPath})
|
||||
const commitHash = result.stdout.toString().trim()
|
||||
version += '-' + commitHash
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ module.exports = function (packagedAppPath) {
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'atom-keymap', 'lib', 'command-event.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'babel-core', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'cached-run-in-this-context', 'lib', 'main.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'decompress-zip', 'lib', 'decompress-zip.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'debug', 'node.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'git-utils', 'src', 'git.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'glob', 'glob.js') ||
|
||||
@@ -63,6 +62,7 @@ module.exports = function (packagedAppPath) {
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'temp', 'lib', 'temp.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'tmp', 'lib', 'tmp.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'tree-sitter', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'yauzl', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'winreg', 'lib', 'registry.js')
|
||||
)
|
||||
}
|
||||
@@ -74,7 +74,7 @@ module.exports = function (packagedAppPath) {
|
||||
const verifySnapshotScriptPath = path.join(CONFIG.repositoryRootPath, 'script', 'verify-snapshot-script')
|
||||
let nodeBundledInElectronPath
|
||||
if (process.platform === 'darwin') {
|
||||
const executableName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
|
||||
const executableName = CONFIG.appName
|
||||
nodeBundledInElectronPath = path.join(packagedAppPath, 'Contents', 'MacOS', executableName)
|
||||
} else if (process.platform === 'win32') {
|
||||
nodeBundledInElectronPath = path.join(packagedAppPath, 'atom.exe')
|
||||
|
||||
@@ -114,7 +114,7 @@ function buildAsarUnpackGlobExpression () {
|
||||
|
||||
function getAppName () {
|
||||
if (process.platform === 'darwin') {
|
||||
return CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
|
||||
return CONFIG.appName
|
||||
} else {
|
||||
return 'atom'
|
||||
}
|
||||
@@ -156,7 +156,7 @@ function renamePackagedAppDir (packageOutputDirPath) {
|
||||
if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
|
||||
fs.renameSync(path.join(packageOutputDirPath, appBundleName), packagedAppPath)
|
||||
} else if (process.platform === 'linux') {
|
||||
const appName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom'
|
||||
const appName = CONFIG.channel !== 'stable' ? `atom-${CONFIG.channel}` : 'atom'
|
||||
let architecture
|
||||
if (process.arch === 'ia32') {
|
||||
architecture = 'i386'
|
||||
@@ -169,8 +169,7 @@ function renamePackagedAppDir (packageOutputDirPath) {
|
||||
if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
|
||||
fs.renameSync(packageOutputDirPath, packagedAppPath)
|
||||
} else {
|
||||
const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
|
||||
packagedAppPath = path.join(CONFIG.buildOutputPath, appName)
|
||||
packagedAppPath = path.join(CONFIG.buildOutputPath, CONFIG.appName)
|
||||
if (process.platform === 'win32' && process.arch !== 'ia32') {
|
||||
packagedAppPath += ` ${process.arch}`
|
||||
}
|
||||
|
||||
57
script/lib/upload-to-s3.js
Normal file
@@ -0,0 +1,57 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const aws = require('aws-sdk')
|
||||
|
||||
module.exports = function (s3Key, s3Secret, s3Bucket, directory, assets) {
|
||||
const s3 = new aws.S3({
|
||||
accessKeyId: s3Key,
|
||||
secretAccessKey: s3Secret,
|
||||
params: { Bucket: s3Bucket }
|
||||
})
|
||||
|
||||
function listExistingAssetsForDirectory (directory) {
|
||||
return s3.listObjectsV2({ Prefix: directory }).promise().then((res) => {
|
||||
return res.Contents.map((obj) => { return { Key: obj.Key } })
|
||||
})
|
||||
}
|
||||
|
||||
function deleteExistingAssets (existingAssets) {
|
||||
if (existingAssets.length > 0) {
|
||||
return s3.deleteObjects({ Delete: { Objects: existingAssets } }).promise()
|
||||
} else {
|
||||
return Promise.resolve(true)
|
||||
}
|
||||
}
|
||||
|
||||
function uploadAssets (assets, directory) {
|
||||
return assets.reduce(
|
||||
function (promise, asset) {
|
||||
return promise.then(() => uploadAsset(directory, asset))
|
||||
}, Promise.resolve())
|
||||
}
|
||||
|
||||
function uploadAsset (directory, assetPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.info(`Uploading ${assetPath}`)
|
||||
const params = {
|
||||
Key: `${directory}${path.basename(assetPath)}`,
|
||||
ACL: 'public-read',
|
||||
Body: fs.createReadStream(assetPath)
|
||||
}
|
||||
|
||||
s3.upload(params, error => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return listExistingAssetsForDirectory(directory)
|
||||
.then(deleteExistingAssets)
|
||||
.then(() => uploadAssets(assets, directory))
|
||||
}
|
||||
11381
script/package-lock.json
generated
Normal file
@@ -2,13 +2,15 @@
|
||||
"name": "atom-build-scripts",
|
||||
"description": "Atom build scripts",
|
||||
"dependencies": {
|
||||
"7zip-bin": "^4.0.2",
|
||||
"async": "2.0.1",
|
||||
"aws-sdk": "^2.5.2",
|
||||
"babel-core": "5.8.38",
|
||||
"coffeelint": "1.15.7",
|
||||
"colors": "1.1.2",
|
||||
"donna": "1.0.16",
|
||||
"electron-chromedriver": "~2.0",
|
||||
"electron-link": "0.2.0",
|
||||
"electron-link": "0.2.2",
|
||||
"electron-mksnapshot": "~2.0",
|
||||
"electron-packager": "7.3.0",
|
||||
"electron-winstaller": "2.6.4",
|
||||
@@ -26,6 +28,7 @@
|
||||
"npm": "5.3.0",
|
||||
"passwd-user": "2.1.0",
|
||||
"pegjs": "0.9.0",
|
||||
"publish-release": "^1.6.0",
|
||||
"random-seed": "^0.3.0",
|
||||
"season": "5.3.0",
|
||||
"semver": "5.3.0",
|
||||
|
||||
52
script/publish-release
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const glob = require('glob')
|
||||
const publishRelease = require('publish-release')
|
||||
const uploadToS3 = require('./lib/upload-to-s3')
|
||||
const CONFIG = require('./config')
|
||||
|
||||
const yargs = require('yargs')
|
||||
const argv = yargs
|
||||
.usage('Usage: $0 [options]')
|
||||
.help('help')
|
||||
.describe('assets-path', 'Path to the folder where all release assets are stored')
|
||||
.wrap(yargs.terminalWidth())
|
||||
.argv
|
||||
|
||||
let assetsPath = argv.assetsPath || path.join(CONFIG.repositoryRootPath, 'out')
|
||||
let assets = glob.sync(path.join(assetsPath, '*(*.exe|*.zip|*.nupkg|*.tar.gz|*.rpm|*.deb|RELEASES*)'))
|
||||
|
||||
console.log(`Uploading release assets for ${CONFIG.computedAppVersion} to S3`)
|
||||
|
||||
uploadToS3(
|
||||
process.env.ATOM_RELEASES_S3_KEY,
|
||||
process.env.ATOM_RELEASES_S3_SECRET,
|
||||
process.env.ATOM_RELEASES_S3_BUCKET,
|
||||
`releases/v${CONFIG.computedAppVersion}/`,
|
||||
assets).then(
|
||||
() => {
|
||||
console.log(`Publishing GitHub release ${CONFIG.computedAppVersion}`)
|
||||
publishRelease({
|
||||
token: process.env.GITHUB_TOKEN,
|
||||
owner: 'atom',
|
||||
repo: CONFIG.channel !== 'nightly' ? 'atom' : 'atom-nightly-releases',
|
||||
name: CONFIG.computedAppVersion,
|
||||
tag: `v${CONFIG.computedAppVersion}`,
|
||||
draft: false,
|
||||
prerelease: CONFIG.channel !== 'stable',
|
||||
reuseRelease: true,
|
||||
skipIfPublished: true,
|
||||
assets
|
||||
}, function (err, release) {
|
||||
if (err) {
|
||||
console.error("An error occurred while publishing the release:\n\n", err)
|
||||
} else {
|
||||
console.log("Release published successfully: ", release.html_url)
|
||||
}
|
||||
})
|
||||
}).catch((err) => {
|
||||
console.error('An error occurred while uploading the release:', err)
|
||||
})
|
||||
5
script/publish-release.cmd
Normal file
@@ -0,0 +1,5 @@
|
||||
@IF EXIST "%~dp0\node.exe" (
|
||||
"%~dp0\node.exe" "%~dp0\publish-release" %*
|
||||
) ELSE (
|
||||
node "%~dp0\publish-release" %*
|
||||
)
|
||||
65
script/vsts/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Atom Release Build Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This folder contains build configuration and scripts for automating Atom's
|
||||
release pipeline using [Visual Studio Team Services](https://azure.microsoft.com/en-us/services/visual-studio-team-services/).
|
||||
VSTS allows us to leverage [multi-phase jobs](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted-jobs.md) to generate Atom installation packages
|
||||
on Windows, macOS, and Linux and then publish a new release automatically once
|
||||
the build completes successfully.
|
||||
|
||||
## Nightly Release Build
|
||||
|
||||
Our scheduled nightly release uses a mutli-phase job to automatically generate Atom
|
||||
Nightly installation packages and then publish them to GitHub and atom.io.
|
||||
|
||||
The [Atom Nightly build definition](https://github.visualstudio.com/Atom/_build/index?context=mine&path=%5C&definitionId=1&_a=completed)
|
||||
is configured with the [`nightly-release.yml`](nightly-release.yml) file. More
|
||||
information on VSTS' YAML configuration format can be found in their [Getting Started](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted.md)
|
||||
documentation.
|
||||
|
||||
### Versioning Phase
|
||||
|
||||
In this phase, we run [`script/vsts/generate-version.js`](generate-version.js) to
|
||||
determine the version of the next Atom Nightly release. This script consults the
|
||||
GitHub v3 API to get the list of releases on the [`atom/atom-nightly-releases`](https://github.com/atom/atom-nightly-releases)
|
||||
repo. We look for the most recent, non-draft release and then parse its version
|
||||
number (e.g. `1.30.0-nightly4`) to extract the base version and the monotonically-increasing
|
||||
nightly release number.
|
||||
|
||||
Once we have the version and release number, we compare the base version number
|
||||
(`1.30.0`) against the one in `package.json` of the latest commit in the local
|
||||
repo. If those versions are the same, we increment the release number (`1.30.0-nightly5`).
|
||||
If those versions are different, we use `0` for the release number to start a
|
||||
new series of Nightly releases for the new version (`1.31.0-nightly0`).
|
||||
|
||||
Once the release version has been determined, it is set as our custom `ReleaseVersion`
|
||||
[output variable](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted-outputvariables.md)
|
||||
by writing out a special string to `stdout` which is recognized by VSTS. This
|
||||
variable will be used in later build steps.
|
||||
|
||||
If any part of the build process fails from this point forward, the same version
|
||||
number *should* be chosen in the next build unless the base version number has
|
||||
been changed in `master`.
|
||||
|
||||
### OS-specific Build Phases
|
||||
|
||||
In this part of the build, we use [phase templates](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted-templates.md)
|
||||
for [Windows](windows.yml), [macOS](macos.yml), and [Linux](linux.yml) to build
|
||||
Atom simultaneously across those platforms and then run the Atom test suite to
|
||||
verify the builds. If build, test, and linting come back clean, we take the build
|
||||
assets generated in the `out` folder on each OS and then stage them as build artifacts.
|
||||
|
||||
For each OS build, we refer to the `ReleaseVersion` variable, set in the previous
|
||||
phase, to configure the `ATOM_RELEASE_VERSION` environment variable to override
|
||||
the version contained in Atom's `package.json`.
|
||||
|
||||
### Publish Phase
|
||||
|
||||
If all three OS builds have completed successfully, the publish phase will launch the
|
||||
[`script/publish-release`](../publish-release) script to collect the release
|
||||
artifacts created from those builds and then upload them to the S3 bucket from
|
||||
which Atom release assets are served. If the upload process is successful, a new
|
||||
release will be created on the `atom/atom-nightly-releases` repo using the
|
||||
`ReleaseVersion` with a `v` prefix as the tag name. The release assets will also
|
||||
be uploaded to the GitHub release at this time.
|
||||
33
script/vsts/generate-version.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const path = require('path')
|
||||
const request = require('request-promise-native')
|
||||
|
||||
const repositoryRootPath = path.resolve(__dirname, '..', '..')
|
||||
const appMetadata = require(path.join(repositoryRootPath, 'package.json'))
|
||||
const baseVersion = appMetadata.version.split('-')[0]
|
||||
|
||||
async function generateNightlyVersion () {
|
||||
const releases = await request({
|
||||
url: 'https://api.github.com/repos/atom/atom-nightly-releases/releases',
|
||||
headers: {'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'Atom Release Build'},
|
||||
json: true
|
||||
})
|
||||
|
||||
let releaseNumber = 0
|
||||
if (releases && releases.length > 0) {
|
||||
const latestRelease = releases.find(r => !r.draft)
|
||||
const versionMatch = latestRelease.tag_name.match(/^v?(\d+\.\d+\.\d+)-nightly(\d+)$/)
|
||||
|
||||
if (versionMatch && versionMatch[1] === baseVersion) {
|
||||
releaseNumber = parseInt(versionMatch[2]) + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Set our ReleaseVersion build variable and update VSTS' build number to
|
||||
// include the version. Writing these strings to stdout causes VSTS to set
|
||||
// the associated variables.
|
||||
const generatedVersion = `${baseVersion}-nightly${releaseNumber}`
|
||||
console.log(`##vso[task.setvariable variable=ReleaseVersion;isOutput=true]${generatedVersion}`)
|
||||
console.log(`##vso[build.updatebuildnumber]${generatedVersion}+${process.env.BUILD_BUILDNUMBER}`)
|
||||
}
|
||||
|
||||
generateNightlyVersion()
|
||||
58
script/vsts/linux.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
phases:
|
||||
- phase: Linux
|
||||
dependsOn: GetReleaseVersion
|
||||
variables:
|
||||
ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ]
|
||||
queue:
|
||||
name: Hosted Linux Preview
|
||||
timeoutInMinutes: 180
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: 8.9.3
|
||||
displayName: Install Node.js 8.9.3
|
||||
|
||||
- script: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends build-essential xvfb clang-3.5 fakeroot git libsecret-1-dev rpm libx11-dev libxkbfile-dev xz-utils xorriso zsync libxss1 libgconf2-4 libgtk-3-0
|
||||
displayName: Install apt dependencies
|
||||
|
||||
- script: |
|
||||
script/build --create-debian-package --create-rpm-package --compress-artifacts
|
||||
env:
|
||||
ATOM_RELEASE_VERSION: $(ReleaseVersion)
|
||||
displayName: Build Atom
|
||||
|
||||
- script: script/lint
|
||||
displayName: Run linter
|
||||
|
||||
- script: |
|
||||
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16
|
||||
export DISPLAY=':99.0'
|
||||
Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
|
||||
script/test
|
||||
env:
|
||||
CI: true
|
||||
CI_PROVIDER: VSTS
|
||||
displayName: Run tests
|
||||
|
||||
# This step is necessary in the short term due to a bug in the *NIX
|
||||
# implementation of the CopyFiles task which scans the entire file
|
||||
# system structure just to resolve the glob pattern.
|
||||
- script: rm -rf $(Build.SourcesDirectory)/out/*/
|
||||
displayName: Delete Intermediate Output
|
||||
|
||||
- task: CopyFiles@2
|
||||
inputs:
|
||||
sourceFolder: $(Build.SourcesDirectory)/out
|
||||
contents: '?(*.deb|*.rpm|*.tar.gz)'
|
||||
targetFolder: $(Build.ArtifactStagingDirectory)
|
||||
displayName: Stage Artifacts
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)
|
||||
ArtifactName: Binaries
|
||||
ArtifactType: Container
|
||||
displayName: Upload Artifacts
|
||||
55
script/vsts/macos.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
phases:
|
||||
- phase: macOS
|
||||
dependsOn: GetReleaseVersion
|
||||
variables:
|
||||
ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ]
|
||||
queue:
|
||||
name: Hosted macOS Preview
|
||||
timeoutInMinutes: 180
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: 8.9.3
|
||||
displayName: Install Node.js 8.9.3
|
||||
|
||||
- script: |
|
||||
script/build --code-sign --compress-artifacts
|
||||
displayName: Build Atom
|
||||
env:
|
||||
ATOM_RELEASE_VERSION: $(ReleaseVersion)
|
||||
ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL: $(ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL)
|
||||
ATOM_MAC_CODE_SIGNING_CERT_PASSWORD: $(ATOM_MAC_CODE_SIGNING_CERT_PASSWORD)
|
||||
ATOM_MAC_CODE_SIGNING_KEYCHAIN: $(ATOM_MAC_CODE_SIGNING_KEYCHAIN)
|
||||
ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD: $(ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD)
|
||||
|
||||
- script: script/lint
|
||||
displayName: Run linter
|
||||
|
||||
- script: |
|
||||
osascript -e 'tell application "System Events" to keystroke "x"' # clear screen saver
|
||||
caffeinate -s script/test # Run with caffeinate to prevent screen saver
|
||||
env:
|
||||
CI: true
|
||||
CI_PROVIDER: VSTS
|
||||
displayName: Run tests
|
||||
|
||||
# This step is necessary in the short term due to a bug in the *NIX
|
||||
# implementation of the CopyFiles task which scans the entire file
|
||||
# system structure just to resolve the glob pattern.
|
||||
- script: rm -rf $(Build.SourcesDirectory)/out/*/
|
||||
displayName: Delete Intermediate Output
|
||||
|
||||
- task: CopyFiles@2
|
||||
inputs:
|
||||
sourceFolder: $(Build.SourcesDirectory)/out
|
||||
contents: '*.zip'
|
||||
targetFolder: $(Build.ArtifactStagingDirectory)
|
||||
displayName: Stage Artifacts
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)
|
||||
ArtifactName: Binaries
|
||||
ArtifactType: Container
|
||||
displayName: Upload Artifacts
|
||||
57
script/vsts/nightly-release.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
phases:
|
||||
|
||||
- phase: GetReleaseVersion
|
||||
steps:
|
||||
# This has to be done separately because VSTS inexplicably
|
||||
# exits the script block after `npm install` completes.
|
||||
- script: |
|
||||
cd script\vsts
|
||||
npm install
|
||||
displayName: npm install
|
||||
- script: node script\vsts\generate-version.js
|
||||
name: Version
|
||||
|
||||
# Import OS-specific build definitions
|
||||
- template: windows.yml
|
||||
- template: macos.yml
|
||||
- template: linux.yml
|
||||
|
||||
- phase: Release
|
||||
queue: Hosted # Need this for Python 2.7
|
||||
|
||||
dependsOn:
|
||||
- GetReleaseVersion
|
||||
- Windows
|
||||
- Linux
|
||||
- macOS
|
||||
|
||||
variables:
|
||||
ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ]
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: 8.9.3
|
||||
displayName: Install Node.js 8.9.3
|
||||
|
||||
# This has to be done separately because VSTS inexplicably
|
||||
# exits the script block after `npm install` completes.
|
||||
- script: |
|
||||
cd script
|
||||
npm install
|
||||
displayName: npm install
|
||||
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Release Artifacts
|
||||
inputs:
|
||||
artifactName: Binaries
|
||||
|
||||
- script: |
|
||||
$(Build.SourcesDirectory)\script\publish-release.cmd --assets-path "$(System.ArtifactsDirectory)/Binaries"
|
||||
env:
|
||||
GITHUB_TOKEN: $(GITHUB_TOKEN)
|
||||
ATOM_RELEASE_VERSION: $(ReleaseVersion)
|
||||
ATOM_RELEASES_S3_KEY: $(ATOM_RELEASES_S3_KEY)
|
||||
ATOM_RELEASES_S3_SECRET: $(ATOM_RELEASES_S3_SECRET)
|
||||
ATOM_RELEASES_S3_BUCKET: $(ATOM_RELEASES_S3_BUCKET)
|
||||
displayName: Create Nightly Release
|
||||
357
script/vsts/package-lock.json
generated
Normal file
@@ -0,0 +1,357 @@
|
||||
{
|
||||
"name": "atom-release-scripts",
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
||||
"requires": {
|
||||
"co": "4.6.0",
|
||||
"fast-deep-equal": "1.1.0",
|
||||
"fast-json-stable-stringify": "2.0.0",
|
||||
"json-schema-traverse": "0.3.1"
|
||||
}
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
|
||||
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
|
||||
"integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w=="
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
|
||||
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"tweetnacl": "0.14.5"
|
||||
}
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
|
||||
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
|
||||
"requires": {
|
||||
"delayed-stream": "1.0.0"
|
||||
}
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsbn": "0.1.1"
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
|
||||
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
|
||||
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
|
||||
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
|
||||
"requires": {
|
||||
"asynckit": "0.4.0",
|
||||
"combined-stream": "1.0.6",
|
||||
"mime-types": "2.1.18"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
}
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
|
||||
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
|
||||
"requires": {
|
||||
"ajv": "5.5.2",
|
||||
"har-schema": "2.0.0"
|
||||
}
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"jsprim": "1.4.1",
|
||||
"sshpk": "1.14.2"
|
||||
}
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
|
||||
"optional": true
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
|
||||
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
"json-schema": "0.2.3",
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.10",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
|
||||
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.18",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
|
||||
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
|
||||
"requires": {
|
||||
"mime-db": "1.33.0"
|
||||
}
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
|
||||
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||
},
|
||||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||
},
|
||||
"request": {
|
||||
"version": "2.87.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz",
|
||||
"integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==",
|
||||
"requires": {
|
||||
"aws-sign2": "0.7.0",
|
||||
"aws4": "1.7.0",
|
||||
"caseless": "0.12.0",
|
||||
"combined-stream": "1.0.6",
|
||||
"extend": "3.0.1",
|
||||
"forever-agent": "0.6.1",
|
||||
"form-data": "2.3.2",
|
||||
"har-validator": "5.0.3",
|
||||
"http-signature": "1.2.0",
|
||||
"is-typedarray": "1.0.0",
|
||||
"isstream": "0.1.2",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"mime-types": "2.1.18",
|
||||
"oauth-sign": "0.8.2",
|
||||
"performance-now": "2.1.0",
|
||||
"qs": "6.5.2",
|
||||
"safe-buffer": "5.1.2",
|
||||
"tough-cookie": "2.3.4",
|
||||
"tunnel-agent": "0.6.0",
|
||||
"uuid": "3.3.0"
|
||||
}
|
||||
},
|
||||
"request-promise-core": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
|
||||
"integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
|
||||
"requires": {
|
||||
"lodash": "4.17.10"
|
||||
}
|
||||
},
|
||||
"request-promise-native": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz",
|
||||
"integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=",
|
||||
"requires": {
|
||||
"request-promise-core": "1.1.1",
|
||||
"stealthy-require": "1.1.1",
|
||||
"tough-cookie": "2.3.4"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
|
||||
"integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
|
||||
"requires": {
|
||||
"asn1": "0.2.3",
|
||||
"assert-plus": "1.0.0",
|
||||
"bcrypt-pbkdf": "1.0.1",
|
||||
"dashdash": "1.14.1",
|
||||
"ecc-jsbn": "0.1.1",
|
||||
"getpass": "0.1.7",
|
||||
"jsbn": "0.1.1",
|
||||
"safer-buffer": "2.1.2",
|
||||
"tweetnacl": "0.14.5"
|
||||
}
|
||||
},
|
||||
"stealthy-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
|
||||
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
|
||||
"integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
|
||||
"requires": {
|
||||
"punycode": "1.4.1"
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||
"optional": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.0.tgz",
|
||||
"integrity": "sha512-ijO9N2xY/YaOqQ5yz5c4sy2ZjWmA6AR6zASb/gdpeKZ8+948CxwfMW9RrKVk5may6ev8c0/Xguu32e2Llelpqw=="
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "1.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
script/vsts/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "atom-release-scripts",
|
||||
"description": "Atom release scripts",
|
||||
"dependencies": {
|
||||
"request": "^2.87.0",
|
||||
"request-promise-native": "^1.0.5"
|
||||
}
|
||||
}
|
||||
54
script/vsts/windows.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
phases:
|
||||
- phase: Windows
|
||||
dependsOn: GetReleaseVersion
|
||||
variables:
|
||||
ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ]
|
||||
queue:
|
||||
name: Hosted
|
||||
timeoutInMinutes: 180
|
||||
parallel: 2
|
||||
matrix:
|
||||
x64:
|
||||
buildArch: x64
|
||||
# TODO: x86 is currently not supported on VSTS
|
||||
# x86:
|
||||
# buildArch: x86
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: 8.9.3
|
||||
displayName: Install Node.js 8.9.3
|
||||
|
||||
- script: |
|
||||
IF NOT EXIST C:\tmp MKDIR C:\tmp
|
||||
SET SQUIRREL_TEMP=C:\tmp
|
||||
script\build.cmd --create-windows-installer --code-sign --compress-artifacts
|
||||
env:
|
||||
ATOM_RELEASE_VERSION: $(ReleaseVersion)
|
||||
ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL: $(ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL)
|
||||
ATOM_WIN_CODE_SIGNING_CERT_PASSWORD: $(ATOM_WIN_CODE_SIGNING_CERT_PASSWORD)
|
||||
displayName: Build Atom
|
||||
|
||||
- script: script\lint.cmd
|
||||
displayName: Run linter
|
||||
|
||||
- script: script\test.cmd
|
||||
env:
|
||||
CI: true
|
||||
CI_PROVIDER: VSTS
|
||||
displayName: Run tests
|
||||
|
||||
- task: CopyFiles@2
|
||||
inputs:
|
||||
sourceFolder: $(Build.SourcesDirectory)/out
|
||||
contents: '?(*.exe|*.zip|*.nupkg|RELEASES*)'
|
||||
targetFolder: $(Build.ArtifactStagingDirectory)
|
||||
displayName: Stage Artifacts
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)
|
||||
ArtifactName: Binaries
|
||||
ArtifactType: Container
|
||||
displayName: Upload Artifacts
|
||||
@@ -33,6 +33,8 @@ formatStackTrace = (spec, message='', stackTrace) ->
|
||||
line = line.trim()
|
||||
# at jasmine.Spec.<anonymous> (path:1:2) -> at path:1:2
|
||||
.replace(/^at jasmine\.Spec\.<anonymous> \(([^)]+)\)/, 'at $1')
|
||||
# at jasmine.Spec.it (path:1:2) -> at path:1:2
|
||||
.replace(/^at jasmine\.Spec\.f*it \(([^)]+)\)/, 'at $1')
|
||||
# at it (path:1:2) -> at path:1:2
|
||||
.replace(/^at f*it \(([^)]+)\)/, 'at $1')
|
||||
# at spec/file-test.js -> at file-test.js
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs-plus')
|
||||
const temp = require('temp').track()
|
||||
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers');
|
||||
const CommandInstaller = require('../src/command-installer')
|
||||
|
||||
describe('CommandInstaller on #darwin', () => {
|
||||
@@ -56,8 +57,8 @@ describe('CommandInstaller on #darwin', () => {
|
||||
const appDelegate = jasmine.createSpyObj('appDelegate', ['confirm'])
|
||||
installer = new CommandInstaller(appDelegate)
|
||||
installer.initialize('2.0.2')
|
||||
spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback())
|
||||
spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback())
|
||||
spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback(undefined, 'atom'))
|
||||
spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback(undefined, 'apm'))
|
||||
|
||||
installer.installShellCommandsInteractively()
|
||||
|
||||
@@ -140,4 +141,41 @@ describe('CommandInstaller on #darwin', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when using a nightly version of atom', () => {
|
||||
beforeEach(() => {
|
||||
installer = new CommandInstaller()
|
||||
installer.initialize('2.2.0-nightly0')
|
||||
})
|
||||
|
||||
it("symlinks the atom command as 'atom-nightly'", () => {
|
||||
const installedAtomPath = path.join(installationPath, 'atom-nightly')
|
||||
expect(fs.isFileSync(installedAtomPath)).toBeFalsy()
|
||||
|
||||
waitsFor(done => {
|
||||
installer.installAtomCommand(false, error => {
|
||||
expect(error).toBeNull()
|
||||
expect(fs.realpathSync(installedAtomPath)).toBe(fs.realpathSync(atomBinPath))
|
||||
expect(fs.isExecutableSync(installedAtomPath)).toBe(true)
|
||||
expect(fs.isFileSync(path.join(installationPath, 'atom'))).toBe(false)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("symlinks the apm command as 'apm-nightly'", () => {
|
||||
const installedApmPath = path.join(installationPath, 'apm-nightly')
|
||||
expect(fs.isFileSync(installedApmPath)).toBeFalsy()
|
||||
|
||||
waitsFor(done => {
|
||||
installer.installApmCommand(false, error => {
|
||||
expect(error).toBeNull()
|
||||
expect(fs.realpathSync(installedApmPath)).toBe(fs.realpathSync(apmBinPath))
|
||||
expect(fs.isExecutableSync(installedApmPath)).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(installationPath, 'nightly'))).toBe(false)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
3
spec/fixtures/git/master.git/config
vendored
@@ -4,3 +4,6 @@
|
||||
bare = false
|
||||
logallrefupdates = true
|
||||
ignorecase = true
|
||||
[remote "origin"]
|
||||
url = https://github.com/example-user/example-repo.git
|
||||
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||
|
||||
@@ -283,32 +283,6 @@ describe('GrammarRegistry', () => {
|
||||
expect(atom.grammars.selectGrammar('/hu.git/config').name).toBe('Null Grammar')
|
||||
})
|
||||
|
||||
describe('when the grammar has a contentRegExp field', () => {
|
||||
it('favors grammars whose contentRegExp matches a prefix of the file\'s content', () => {
|
||||
atom.grammars.addGrammar({
|
||||
id: 'javascript-1',
|
||||
fileTypes: ['js']
|
||||
})
|
||||
atom.grammars.addGrammar({
|
||||
id: 'flow-javascript',
|
||||
contentRegExp: new RegExp('//.*@flow'),
|
||||
fileTypes: ['js']
|
||||
})
|
||||
atom.grammars.addGrammar({
|
||||
id: 'javascript-2',
|
||||
fileTypes: ['js']
|
||||
})
|
||||
|
||||
const selectedGrammar = atom.grammars.selectGrammar('test.js', dedent`
|
||||
// Copyright EvilCorp
|
||||
// @flow
|
||||
|
||||
module.exports = function () { return 1 + 1 }
|
||||
`)
|
||||
expect(selectedGrammar.id).toBe('flow-javascript')
|
||||
})
|
||||
})
|
||||
|
||||
it("uses the filePath's shebang line if the grammar cannot be determined by the extension or basename", async () => {
|
||||
await atom.packages.activatePackage('language-javascript')
|
||||
await atom.packages.activatePackage('language-ruby')
|
||||
@@ -441,6 +415,104 @@ describe('GrammarRegistry', () => {
|
||||
expect(grammar.id).toBe('javascript')
|
||||
expect(grammar instanceof TreeSitterGrammar).toBe(true)
|
||||
})
|
||||
|
||||
it('only favors a tree-sitter grammar if it actually matches in some way (regression)', () => {
|
||||
atom.config.set('core.useTreeSitterParsers', true)
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
|
||||
|
||||
const grammar = grammarRegistry.selectGrammar('test', '')
|
||||
expect(grammar.name).toBe('Null Grammar')
|
||||
})
|
||||
})
|
||||
|
||||
describe('tree-sitter grammars with content regexes', () => {
|
||||
it('recognizes C++ header files', () => {
|
||||
atom.config.set('core.useTreeSitterParsers', true)
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-c.cson'))
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson'))
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-coffee-script/grammars/coffeescript.cson'))
|
||||
|
||||
let grammar = grammarRegistry.selectGrammar('test.h', dedent `
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
void verb();
|
||||
} Noun;
|
||||
`)
|
||||
expect(grammar.name).toBe('C')
|
||||
|
||||
grammar = grammarRegistry.selectGrammar('test.h', dedent `
|
||||
#include <string>
|
||||
|
||||
class Noun {
|
||||
public:
|
||||
void verb();
|
||||
};
|
||||
`)
|
||||
expect(grammar.name).toBe('C++')
|
||||
|
||||
// The word `class` only indicates C++ in `.h` files, not in all files.
|
||||
grammar = grammarRegistry.selectGrammar('test.coffee', dedent `
|
||||
module.exports =
|
||||
class Noun
|
||||
verb: -> true
|
||||
`)
|
||||
expect(grammar.name).toBe('CoffeeScript')
|
||||
})
|
||||
|
||||
it('recognizes C++ files that do not match the content regex (regression)', () => {
|
||||
atom.config.set('core.useTreeSitterParsers', true)
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-c.cson'))
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/c++.cson'))
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson'))
|
||||
|
||||
let grammar = grammarRegistry.selectGrammar('test.cc', dedent `
|
||||
int a();
|
||||
`)
|
||||
expect(grammar.name).toBe('C++')
|
||||
})
|
||||
|
||||
it('recognizes shell scripts with shebang lines', () => {
|
||||
atom.config.set('core.useTreeSitterParsers', true)
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-shellscript/grammars/shell-unix-bash.cson'))
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-shellscript/grammars/tree-sitter-bash.cson'))
|
||||
|
||||
let grammar = grammarRegistry.selectGrammar('test.h', dedent `
|
||||
#!/bin/bash
|
||||
|
||||
echo "hi"
|
||||
`)
|
||||
expect(grammar.name).toBe('Shell Script')
|
||||
expect(grammar instanceof TreeSitterGrammar).toBeTruthy()
|
||||
|
||||
atom.config.set('core.useTreeSitterParsers', false)
|
||||
grammar = grammarRegistry.selectGrammar('test.h', dedent `
|
||||
#!/bin/bash
|
||||
|
||||
echo "hi"
|
||||
`)
|
||||
expect(grammar.name).toBe('Shell Script')
|
||||
expect(grammar instanceof TreeSitterGrammar).toBeFalsy()
|
||||
})
|
||||
|
||||
it('recognizes JavaScript files that use Flow', () => {
|
||||
atom.config.set('core.useTreeSitterParsers', true)
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
|
||||
grammarRegistry.loadGrammarSync(require.resolve('language-typescript/grammars/tree-sitter-flow.cson'))
|
||||
|
||||
let grammar = grammarRegistry.selectGrammar('test.js', dedent`
|
||||
// Copyright something
|
||||
// @flow
|
||||
|
||||
module.exports = function () { return 1 + 1 }
|
||||
`)
|
||||
expect(grammar.name).toBe('Flow JavaScript')
|
||||
|
||||
grammar = grammarRegistry.selectGrammar('test.js', dedent`
|
||||
module.exports = function () { return 1 + 1 }
|
||||
`)
|
||||
expect(grammar.name).toBe('JavaScript')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -453,6 +525,34 @@ describe('GrammarRegistry', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('.addInjectionPoint(languageId, {type, language, content})', () => {
|
||||
const injectionPoint = {
|
||||
type: 'some_node_type',
|
||||
language() { return 'some_language_name' },
|
||||
content(node) { return node }
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
atom.config.set('core.useTreeSitterParsers', true)
|
||||
})
|
||||
|
||||
it('adds an injection point to the grammar with the given id', async () => {
|
||||
await atom.packages.activatePackage('language-javascript')
|
||||
atom.grammars.addInjectionPoint('javascript', injectionPoint)
|
||||
const grammar = atom.grammars.grammarForId('javascript')
|
||||
expect(grammar.injectionPoints).toContain(injectionPoint)
|
||||
})
|
||||
|
||||
describe('when called before a grammar with the given id is loaded', () => {
|
||||
it('adds the injection point once the grammar is loaded', async () => {
|
||||
atom.grammars.addInjectionPoint('javascript', injectionPoint)
|
||||
await atom.packages.activatePackage('language-javascript')
|
||||
const grammar = atom.grammars.grammarForId('javascript')
|
||||
expect(grammar.injectionPoints).toContain(injectionPoint)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('serialization', () => {
|
||||
it('persists editors\' grammar overrides', async () => {
|
||||
const buffer1 = new TextBuffer()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const {dialog} = require('electron')
|
||||
const FileRecoveryService = require('../../src/main-process/file-recovery-service')
|
||||
const fs = require('fs-plus')
|
||||
const fsreal = require('fs')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const sinon = require('sinon')
|
||||
const {escapeRegExp} = require('underscore-plus')
|
||||
const temp = require('temp').track()
|
||||
@@ -116,13 +118,22 @@ describe("FileRecoveryService", () => {
|
||||
const mockWindow = {}
|
||||
const filePath = temp.path()
|
||||
fs.writeFileSync(filePath, "content")
|
||||
fs.chmodSync(filePath, 0444)
|
||||
|
||||
let logs = []
|
||||
spies.stub(console, 'log', (message) => logs.push(message))
|
||||
spies.stub(dialog, 'showMessageBox')
|
||||
|
||||
// Copy files to be recovered before mocking fs.createWriteStream
|
||||
await recoveryService.willSavePath(mockWindow, filePath)
|
||||
|
||||
// Stub out fs.createWriteStream so that we can return a fake error when
|
||||
// attempting to copy the recovered file to its original location
|
||||
var fakeEmitter = new EventEmitter()
|
||||
var onStub = spies.stub(fakeEmitter, 'on')
|
||||
onStub.withArgs('error').yields(new Error('Nope')).returns(fakeEmitter)
|
||||
onStub.withArgs('open').returns(fakeEmitter)
|
||||
spies.stub(fsreal, 'createWriteStream').withArgs(filePath).returns(fakeEmitter)
|
||||
|
||||
await recoveryService.didCrashWindow(mockWindow)
|
||||
let recoveryFiles = fs.listTreeSync(recoveryDirectory)
|
||||
assert.equal(recoveryFiles.length, 1)
|
||||
|
||||
@@ -1032,6 +1032,7 @@ describe('PackageManager', () => {
|
||||
})
|
||||
|
||||
it('loads any tree-sitter grammars defined in the package', async () => {
|
||||
atom.config.set('core.useTreeSitterParsers', true)
|
||||
await atom.packages.activatePackage('package-with-tree-sitter-grammar')
|
||||
const grammar = atom.grammars.selectGrammar('test.somelang')
|
||||
expect(grammar.name).toBe('Some Language')
|
||||
|
||||
@@ -219,6 +219,34 @@ describe('Pane', () => {
|
||||
runs(() => expect(eventOrder).toEqual(['add', 'remove']))
|
||||
})
|
||||
|
||||
it('subscribes to be notified when item terminates its pending state', () => {
|
||||
const fakeDisposable = { dispose: () => {} }
|
||||
const spy = jasmine.createSpy('onDidTerminatePendingState').andReturn((fakeDisposable))
|
||||
|
||||
const pane = new Pane(paneParams({items: []}))
|
||||
const item = {
|
||||
getTitle: () => '',
|
||||
onDidTerminatePendingState: spy
|
||||
}
|
||||
pane.addItem(item)
|
||||
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('subscribes to be notified when item is destroyed', () => {
|
||||
const fakeDisposable = { dispose: () => {} }
|
||||
const spy = jasmine.createSpy('onDidDestroy').andReturn((fakeDisposable))
|
||||
|
||||
const pane = new Pane(paneParams({items: []}))
|
||||
const item = {
|
||||
getTitle: () => '',
|
||||
onDidDestroy: spy
|
||||
}
|
||||
pane.addItem(item)
|
||||
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
describe('when using the old API of ::addItem(item, index)', () => {
|
||||
beforeEach(() => spyOn(Grim, 'deprecate'))
|
||||
|
||||
|
||||
@@ -969,6 +969,77 @@ describe('Project', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('.observeRepositories()', () => {
|
||||
it('invokes the observer with current and future repositories', () => {
|
||||
const observed = []
|
||||
|
||||
const directory1 = temp.mkdirSync('git-repo1')
|
||||
const gitDirPath1 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
fs.copySync(gitDirPath1, path.join(directory1, '.git'))
|
||||
|
||||
const directory2 = temp.mkdirSync('git-repo2')
|
||||
const gitDirPath2 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'repo-with-submodules', 'git.git'))
|
||||
fs.copySync(gitDirPath2, path.join(directory2, '.git'))
|
||||
|
||||
atom.project.setPaths([directory1])
|
||||
|
||||
const disposable = atom.project.observeRepositories((repo) => observed.push(repo))
|
||||
expect(observed.length).toBe(1)
|
||||
expect(observed[0].getReferenceTarget('refs/heads/master')).toBe('ef046e9eecaa5255ea5e9817132d4001724d6ae1')
|
||||
|
||||
atom.project.addPath(directory2)
|
||||
expect(observed.length).toBe(2)
|
||||
expect(observed[1].getReferenceTarget('refs/heads/master')).toBe('d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5')
|
||||
|
||||
disposable.dispose()
|
||||
})
|
||||
})
|
||||
|
||||
describe('.onDidAddRepository()', () => {
|
||||
it('invokes callback when a path is added and the path is the root of a repository', () => {
|
||||
const observed = []
|
||||
const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo))
|
||||
|
||||
const projectRootPath = temp.mkdirSync()
|
||||
const fixtureRepoPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
fs.copySync(fixtureRepoPath, path.join(projectRootPath, '.git'))
|
||||
|
||||
atom.project.addPath(projectRootPath)
|
||||
expect(observed.length).toBe(1)
|
||||
expect(observed[0].getOriginURL()).toEqual('https://github.com/example-user/example-repo.git')
|
||||
|
||||
disposable.dispose()
|
||||
})
|
||||
|
||||
it('invokes callback when a path is added and the path is subdirectory of a repository', () => {
|
||||
const observed = []
|
||||
const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo))
|
||||
|
||||
const projectRootPath = temp.mkdirSync()
|
||||
const fixtureRepoPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git'))
|
||||
fs.copySync(fixtureRepoPath, path.join(projectRootPath, '.git'))
|
||||
|
||||
const projectSubDirPath = path.join(projectRootPath, 'sub-dir')
|
||||
fs.mkdirSync(projectSubDirPath)
|
||||
|
||||
atom.project.addPath(projectSubDirPath)
|
||||
expect(observed.length).toBe(1)
|
||||
expect(observed[0].getOriginURL()).toEqual('https://github.com/example-user/example-repo.git')
|
||||
|
||||
disposable.dispose()
|
||||
})
|
||||
|
||||
it('does not invoke callback when a path is added and the path is not part of a repository', () => {
|
||||
const observed = []
|
||||
const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo))
|
||||
|
||||
atom.project.addPath(temp.mkdirSync('not-a-repository'))
|
||||
expect(observed.length).toBe(0)
|
||||
|
||||
disposable.dispose()
|
||||
})
|
||||
})
|
||||
|
||||
describe('.relativize(path)', () => {
|
||||
it('returns the path, relative to whichever root directory it is inside of', () => {
|
||||
atom.project.addPath(temp.mkdirSync('another-path'))
|
||||
|
||||
@@ -26,6 +26,7 @@ document.registerElement('text-editor-component-test-element', {
|
||||
})
|
||||
|
||||
const editors = []
|
||||
let verticalScrollbarWidth, horizontalScrollbarHeight
|
||||
|
||||
describe('TextEditorComponent', () => {
|
||||
beforeEach(() => {
|
||||
@@ -33,8 +34,15 @@ describe('TextEditorComponent', () => {
|
||||
|
||||
// Force scrollbars to be visible regardless of local system configuration
|
||||
const scrollbarStyle = document.createElement('style')
|
||||
scrollbarStyle.textContent = '::-webkit-scrollbar { -webkit-appearance: none }'
|
||||
scrollbarStyle.textContent = 'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }'
|
||||
jasmine.attachToDOM(scrollbarStyle)
|
||||
|
||||
if (verticalScrollbarWidth == null) {
|
||||
const {component, element} = buildComponent({text: 'abcdefgh\n'.repeat(10), width: 30, height: 30})
|
||||
verticalScrollbarWidth = getVerticalScrollbarWidth(component)
|
||||
horizontalScrollbarHeight = getHorizontalScrollbarHeight(component)
|
||||
element.remove()
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -184,8 +192,8 @@ describe('TextEditorComponent', () => {
|
||||
})
|
||||
|
||||
it('makes the content at least as tall as the scroll container client height', async () => {
|
||||
const {component, element, editor} = buildComponent({text: 'a', height: 100})
|
||||
expect(component.refs.content.offsetHeight).toBe(100)
|
||||
const {component, element, editor} = buildComponent({text: 'a'.repeat(100), width: 50, height: 100})
|
||||
expect(component.refs.content.offsetHeight).toBe(100 - getHorizontalScrollbarHeight(component))
|
||||
|
||||
editor.setText('a\n'.repeat(30))
|
||||
await component.getNextUpdatePromise()
|
||||
@@ -201,7 +209,7 @@ describe('TextEditorComponent', () => {
|
||||
await setEditorHeightInLines(component, 6)
|
||||
|
||||
// scroll to end
|
||||
await setScrollTop(component, scrollContainer.scrollHeight - scrollContainer.clientHeight)
|
||||
await setScrollTop(component, Infinity)
|
||||
expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() - 3)
|
||||
|
||||
editor.update({scrollPastEnd: false})
|
||||
@@ -211,7 +219,7 @@ describe('TextEditorComponent', () => {
|
||||
// Always allows at least 3 lines worth of overscroll if the editor is short
|
||||
await setEditorHeightInLines(component, 2)
|
||||
await editor.update({scrollPastEnd: true})
|
||||
await setScrollTop(component, scrollContainer.scrollHeight - scrollContainer.clientHeight)
|
||||
await setScrollTop(component, Infinity)
|
||||
expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() + 1)
|
||||
})
|
||||
|
||||
@@ -296,31 +304,6 @@ describe('TextEditorComponent', () => {
|
||||
expect(lineNumberNodeForScreenRow(component, 0).querySelector('.foldable')).toBeNull()
|
||||
})
|
||||
|
||||
it('gracefully handles folds that change the soft-wrap boundary by causing the vertical scrollbar to disappear (regression)', async () => {
|
||||
const text = ('x'.repeat(100) + '\n') + 'y\n'.repeat(28) + ' z\n'.repeat(50)
|
||||
const {component, element, editor} = buildComponent({text, height: 1000, width: 500})
|
||||
|
||||
element.addEventListener('scroll', (event) => {
|
||||
event.stopPropagation()
|
||||
}, true)
|
||||
|
||||
editor.setSoftWrapped(true)
|
||||
jasmine.attachToDOM(element)
|
||||
await component.getNextUpdatePromise()
|
||||
|
||||
const firstScreenLineLengthWithVerticalScrollbar = element.querySelector('.line').textContent.length
|
||||
|
||||
setScrollTop(component, 620)
|
||||
await component.getNextUpdatePromise()
|
||||
|
||||
editor.foldBufferRow(28)
|
||||
await component.getNextUpdatePromise()
|
||||
|
||||
const firstLineElement = element.querySelector('.line')
|
||||
expect(firstLineElement.dataset.screenRow).toBe('0')
|
||||
expect(firstLineElement.textContent.length).toBeGreaterThan(firstScreenLineLengthWithVerticalScrollbar)
|
||||
})
|
||||
|
||||
it('shows the foldable icon on the last screen row of a buffer row that can be folded', async () => {
|
||||
const {component, element, editor} = buildComponent({text: 'abc\n de\nfghijklm\n no', softWrapped: true})
|
||||
await setEditorWidthInCharacters(component, 5)
|
||||
@@ -361,18 +344,14 @@ describe('TextEditorComponent', () => {
|
||||
expect(getVerticalScrollbarWidth(component)).toBeGreaterThan(0)
|
||||
expect(getHorizontalScrollbarHeight(component)).toBe(0)
|
||||
expect(verticalScrollbar.style.visibility).toBe('')
|
||||
expect(verticalScrollbar.style.bottom).toBe('0px')
|
||||
expect(horizontalScrollbar.style.visibility).toBe('hidden')
|
||||
expect(component.refs.scrollbarCorner).toBeUndefined()
|
||||
|
||||
editor.setText('a'.repeat(100))
|
||||
await component.getNextUpdatePromise()
|
||||
expect(getVerticalScrollbarWidth(component)).toBe(0)
|
||||
expect(getHorizontalScrollbarHeight(component)).toBeGreaterThan(0)
|
||||
expect(verticalScrollbar.style.visibility).toBe('hidden')
|
||||
expect(horizontalScrollbar.style.right).toBe('0px')
|
||||
expect(horizontalScrollbar.style.visibility).toBe('')
|
||||
expect(component.refs.scrollbarCorner).toBeUndefined()
|
||||
|
||||
editor.setText('')
|
||||
await component.getNextUpdatePromise()
|
||||
@@ -380,37 +359,6 @@ describe('TextEditorComponent', () => {
|
||||
expect(getHorizontalScrollbarHeight(component)).toBe(0)
|
||||
expect(verticalScrollbar.style.visibility).toBe('hidden')
|
||||
expect(horizontalScrollbar.style.visibility).toBe('hidden')
|
||||
expect(component.refs.scrollbarCorner).toBeUndefined()
|
||||
|
||||
editor.setText(SAMPLE_TEXT)
|
||||
await component.getNextUpdatePromise()
|
||||
|
||||
// Does not show scrollbars if the content perfectly fits
|
||||
element.style.width = component.getGutterContainerWidth() + component.getContentWidth() + 'px'
|
||||
element.style.height = component.getContentHeight() + 'px'
|
||||
await component.getNextUpdatePromise()
|
||||
expect(getVerticalScrollbarWidth(component)).toBe(0)
|
||||
expect(getHorizontalScrollbarHeight(component)).toBe(0)
|
||||
expect(verticalScrollbar.style.visibility).toBe('hidden')
|
||||
expect(horizontalScrollbar.style.visibility).toBe('hidden')
|
||||
|
||||
// Shows scrollbars if the only reason we overflow is the presence of the
|
||||
// scrollbar for the opposite axis.
|
||||
element.style.width = component.getGutterContainerWidth() + component.getContentWidth() - 1 + 'px'
|
||||
element.style.height = component.getContentHeight() + component.getHorizontalScrollbarHeight() - 1 + 'px'
|
||||
await component.getNextUpdatePromise()
|
||||
expect(getVerticalScrollbarWidth(component)).toBeGreaterThan(0)
|
||||
expect(getHorizontalScrollbarHeight(component)).toBeGreaterThan(0)
|
||||
expect(verticalScrollbar.style.visibility).toBe('')
|
||||
expect(horizontalScrollbar.style.visibility).toBe('')
|
||||
|
||||
element.style.width = component.getGutterContainerWidth() + component.getContentWidth() + component.getVerticalScrollbarWidth() - 1 + 'px'
|
||||
element.style.height = component.getContentHeight() - 1 + 'px'
|
||||
await component.getNextUpdatePromise()
|
||||
expect(getVerticalScrollbarWidth(component)).toBeGreaterThan(0)
|
||||
expect(getHorizontalScrollbarHeight(component)).toBeGreaterThan(0)
|
||||
expect(verticalScrollbar.style.visibility).toBe('')
|
||||
expect(horizontalScrollbar.style.visibility).toBe('')
|
||||
})
|
||||
|
||||
describe('when scrollbar styles change or the editor element is detached and then reattached', () => {
|
||||
@@ -683,17 +631,6 @@ describe('TextEditorComponent', () => {
|
||||
expect(scrollContainer.clientWidth).toBe(scrollContainer.scrollWidth)
|
||||
})
|
||||
|
||||
it('accounts for the width of the vertical scrollbar when soft-wrapping lines', async () => {
|
||||
const {component, element, editor} = buildComponent({
|
||||
height: 200,
|
||||
text: 'a'.repeat(300),
|
||||
softWrapped: true
|
||||
})
|
||||
await setEditorWidthInCharacters(component, 23)
|
||||
expect(Math.floor(component.getScrollContainerClientWidth() / component.getBaseCharacterWidth())).toBe(20)
|
||||
expect(editor.lineLengthForScreenRow(0)).toBe(20)
|
||||
})
|
||||
|
||||
it('correctly forces the display layer to index visible rows when resizing (regression)', async () => {
|
||||
const text = 'a'.repeat(30) + '\n' + 'b'.repeat(1000)
|
||||
const {component, element, editor} = buildComponent({height: 300, width: 800, attach: false, text})
|
||||
@@ -718,7 +655,7 @@ describe('TextEditorComponent', () => {
|
||||
editor.setText('a')
|
||||
await component.getNextUpdatePromise()
|
||||
|
||||
expect(element.querySelector('.line').offsetWidth).toBe(scrollContainer.offsetWidth)
|
||||
expect(element.querySelector('.line').offsetWidth).toBe(scrollContainer.offsetWidth - verticalScrollbarWidth)
|
||||
})
|
||||
|
||||
it('resizes based on the content when the autoHeight and/or autoWidth options are true', async () => {
|
||||
@@ -728,44 +665,39 @@ describe('TextEditorComponent', () => {
|
||||
const {gutterContainer, scrollContainer} = component.refs
|
||||
const initialWidth = element.offsetWidth
|
||||
const initialHeight = element.offsetHeight
|
||||
expect(initialWidth).toBe(component.getGutterContainerWidth() + component.getContentWidth() + 2 * editorPadding)
|
||||
expect(initialHeight).toBe(component.getContentHeight() + 2 * editorPadding)
|
||||
expect(initialWidth).toBe(
|
||||
component.getGutterContainerWidth() +
|
||||
component.getContentWidth() +
|
||||
verticalScrollbarWidth +
|
||||
2 * editorPadding
|
||||
)
|
||||
expect(initialHeight).toBe(
|
||||
component.getContentHeight() +
|
||||
horizontalScrollbarHeight +
|
||||
2 * editorPadding
|
||||
)
|
||||
|
||||
// When autoWidth is enabled, width adjusts to content
|
||||
editor.setCursorScreenPosition([6, Infinity])
|
||||
editor.insertText('x'.repeat(50))
|
||||
await component.getNextUpdatePromise()
|
||||
expect(element.offsetWidth).toBe(component.getGutterContainerWidth() + component.getContentWidth() + 2 * editorPadding)
|
||||
expect(element.offsetWidth).toBe(
|
||||
component.getGutterContainerWidth() +
|
||||
component.getContentWidth() +
|
||||
verticalScrollbarWidth +
|
||||
2 * editorPadding
|
||||
)
|
||||
expect(element.offsetWidth).toBeGreaterThan(initialWidth)
|
||||
|
||||
// When autoHeight is enabled, height adjusts to content
|
||||
editor.insertText('\n'.repeat(5))
|
||||
await component.getNextUpdatePromise()
|
||||
expect(element.offsetHeight).toBe(component.getContentHeight() + 2 * editorPadding)
|
||||
expect(element.offsetHeight).toBeGreaterThan(initialHeight)
|
||||
|
||||
// When a horizontal scrollbar is visible, autoHeight accounts for it
|
||||
editor.update({autoWidth: false})
|
||||
await component.getNextUpdatePromise()
|
||||
element.style.width = component.getGutterContainerWidth() + component.getContentHeight() - 20 + 'px'
|
||||
await component.getNextUpdatePromise()
|
||||
expect(component.canScrollHorizontally()).toBe(true)
|
||||
expect(component.canScrollVertically()).toBe(false)
|
||||
expect(element.offsetHeight).toBe(component.getContentHeight() + component.getHorizontalScrollbarHeight() + 2 * editorPadding)
|
||||
|
||||
// When a vertical scrollbar is visible, autoWidth accounts for it
|
||||
editor.update({autoWidth: true, autoHeight: false})
|
||||
await component.getNextUpdatePromise()
|
||||
element.style.height = component.getContentHeight() - 20
|
||||
await component.getNextUpdatePromise()
|
||||
expect(component.canScrollHorizontally()).toBe(false)
|
||||
expect(component.canScrollVertically()).toBe(true)
|
||||
expect(element.offsetWidth).toBe(
|
||||
component.getGutterContainerWidth() +
|
||||
component.getContentWidth() +
|
||||
component.getVerticalScrollbarWidth() +
|
||||
expect(element.offsetHeight).toBe(
|
||||
component.getContentHeight() +
|
||||
horizontalScrollbarHeight +
|
||||
2 * editorPadding
|
||||
)
|
||||
expect(element.offsetHeight).toBeGreaterThan(initialHeight)
|
||||
})
|
||||
|
||||
it('does not render the line number gutter at all if the isLineNumberGutterVisible parameter is false', () => {
|
||||
@@ -886,6 +818,18 @@ describe('TextEditorComponent', () => {
|
||||
expect(element.className).toBe('editor a b')
|
||||
})
|
||||
|
||||
it('does not blow away class names managed by the component when packages change the element class name', async () => {
|
||||
assertDocumentFocused()
|
||||
const {component, element, editor} = buildComponent({mini: true})
|
||||
element.classList.add('a', 'b')
|
||||
element.focus()
|
||||
await component.getNextUpdatePromise()
|
||||
expect(element.className).toBe('editor mini a b is-focused')
|
||||
element.className = 'a c d';
|
||||
await component.getNextUpdatePromise()
|
||||
expect(element.className).toBe('a c d editor is-focused mini')
|
||||
})
|
||||
|
||||
it('ignores resize events when the editor is hidden', async () => {
|
||||
const {component, element, editor} = buildComponent({autoHeight: false})
|
||||
element.style.height = 5 * component.getLineHeight() + 'px'
|
||||
@@ -1051,7 +995,6 @@ describe('TextEditorComponent', () => {
|
||||
it('does not render scrollbars', async () => {
|
||||
const {component, element, editor} = buildComponent({mini: true, autoHeight: false})
|
||||
await setEditorWidthInCharacters(component, 10)
|
||||
await setEditorHeightInLines(component, 1)
|
||||
|
||||
editor.setText('x'.repeat(20) + 'y'.repeat(20))
|
||||
await component.getNextUpdatePromise()
|
||||
@@ -1136,7 +1079,7 @@ describe('TextEditorComponent', () => {
|
||||
|
||||
describe('autoscroll', () => {
|
||||
it('automatically scrolls vertically when the requested range is within the vertical scroll margin of the top or bottom', async () => {
|
||||
const {component, editor} = buildComponent({height: 120})
|
||||
const {component, editor} = buildComponent({height: 120 + horizontalScrollbarHeight})
|
||||
expect(component.getLastVisibleRow()).toBe(7)
|
||||
|
||||
editor.scrollToScreenRange([[4, 0], [6, 0]])
|
||||
@@ -1158,7 +1101,7 @@ describe('TextEditorComponent', () => {
|
||||
|
||||
it('does not vertically autoscroll by more than half of the visible lines if the editor is shorter than twice the scroll margin', async () => {
|
||||
const {component, element, editor} = buildComponent({autoHeight: false})
|
||||
element.style.height = 5.5 * component.measurements.lineHeight + 'px'
|
||||
element.style.height = 5.5 * component.measurements.lineHeight + horizontalScrollbarHeight + 'px'
|
||||
await component.getNextUpdatePromise()
|
||||
expect(component.getLastVisibleRow()).toBe(5)
|
||||
const scrollMarginInLines = 2
|
||||
@@ -1188,8 +1131,8 @@ describe('TextEditorComponent', () => {
|
||||
await component.getNextUpdatePromise()
|
||||
|
||||
const actualScrollCenter = (component.getScrollTop() + component.getScrollBottom()) / 2
|
||||
const expectedScrollCenter = Math.round((4 + 7) / 2 * component.getLineHeight())
|
||||
expect(actualScrollCenter).toBe(expectedScrollCenter)
|
||||
const expectedScrollCenter = (4 + 7) / 2 * component.getLineHeight()
|
||||
expect(actualScrollCenter).toBeCloseTo(expectedScrollCenter, 0)
|
||||
})
|
||||
|
||||
it('automatically scrolls horizontally when the requested range is within the horizontal scroll margin of the right edge of the gutter or right edge of the scroll container', async () => {
|
||||
@@ -1202,29 +1145,27 @@ describe('TextEditorComponent', () => {
|
||||
|
||||
editor.scrollToScreenRange([[1, 12], [2, 28]])
|
||||
await component.getNextUpdatePromise()
|
||||
let expectedScrollLeft = Math.round(
|
||||
let expectedScrollLeft =
|
||||
clientLeftForCharacter(component, 1, 12) -
|
||||
lineNodeForScreenRow(component, 1).getBoundingClientRect().left -
|
||||
(editor.horizontalScrollMargin * component.measurements.baseCharacterWidth)
|
||||
)
|
||||
expect(component.getScrollLeft()).toBe(expectedScrollLeft)
|
||||
expect(component.getScrollLeft()).toBeCloseTo(expectedScrollLeft, 0)
|
||||
|
||||
editor.scrollToScreenRange([[1, 12], [2, 28]], {reversed: false})
|
||||
await component.getNextUpdatePromise()
|
||||
expectedScrollLeft = Math.round(
|
||||
expectedScrollLeft =
|
||||
component.getGutterContainerWidth() +
|
||||
clientLeftForCharacter(component, 2, 28) -
|
||||
lineNodeForScreenRow(component, 2).getBoundingClientRect().left +
|
||||
(editor.horizontalScrollMargin * component.measurements.baseCharacterWidth) -
|
||||
component.getScrollContainerClientWidth()
|
||||
)
|
||||
expect(component.getScrollLeft()).toBe(expectedScrollLeft)
|
||||
expect(component.getScrollLeft()).toBeCloseTo(expectedScrollLeft, 0)
|
||||
})
|
||||
|
||||
it('does not horizontally autoscroll by more than half of the visible "base-width" characters if the editor is narrower than twice the scroll margin', async () => {
|
||||
const {component, editor} = buildComponent({autoHeight: false})
|
||||
await setEditorWidthInCharacters(component, 1.5 * editor.horizontalScrollMargin)
|
||||
const editorWidthInChars = component.getScrollContainerWidth() / component.getBaseCharacterWidth()
|
||||
const editorWidthInChars = component.getScrollContainerClientWidth() / component.getBaseCharacterWidth()
|
||||
expect(Math.round(editorWidthInChars)).toBe(9)
|
||||
|
||||
editor.scrollToScreenRange([[6, 10], [6, 15]])
|
||||
@@ -1324,22 +1265,22 @@ describe('TextEditorComponent', () => {
|
||||
|
||||
// Assigns the scrollTop based on the logical position when attached
|
||||
jasmine.attachToDOM(element)
|
||||
expect(component.getScrollLeft()).toBe(Math.round(2 * component.getBaseCharacterWidth()))
|
||||
expect(component.getScrollLeft()).toBeCloseTo(2 * component.getBaseCharacterWidth(), 0)
|
||||
|
||||
// Allows the scrollTopRow to be updated while attached
|
||||
component.setScrollLeftColumn(4)
|
||||
expect(component.getScrollLeft()).toBe(Math.round(4 * component.getBaseCharacterWidth()))
|
||||
expect(component.getScrollLeft()).toBeCloseTo(4 * component.getBaseCharacterWidth(), 0)
|
||||
|
||||
// Preserves the scrollTopRow when detached
|
||||
element.remove()
|
||||
expect(component.getScrollLeft()).toBe(Math.round(4 * component.getBaseCharacterWidth()))
|
||||
expect(component.getScrollLeft()).toBeCloseTo(4 * component.getBaseCharacterWidth(), 0)
|
||||
|
||||
component.setScrollLeftColumn(6)
|
||||
expect(component.getScrollLeft()).toBe(Math.round(6 * component.getBaseCharacterWidth()))
|
||||
expect(component.getScrollLeft()).toBeCloseTo(6 * component.getBaseCharacterWidth(), 0)
|
||||
|
||||
jasmine.attachToDOM(element)
|
||||
element.style.width = '60px'
|
||||
expect(component.getScrollLeft()).toBe(Math.round(6 * component.getBaseCharacterWidth()))
|
||||
expect(component.getScrollLeft()).toBeCloseTo(6 * component.getBaseCharacterWidth(), 0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2123,7 +2064,8 @@ describe('TextEditorComponent', () => {
|
||||
|
||||
// render an editor that already contains some block decorations
|
||||
const {component, element} = buildComponent({editor, rowsPerTile: 3})
|
||||
await setEditorHeightInLines(component, 4)
|
||||
element.style.height = 4 * component.getLineHeight() + horizontalScrollbarHeight + 'px'
|
||||
await component.getNextUpdatePromise()
|
||||
expect(component.getRenderedStartRow()).toBe(0)
|
||||
expect(component.getRenderedEndRow()).toBe(9)
|
||||
expect(component.getScrollHeight()).toBe(
|
||||
@@ -2338,7 +2280,7 @@ describe('TextEditorComponent', () => {
|
||||
component.element.style.width = (
|
||||
component.getGutterContainerWidth() +
|
||||
component.getScrollContainerClientWidth() * 2 +
|
||||
component.getVerticalScrollbarWidth()
|
||||
verticalScrollbarWidth
|
||||
) + 'px'
|
||||
await component.getNextUpdatePromise()
|
||||
expect(component.getRenderedStartRow()).toBe(0)
|
||||
@@ -3568,12 +3510,12 @@ describe('TextEditorComponent', () => {
|
||||
describe('on the scrollbars', () => {
|
||||
it('delegates the mousedown events to the parent component unless the mousedown was on the actual scrollbar', async () => {
|
||||
const {component, element, editor} = buildComponent({height: 100})
|
||||
await setEditorWidthInCharacters(component, 8.5)
|
||||
await setEditorWidthInCharacters(component, 6)
|
||||
|
||||
const verticalScrollbar = component.refs.verticalScrollbar
|
||||
const horizontalScrollbar = component.refs.horizontalScrollbar
|
||||
const leftEdgeOfVerticalScrollbar = verticalScrollbar.element.getBoundingClientRect().right - getVerticalScrollbarWidth(component)
|
||||
const topEdgeOfHorizontalScrollbar = horizontalScrollbar.element.getBoundingClientRect().bottom - getHorizontalScrollbarHeight(component)
|
||||
const leftEdgeOfVerticalScrollbar = verticalScrollbar.element.getBoundingClientRect().right - verticalScrollbarWidth
|
||||
const topEdgeOfHorizontalScrollbar = horizontalScrollbar.element.getBoundingClientRect().bottom - horizontalScrollbarHeight
|
||||
|
||||
verticalScrollbar.didMouseDown({
|
||||
button: 0,
|
||||
@@ -4135,7 +4077,7 @@ describe('TextEditorComponent', () => {
|
||||
|
||||
it('assigns scrollTop on the component when calling setFirstVisibleScreenRow', async () => {
|
||||
const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false})
|
||||
element.style.height = 4 * component.measurements.lineHeight + 'px'
|
||||
element.style.height = 4 * component.measurements.lineHeight + horizontalScrollbarHeight + 'px'
|
||||
await component.getNextUpdatePromise()
|
||||
|
||||
expect(component.getMaxScrollTop() / component.getLineHeight()).toBe(9)
|
||||
@@ -4162,17 +4104,17 @@ describe('TextEditorComponent', () => {
|
||||
element.style.width = 30 * component.getBaseCharacterWidth() + 'px'
|
||||
await component.getNextUpdatePromise()
|
||||
expect(editor.getFirstVisibleScreenColumn()).toBe(0)
|
||||
expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(0 * component.getBaseCharacterWidth())
|
||||
expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(0)
|
||||
|
||||
setScrollLeft(component, 5.5 * component.getBaseCharacterWidth())
|
||||
expect(editor.getFirstVisibleScreenColumn()).toBe(5)
|
||||
await component.getNextUpdatePromise()
|
||||
expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(Math.round(5.5 * component.getBaseCharacterWidth()))
|
||||
expect(component.refs.horizontalScrollbar.element.scrollLeft).toBeCloseTo(5.5 * component.getBaseCharacterWidth(), -1)
|
||||
|
||||
editor.setFirstVisibleScreenColumn(12)
|
||||
expect(component.getScrollLeft()).toBe(Math.round(12 * component.getBaseCharacterWidth()))
|
||||
expect(component.getScrollLeft()).toBeCloseTo(12 * component.getBaseCharacterWidth(), -1)
|
||||
await component.getNextUpdatePromise()
|
||||
expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(Math.round(12 * component.getBaseCharacterWidth()))
|
||||
expect(component.refs.horizontalScrollbar.element.scrollLeft).toBeCloseTo(12 * component.getBaseCharacterWidth(), -1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4319,6 +4261,7 @@ async function setEditorWidthInCharacters (component, widthInCharacters) {
|
||||
component.element.style.width =
|
||||
component.getGutterContainerWidth() +
|
||||
widthInCharacters * component.measurements.baseCharacterWidth +
|
||||
verticalScrollbarWidth +
|
||||
'px'
|
||||
await component.getNextUpdatePromise()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('TextEditorElement', () => {
|
||||
jasmineContent = document.body.querySelector('#jasmine-content')
|
||||
// Force scrollbars to be visible regardless of local system configuration
|
||||
const scrollbarStyle = document.createElement('style')
|
||||
scrollbarStyle.textContent = '::-webkit-scrollbar { -webkit-appearance: none }'
|
||||
scrollbarStyle.textContent = 'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }'
|
||||
jasmine.attachToDOM(scrollbarStyle)
|
||||
})
|
||||
|
||||
@@ -338,18 +338,20 @@ describe('TextEditorElement', () => {
|
||||
element.style.width = '200px'
|
||||
jasmine.attachToDOM(element)
|
||||
|
||||
const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight()
|
||||
|
||||
expect(element.getMaxScrollTop()).toBe(0)
|
||||
await editor.update({autoHeight: false})
|
||||
|
||||
element.style.height = '100px'
|
||||
element.style.height = 100 + horizontalScrollbarHeight + 'px'
|
||||
await element.getNextUpdatePromise()
|
||||
expect(element.getMaxScrollTop()).toBe(60)
|
||||
|
||||
element.style.height = '120px'
|
||||
element.style.height = 120 + horizontalScrollbarHeight + 'px'
|
||||
await element.getNextUpdatePromise()
|
||||
expect(element.getMaxScrollTop()).toBe(40)
|
||||
|
||||
element.style.height = '200px'
|
||||
element.style.height = 200 + horizontalScrollbarHeight + 'px'
|
||||
await element.getNextUpdatePromise()
|
||||
expect(element.getMaxScrollTop()).toBe(0)
|
||||
})
|
||||
@@ -392,10 +394,13 @@ describe('TextEditorElement', () => {
|
||||
it('returns true if the given row range intersects the visible row range', async () => {
|
||||
const element = buildTextEditorElement()
|
||||
const editor = element.getModel()
|
||||
const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight()
|
||||
|
||||
editor.update({autoHeight: false})
|
||||
element.getModel().setText('x\n'.repeat(20))
|
||||
element.style.height = '120px'
|
||||
element.style.height = 120 + horizontalScrollbarHeight + 'px'
|
||||
await element.getNextUpdatePromise()
|
||||
|
||||
element.setScrollTop(80)
|
||||
await element.getNextUpdatePromise()
|
||||
expect(element.getVisibleRowRange()).toEqual([4, 11])
|
||||
@@ -412,9 +417,11 @@ describe('TextEditorElement', () => {
|
||||
it('returns a {top/left/width/height} object describing the rectangle between two screen positions, even if they are not on screen', async () => {
|
||||
const element = buildTextEditorElement()
|
||||
const editor = element.getModel()
|
||||
const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight()
|
||||
|
||||
editor.update({autoHeight: false})
|
||||
element.getModel().setText('xxxxxxxxxxxxxxxxxxxxxx\n'.repeat(20))
|
||||
element.style.height = '120px'
|
||||
element.style.height = 120 + horizontalScrollbarHeight + 'px'
|
||||
await element.getNextUpdatePromise()
|
||||
element.setScrollTop(80)
|
||||
await element.getNextUpdatePromise()
|
||||
|
||||
@@ -861,6 +861,15 @@ describe('TextEditor', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("clears the goal column", () => {
|
||||
editor.setText('first\n\nthird')
|
||||
editor.setCursorScreenPosition([0, 3])
|
||||
editor.moveDown()
|
||||
editor.moveToFirstCharacterOfLine()
|
||||
editor.moveDown()
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 0])
|
||||
})
|
||||
})
|
||||
|
||||
describe('.moveToBeginningOfWord()', () => {
|
||||
|
||||
@@ -213,6 +213,18 @@ describe('TooltipManager', () => {
|
||||
})
|
||||
)
|
||||
|
||||
describe('when a user types', () =>
|
||||
it('hides the tooltips', () => {
|
||||
const disposable = manager.add(element, { title: 'Title' })
|
||||
hover(element, function () {
|
||||
expect(document.body.querySelector('.tooltip')).not.toBeNull()
|
||||
window.dispatchEvent(new CustomEvent('keydown'))
|
||||
expect(document.body.querySelector('.tooltip')).toBeNull()
|
||||
disposable.dispose()
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
describe('findTooltips', () => {
|
||||
it('adds and remove tooltips correctly', () => {
|
||||
expect(manager.findTooltips(element).length).toBe(0)
|
||||
|
||||
@@ -1299,6 +1299,34 @@ describe('Workspace', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('the root-scope-used hook', () => {
|
||||
it('fires when opening a file or changing the grammar of an open file', async () => {
|
||||
await atom.packages.activatePackage('language-javascript')
|
||||
await atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
const observeTextEditorsSpy = jasmine.createSpy('observeTextEditors')
|
||||
const javascriptGrammarUsed = jasmine.createSpy('javascript')
|
||||
const coffeeScriptGrammarUsed = jasmine.createSpy('coffeescript')
|
||||
|
||||
atom.packages.triggerDeferredActivationHooks()
|
||||
atom.packages.onDidTriggerActivationHook('source.js:root-scope-used', () => {
|
||||
atom.workspace.observeTextEditors(observeTextEditorsSpy)
|
||||
javascriptGrammarUsed()
|
||||
})
|
||||
atom.packages.onDidTriggerActivationHook('source.coffee:root-scope-used', coffeeScriptGrammarUsed)
|
||||
|
||||
expect(javascriptGrammarUsed).not.toHaveBeenCalled()
|
||||
expect(observeTextEditorsSpy).not.toHaveBeenCalled()
|
||||
const editor = await atom.workspace.open('sample.js', {autoIndent: false})
|
||||
expect(javascriptGrammarUsed).toHaveBeenCalled()
|
||||
expect(observeTextEditorsSpy.callCount).toBe(1)
|
||||
|
||||
expect(coffeeScriptGrammarUsed).not.toHaveBeenCalled()
|
||||
atom.grammars.assignLanguageMode(editor, 'source.coffee')
|
||||
expect(coffeeScriptGrammarUsed).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('::reopenItem()', () => {
|
||||
it("opens the uri associated with the last closed pane that isn't currently open", () => {
|
||||
const pane = workspace.getActivePane()
|
||||
|
||||
@@ -487,21 +487,25 @@ class AtomEnvironment {
|
||||
|
||||
// Public: Gets the release channel of the Atom application.
|
||||
//
|
||||
// Returns the release channel as a {String}. Will return one of `dev`, `beta`, or `stable`.
|
||||
// Returns the release channel as a {String}. Will return a specific release channel
|
||||
// name like 'beta' or 'nightly' if one is found in the Atom version or 'stable'
|
||||
// otherwise.
|
||||
getReleaseChannel () {
|
||||
const version = this.getVersion()
|
||||
if (version.includes('beta')) {
|
||||
return 'beta'
|
||||
} else if (version.includes('dev')) {
|
||||
return 'dev'
|
||||
} else {
|
||||
return 'stable'
|
||||
// This matches stable, dev (with or without commit hash) and any other
|
||||
// release channel following the pattern '1.00.0-channel0'
|
||||
const match = this.getVersion().match(/\d+\.\d+\.\d+(-([a-z]+)(\d+|-\w{4,})?)?$/)
|
||||
if (!match) {
|
||||
return 'unrecognized'
|
||||
} else if (match[2]) {
|
||||
return match[2]
|
||||
}
|
||||
|
||||
return 'stable'
|
||||
}
|
||||
|
||||
// Public: Returns a {Boolean} that is `true` if the current version is an official release.
|
||||
isReleasedVersion () {
|
||||
return !/\w{7}/.test(this.getVersion()) // Check if the release is a 7-character SHA prefix
|
||||
return this.getReleaseChannel().match(/stable|beta|nightly/) != null
|
||||
}
|
||||
|
||||
// Public: Get the time taken to completely load the current window.
|
||||
|
||||
@@ -27,22 +27,36 @@ class CommandInstaller {
|
||||
}, () => {})
|
||||
}
|
||||
|
||||
this.installAtomCommand(true, error => {
|
||||
this.installAtomCommand(true, (error, atomCommandName) => {
|
||||
if (error) return showErrorDialog(error)
|
||||
this.installApmCommand(true, error => {
|
||||
this.installApmCommand(true, (error, apmCommandName) => {
|
||||
if (error) return showErrorDialog(error)
|
||||
this.applicationDelegate.confirm({
|
||||
message: 'Commands installed.',
|
||||
detail: 'The shell commands `atom` and `apm` are installed.'
|
||||
detail: `The shell commands \`${atomCommandName}\` and \`${apmCommandName}\` are installed.`
|
||||
}, () => {})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getCommandNameForChannel (commandName) {
|
||||
let channelMatch = this.appVersion.match(/beta|nightly/)
|
||||
let channel = channelMatch ? channelMatch[0] : ''
|
||||
|
||||
switch (channel) {
|
||||
case 'beta':
|
||||
return `${commandName}-beta`
|
||||
case 'nightly':
|
||||
return `${commandName}-nightly`
|
||||
default:
|
||||
return commandName
|
||||
}
|
||||
}
|
||||
|
||||
installAtomCommand (askForPrivilege, callback) {
|
||||
this.installCommand(
|
||||
path.join(this.getResourcesDirectory(), 'app', 'atom.sh'),
|
||||
this.appVersion.includes('beta') ? 'atom-beta' : 'atom',
|
||||
this.getCommandNameForChannel('atom'),
|
||||
askForPrivilege,
|
||||
callback
|
||||
)
|
||||
@@ -51,7 +65,7 @@ class CommandInstaller {
|
||||
installApmCommand (askForPrivilege, callback) {
|
||||
this.installCommand(
|
||||
path.join(this.getResourcesDirectory(), 'app', 'apm', 'node_modules', '.bin', 'apm'),
|
||||
this.appVersion.includes('beta') ? 'apm-beta' : 'apm',
|
||||
this.getCommandNameForChannel('apm'),
|
||||
askForPrivilege,
|
||||
callback
|
||||
)
|
||||
@@ -64,11 +78,11 @@ class CommandInstaller {
|
||||
|
||||
fs.readlink(destinationPath, (error, realpath) => {
|
||||
if (error && error.code !== 'ENOENT') return callback(error)
|
||||
if (realpath === commandPath) return callback()
|
||||
if (realpath === commandPath) return callback(null, commandName)
|
||||
this.createSymlink(fs, commandPath, destinationPath, error => {
|
||||
if (error && error.code === 'EACCES' && askForPrivilege) {
|
||||
const fsAdmin = require('fs-admin')
|
||||
this.createSymlink(fsAdmin, commandPath, destinationPath, callback)
|
||||
this.createSymlink(fsAdmin, commandPath, destinationPath, (error) => { callback(error, commandName) })
|
||||
} else {
|
||||
callback(error)
|
||||
}
|
||||
|
||||
@@ -355,6 +355,21 @@ const configSchema = {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Experimental: Use the new Tree-sitter parsing system for supported languages.'
|
||||
},
|
||||
colorProfile: {
|
||||
description: "Specify whether Atom should use the operating system's color profile (recommended) or an alternative color profile.<br>Changing this setting will require a relaunch of Atom to take effect.",
|
||||
type: 'string',
|
||||
default: 'default',
|
||||
enum: [
|
||||
{
|
||||
value: 'default',
|
||||
description: 'Use color profile configured in the operating system'
|
||||
},
|
||||
{
|
||||
value: 'srgb',
|
||||
description: 'Use sRGB color profile'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -326,7 +326,9 @@ class Cursor extends Model {
|
||||
|
||||
// Public: Moves the cursor to the bottom of the buffer.
|
||||
moveToBottom () {
|
||||
const column = this.goalColumn
|
||||
this.setBufferPosition(this.editor.getEofBufferPosition())
|
||||
this.goalColumn = column
|
||||
}
|
||||
|
||||
// Public: Moves the cursor to the beginning of the line.
|
||||
@@ -711,6 +713,7 @@ class Cursor extends Model {
|
||||
changePosition (options, fn) {
|
||||
this.clearSelection({autoscroll: false})
|
||||
fn()
|
||||
this.goalColumn = null
|
||||
const autoscroll = (options && options.autoscroll != null)
|
||||
? options.autoscroll
|
||||
: this.isLastCursor()
|
||||
|
||||
@@ -10,7 +10,6 @@ const Token = require('./token')
|
||||
const fs = require('fs-plus')
|
||||
const {Point, Range} = require('text-buffer')
|
||||
|
||||
const GRAMMAR_TYPE_BONUS = 1000
|
||||
const PATH_SPLIT_REGEX = new RegExp('[/.]')
|
||||
|
||||
// Extended: This class holds the grammars used for tokenizing.
|
||||
@@ -170,7 +169,7 @@ class GrammarRegistry {
|
||||
|
||||
languageModeForGrammarAndBuffer (grammar, buffer) {
|
||||
if (grammar instanceof TreeSitterGrammar) {
|
||||
return new TreeSitterLanguageMode({grammar, buffer, config: this.config})
|
||||
return new TreeSitterLanguageMode({grammar, buffer, config: this.config, grammars: this})
|
||||
} else {
|
||||
return new TextMateLanguageMode({grammar, buffer, config: this.config})
|
||||
}
|
||||
@@ -213,12 +212,21 @@ class GrammarRegistry {
|
||||
if (score > 0 && !grammar.bundledPackage) {
|
||||
score += 0.125
|
||||
}
|
||||
if (this.grammarMatchesContents(grammar, contents)) {
|
||||
score += 0.25
|
||||
}
|
||||
|
||||
if (score > 0 && this.isGrammarPreferredType(grammar)) {
|
||||
score += GRAMMAR_TYPE_BONUS
|
||||
if (grammar instanceof TreeSitterGrammar) {
|
||||
if (!this.config.get('core.useTreeSitterParsers')) return -Infinity
|
||||
|
||||
if (grammar.contentRegExp) {
|
||||
if (grammar.contentRegExp.test(contents)) {
|
||||
score += 0.25
|
||||
} else {
|
||||
score -= 0.25
|
||||
}
|
||||
}
|
||||
|
||||
if (score > 0) score += 0.5
|
||||
} else if (this.grammarMatchesPrefix(grammar, contents)) {
|
||||
score += 0.25
|
||||
}
|
||||
|
||||
return score
|
||||
@@ -256,12 +264,8 @@ class GrammarRegistry {
|
||||
return pathScore
|
||||
}
|
||||
|
||||
grammarMatchesContents (grammar, contents) {
|
||||
if (contents == null) return false
|
||||
|
||||
if (grammar.contentRegExp) { // TreeSitter grammars
|
||||
return grammar.contentRegExp.test(contents)
|
||||
} else if (grammar.firstLineRegex) { // FirstMate grammars
|
||||
grammarMatchesPrefix (grammar, contents) {
|
||||
if (contents && grammar.firstLineRegex) {
|
||||
let escaped = false
|
||||
let numberOfNewlinesInRegex = 0
|
||||
for (let character of grammar.firstLineRegex.source) {
|
||||
@@ -287,8 +291,9 @@ class GrammarRegistry {
|
||||
|
||||
forEachGrammar (callback) {
|
||||
this.textmateRegistry.grammars.forEach(callback)
|
||||
for (let grammarId in this.treeSitterGrammarsById) {
|
||||
callback(this.treeSitterGrammarsById[grammarId])
|
||||
for (const grammarId in this.treeSitterGrammarsById) {
|
||||
const grammar = this.treeSitterGrammarsById[grammarId]
|
||||
if (grammar.id) callback(grammar)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,26 +348,23 @@ class GrammarRegistry {
|
||||
|
||||
this.grammarScoresByBuffer.forEach((score, buffer) => {
|
||||
const languageMode = buffer.getLanguageMode()
|
||||
if (grammar.injectionSelector) {
|
||||
if (languageMode.hasTokenForSelector(grammar.injectionSelector)) {
|
||||
languageMode.retokenizeLines()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const languageOverride = this.languageOverridesByBufferId.get(buffer.id)
|
||||
|
||||
if ((grammar.id === buffer.getLanguageMode().getLanguageId() ||
|
||||
grammar.id === languageOverride)) {
|
||||
buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer))
|
||||
return
|
||||
} else if (!languageOverride) {
|
||||
const score = this.getGrammarScore(grammar, buffer.getPath(), getGrammarSelectionContent(buffer))
|
||||
const currentScore = this.grammarScoresByBuffer.get(buffer)
|
||||
if (currentScore == null || score > currentScore) {
|
||||
buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer))
|
||||
this.grammarScoresByBuffer.set(buffer, score)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
languageMode.updateForInjection(grammar)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -387,6 +389,32 @@ class GrammarRegistry {
|
||||
return this.textmateRegistry.onDidUpdateGrammar(callback)
|
||||
}
|
||||
|
||||
// Experimental: Specify a type of syntax node that may embed other languages.
|
||||
//
|
||||
// * `grammarId` The {String} id of the parent language
|
||||
// * `injectionPoint` An {Object} with the following keys:
|
||||
// * `type` The {String} type of syntax node that may embed other languages
|
||||
// * `language` A {Function} that is called with syntax nodes of the specified `type` and
|
||||
// returns a {String} that will be tested against other grammars' `injectionRegExp` in
|
||||
// order to determine what language should be embedded.
|
||||
// * `content` A {Function} that is called with syntax nodes of the specified `type` and
|
||||
// returns another syntax node or array of syntax nodes that contain the embedded source code.
|
||||
addInjectionPoint (grammarId, injectionPoint) {
|
||||
const grammar = this.treeSitterGrammarsById[grammarId]
|
||||
if (grammar) {
|
||||
grammar.injectionPoints.push(injectionPoint)
|
||||
} else {
|
||||
this.treeSitterGrammarsById[grammarId] = {
|
||||
injectionPoints: [injectionPoint]
|
||||
}
|
||||
}
|
||||
return new Disposable(() => {
|
||||
const grammar = this.treeSitterGrammarsById[grammarId]
|
||||
const index = grammar.injectionPoints.indexOf(injectionPoint)
|
||||
if (index !== -1) grammar.injectionPoints.splice(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
get nullGrammar () {
|
||||
return this.textmateRegistry.nullGrammar
|
||||
}
|
||||
@@ -405,12 +433,14 @@ class GrammarRegistry {
|
||||
|
||||
addGrammar (grammar) {
|
||||
if (grammar instanceof TreeSitterGrammar) {
|
||||
const existingParams = this.treeSitterGrammarsById[grammar.id] || {}
|
||||
this.treeSitterGrammarsById[grammar.id] = grammar
|
||||
if (grammar.legacyScopeName) {
|
||||
this.config.setLegacyScopeAliasForNewScope(grammar.id, grammar.legacyScopeName)
|
||||
this.textMateScopeNamesByTreeSitterLanguageId.set(grammar.id, grammar.legacyScopeName)
|
||||
this.treeSitterLanguageIdsByTextMateScopeName.set(grammar.legacyScopeName, grammar.id)
|
||||
}
|
||||
if (existingParams.injectionPoints) grammar.injectionPoints.push(...existingParams.injectionPoints)
|
||||
this.grammarAddedOrUpdated(grammar)
|
||||
return new Disposable(() => this.removeGrammar(grammar))
|
||||
} else {
|
||||
@@ -511,10 +541,13 @@ class GrammarRegistry {
|
||||
return this.textmateRegistry.scopeForId(id)
|
||||
}
|
||||
|
||||
isGrammarPreferredType (grammar) {
|
||||
return this.config.get('core.useTreeSitterParsers')
|
||||
? grammar instanceof TreeSitterGrammar
|
||||
: grammar instanceof FirstMate.Grammar
|
||||
treeSitterGrammarForLanguageString (languageString) {
|
||||
for (const id in this.treeSitterGrammarsById) {
|
||||
const grammar = this.treeSitterGrammarsById[id]
|
||||
if (grammar.injectionRegExp && grammar.injectionRegExp.test(languageString)) {
|
||||
return grammar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
normalizeLanguageId (languageId) {
|
||||
|
||||
@@ -36,6 +36,9 @@ if global.isGeneratingSnapshot
|
||||
require('image-view')
|
||||
require('incompatible-packages')
|
||||
require('keybinding-resolver')
|
||||
require('language-html')
|
||||
require('language-javascript')
|
||||
require('language-ruby')
|
||||
require('line-ending-selector')
|
||||
require('link')
|
||||
require('markdown-preview')
|
||||
|
||||
@@ -173,7 +173,8 @@ class AtomApplication extends EventEmitter {
|
||||
if (!this.configFilePromise) {
|
||||
this.configFilePromise = this.configFile.watch()
|
||||
this.disposable.add(await this.configFilePromise)
|
||||
this.config.onDidChange('core.titleBar', this.promptForRestart.bind(this))
|
||||
this.config.onDidChange('core.titleBar', () => this.promptForRestart())
|
||||
this.config.onDidChange('core.colorProfile', () => this.promptForRestart())
|
||||
}
|
||||
|
||||
const optionsForWindowsToOpen = []
|
||||
|
||||
@@ -12,13 +12,18 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
options.usage(
|
||||
dedent`Atom Editor v${version}
|
||||
|
||||
Usage: atom [options] [path ...]
|
||||
Usage:
|
||||
atom [options] [path ...]
|
||||
atom file[:line[:column]]
|
||||
|
||||
One or more paths to files or folders may be specified. If there is an
|
||||
existing Atom window that contains all of the given folders, the paths
|
||||
will be opened in that window. Otherwise, they will be opened in a new
|
||||
window.
|
||||
|
||||
A file may be opened at the desired line (and optionally column) by
|
||||
appending the numbers right after the file name, e.g. \`atom file:5:8\`.
|
||||
|
||||
Paths that start with \`atom://\` will be interpreted as URLs.
|
||||
|
||||
Environment Variables:
|
||||
|
||||
@@ -5,6 +5,9 @@ const temp = require('temp').track()
|
||||
const parseCommandLine = require('./parse-command-line')
|
||||
const startCrashReporter = require('../crash-reporter-start')
|
||||
const atomPaths = require('../atom-paths')
|
||||
const fs = require('fs')
|
||||
const CSON = require('season')
|
||||
const Config = require('../config')
|
||||
|
||||
module.exports = function start (resourcePath, startTime) {
|
||||
global.shellStartTime = startTime
|
||||
@@ -39,6 +42,12 @@ module.exports = function start (resourcePath, startTime) {
|
||||
atomPaths.setUserData(app)
|
||||
setupCompileCache()
|
||||
|
||||
const config = getConfig()
|
||||
const colorProfile = config.get('core.colorProfile')
|
||||
if (colorProfile && colorProfile !== 'default') {
|
||||
app.commandLine.appendSwitch('force-color-profile', colorProfile)
|
||||
}
|
||||
|
||||
if (handleStartupEventWithSquirrel()) {
|
||||
return
|
||||
} else if (args.test && args.mainProcess) {
|
||||
@@ -97,3 +106,21 @@ function setupCompileCache () {
|
||||
CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME)
|
||||
CompileCache.install(process.resourcesPath, require)
|
||||
}
|
||||
|
||||
function getConfig () {
|
||||
const config = new Config()
|
||||
|
||||
let configFilePath
|
||||
if (fs.existsSync(path.join(process.env.ATOM_HOME, 'config.json'))) {
|
||||
configFilePath = path.join(process.env.ATOM_HOME, 'config.json')
|
||||
} else if (fs.existsSync(path.join(process.env.ATOM_HOME, 'config.cson'))) {
|
||||
configFilePath = path.join(process.env.ATOM_HOME, 'config.cson')
|
||||
}
|
||||
|
||||
if (configFilePath) {
|
||||
const configFileData = CSON.readFileSync(configFilePath)
|
||||
config.resetUserSettings(configFileData)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
14
src/pane.js
@@ -614,15 +614,15 @@ class Pane {
|
||||
|
||||
if (this.items.includes(item)) return
|
||||
|
||||
const itemSubscriptions = new CompositeDisposable()
|
||||
this.subscriptionsPerItem.set(item, itemSubscriptions)
|
||||
if (typeof item.onDidDestroy === 'function') {
|
||||
const itemSubscriptions = new CompositeDisposable()
|
||||
itemSubscriptions.add(item.onDidDestroy(() => this.removeItem(item, false)))
|
||||
if (typeof item.onDidTerminatePendingState === 'function') {
|
||||
itemSubscriptions.add(item.onDidTerminatePendingState(() => {
|
||||
if (this.getPendingItem() === item) this.clearPendingItem()
|
||||
}))
|
||||
}
|
||||
this.subscriptionsPerItem.set(item, itemSubscriptions)
|
||||
}
|
||||
if (typeof item.onDidTerminatePendingState === 'function') {
|
||||
itemSubscriptions.add(item.onDidTerminatePendingState(() => {
|
||||
if (this.getPendingItem() === item) this.clearPendingItem()
|
||||
}))
|
||||
}
|
||||
|
||||
this.items.splice(index, 0, item)
|
||||
|
||||
@@ -234,6 +234,38 @@ class Project extends Model {
|
||||
return this.emitter.on('did-change-files', callback)
|
||||
}
|
||||
|
||||
// Public: Invoke the given callback with all current and future
|
||||
// repositories in the project.
|
||||
//
|
||||
// * `callback` {Function} to be called with current and future
|
||||
// repositories.
|
||||
// * `repository` A {GitRepository} that is present at the time of
|
||||
// subscription or that is added at some later time.
|
||||
//
|
||||
// Returns a {Disposable} on which `.dispose()` can be called to
|
||||
// unsubscribe.
|
||||
observeRepositories (callback) {
|
||||
for (const repo of this.repositories) {
|
||||
if (repo != null) {
|
||||
callback(repo)
|
||||
}
|
||||
}
|
||||
|
||||
return this.onDidAddRepository(callback)
|
||||
}
|
||||
|
||||
// Public: Invoke the given callback when a repository is added to the
|
||||
// project.
|
||||
//
|
||||
// * `callback` {Function} to be called when a repository is added.
|
||||
// * `repository` A {GitRepository}.
|
||||
//
|
||||
// Returns a {Disposable} on which `.dispose()` can be called to
|
||||
// unsubscribe.
|
||||
onDidAddRepository (callback) {
|
||||
return this.emitter.on('did-add-repository', callback)
|
||||
}
|
||||
|
||||
/*
|
||||
Section: Accessing the git repository
|
||||
*/
|
||||
@@ -400,6 +432,9 @@ class Project extends Model {
|
||||
if (repo) { break }
|
||||
}
|
||||
this.repositories.push(repo != null ? repo : null)
|
||||
if (repo != null) {
|
||||
this.emitter.emit('did-add-repository', repo)
|
||||
}
|
||||
|
||||
if (options.emitEvent !== false) {
|
||||
this.emitter.emit('did-change-paths', this.getPaths())
|
||||
@@ -685,9 +720,6 @@ class Project extends Model {
|
||||
}
|
||||
|
||||
this.grammarRegistry.autoAssignLanguageMode(buffer)
|
||||
if (buffer.languageMode.initialize) {
|
||||
await buffer.languageMode.initialize()
|
||||
}
|
||||
|
||||
this.addBuffer(buffer)
|
||||
return buffer
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
# Extended: Wraps an {Array} of `String`s. The Array describes a path from the
|
||||
# root of the syntax tree to a token including _all_ scope names for the entire
|
||||
# path.
|
||||
#
|
||||
# Methods that take a `ScopeDescriptor` will also accept an {Array} of {String}
|
||||
# scope names e.g. `['.source.js']`.
|
||||
#
|
||||
# You can use `ScopeDescriptor`s to get language-specific config settings via
|
||||
# {Config::get}.
|
||||
#
|
||||
# You should not need to create a `ScopeDescriptor` directly.
|
||||
#
|
||||
# * {TextEditor::getRootScopeDescriptor} to get the language's descriptor.
|
||||
# * {TextEditor::scopeDescriptorForBufferPosition} to get the descriptor at a
|
||||
# specific position in the buffer.
|
||||
# * {Cursor::getScopeDescriptor} to get a cursor's descriptor based on position.
|
||||
#
|
||||
# See the [scopes and scope descriptor guide](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/)
|
||||
# for more information.
|
||||
module.exports =
|
||||
class ScopeDescriptor
|
||||
@fromObject: (scopes) ->
|
||||
if scopes instanceof ScopeDescriptor
|
||||
scopes
|
||||
else
|
||||
new ScopeDescriptor({scopes})
|
||||
|
||||
###
|
||||
Section: Construction and Destruction
|
||||
###
|
||||
|
||||
# Public: Create a {ScopeDescriptor} object.
|
||||
#
|
||||
# * `object` {Object}
|
||||
# * `scopes` {Array} of {String}s
|
||||
constructor: ({@scopes}) ->
|
||||
|
||||
# Public: Returns an {Array} of {String}s
|
||||
getScopesArray: -> @scopes
|
||||
|
||||
getScopeChain: ->
|
||||
# For backward compatibility, prefix TextMate-style scope names with
|
||||
# leading dots (e.g. 'source.js' -> '.source.js').
|
||||
if @scopes[0]?.includes('.')
|
||||
result = ''
|
||||
for scope, i in @scopes
|
||||
result += ' ' if i > 0
|
||||
result += '.' if scope[0] isnt '.'
|
||||
result += scope
|
||||
result
|
||||
else
|
||||
@scopes.join(' ')
|
||||
|
||||
toString: ->
|
||||
@getScopeChain()
|
||||
|
||||
isEqual: (other) ->
|
||||
if @scopes.length isnt other.scopes.length
|
||||
return false
|
||||
for scope, i in @scopes
|
||||
if scope isnt other.scopes[i]
|
||||
return false
|
||||
true
|
||||
80
src/scope-descriptor.js
Normal file
@@ -0,0 +1,80 @@
|
||||
// Extended: Wraps an {Array} of `String`s. The Array describes a path from the
|
||||
// root of the syntax tree to a token including _all_ scope names for the entire
|
||||
// path.
|
||||
//
|
||||
// Methods that take a `ScopeDescriptor` will also accept an {Array} of {String}
|
||||
// scope names e.g. `['.source.js']`.
|
||||
//
|
||||
// You can use `ScopeDescriptor`s to get language-specific config settings via
|
||||
// {Config::get}.
|
||||
//
|
||||
// You should not need to create a `ScopeDescriptor` directly.
|
||||
//
|
||||
// * {TextEditor::getRootScopeDescriptor} to get the language's descriptor.
|
||||
// * {TextEditor::scopeDescriptorForBufferPosition} to get the descriptor at a
|
||||
// specific position in the buffer.
|
||||
// * {Cursor::getScopeDescriptor} to get a cursor's descriptor based on position.
|
||||
//
|
||||
// See the [scopes and scope descriptor guide](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/)
|
||||
// for more information.
|
||||
module.exports =
|
||||
class ScopeDescriptor {
|
||||
static fromObject (scopes) {
|
||||
if (scopes instanceof ScopeDescriptor) {
|
||||
return scopes
|
||||
} else {
|
||||
return new ScopeDescriptor({scopes})
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Section: Construction and Destruction
|
||||
*/
|
||||
|
||||
// Public: Create a {ScopeDescriptor} object.
|
||||
//
|
||||
// * `object` {Object}
|
||||
// * `scopes` {Array} of {String}s
|
||||
constructor ({scopes}) {
|
||||
this.scopes = scopes
|
||||
}
|
||||
|
||||
// Public: Returns an {Array} of {String}s
|
||||
getScopesArray () {
|
||||
return this.scopes
|
||||
}
|
||||
|
||||
getScopeChain () {
|
||||
// For backward compatibility, prefix TextMate-style scope names with
|
||||
// leading dots (e.g. 'source.js' -> '.source.js').
|
||||
if (this.scopes[0] != null && this.scopes[0].includes('.')) {
|
||||
let result = ''
|
||||
for (let i = 0; i < this.scopes.length; i++) {
|
||||
const scope = this.scopes[i]
|
||||
if (i > 0) { result += ' ' }
|
||||
if (scope[0] !== '.') { result += '.' }
|
||||
result += scope
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
return this.scopes.join(' ')
|
||||
}
|
||||
}
|
||||
|
||||
toString () {
|
||||
return this.getScopeChain()
|
||||
}
|
||||
|
||||
isEqual (other) {
|
||||
if (this.scopes.length !== other.scopes.length) {
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < this.scopes.length; i++) {
|
||||
const scope = this.scopes[i]
|
||||
if (scope !== other.scopes[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
38
src/selectors.js
Normal file
@@ -0,0 +1,38 @@
|
||||
module.exports = {selectorMatchesAnyScope, matcherForSelector}
|
||||
|
||||
const {isSubset} = require('underscore-plus')
|
||||
|
||||
// Private: Parse a selector into parts.
|
||||
// If already parsed, returns the selector unmodified.
|
||||
//
|
||||
// * `selector` a {String|Array<String>} specifying what to match
|
||||
// Returns selector parts, an {Array<String>}.
|
||||
function parse (selector) {
|
||||
return typeof selector === 'string'
|
||||
? selector.replace(/^\./, '').split('.')
|
||||
: selector
|
||||
}
|
||||
|
||||
const always = scope => true
|
||||
|
||||
// Essential: Return a matcher function for a selector.
|
||||
//
|
||||
// * selector, a {String} selector
|
||||
// Returns {(scope: String) -> Boolean}, a matcher function returning
|
||||
// true iff the scope matches the selector.
|
||||
function matcherForSelector (selector) {
|
||||
const parts = parse(selector)
|
||||
if (typeof parts === 'function') return parts
|
||||
return selector
|
||||
? scope => isSubset(parts, parse(scope))
|
||||
: always
|
||||
}
|
||||
|
||||
// Essential: Return true iff the selector matches any provided scope.
|
||||
//
|
||||
// * {String} selector
|
||||
// * {Array<String>} scopes
|
||||
// Returns {Boolean} true if any scope matches the selector.
|
||||
function selectorMatchesAnyScope (selector, scopes) {
|
||||
return !selector || scopes.some(matcherForSelector(selector))
|
||||
}
|
||||
9
src/test.ejs
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
|
||||
<% if something() { %>
|
||||
<div class=foo>
|
||||
<%= html `ok how about <span>this</span>` %>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
</html>
|
||||
@@ -458,15 +458,18 @@ class TextEditorComponent {
|
||||
let clientContainerWidth = '100%'
|
||||
if (this.hasInitialMeasurements) {
|
||||
if (model.getAutoHeight()) {
|
||||
clientContainerHeight = this.getContentHeight()
|
||||
if (this.canScrollHorizontally()) clientContainerHeight += this.getHorizontalScrollbarHeight()
|
||||
clientContainerHeight += 'px'
|
||||
clientContainerHeight =
|
||||
this.getContentHeight() +
|
||||
this.getHorizontalScrollbarHeight() +
|
||||
'px'
|
||||
}
|
||||
if (model.getAutoWidth()) {
|
||||
style.width = 'min-content'
|
||||
clientContainerWidth = this.getGutterContainerWidth() + this.getContentWidth()
|
||||
if (this.canScrollVertically()) clientContainerWidth += this.getVerticalScrollbarWidth()
|
||||
clientContainerWidth += 'px'
|
||||
clientContainerWidth =
|
||||
this.getGutterContainerWidth() +
|
||||
this.getContentWidth() +
|
||||
this.getVerticalScrollbarWidth() +
|
||||
'px'
|
||||
} else {
|
||||
style.width = this.element.style.width
|
||||
}
|
||||
@@ -751,20 +754,14 @@ class TextEditorComponent {
|
||||
scrollLeft = this.getScrollLeft()
|
||||
canScrollHorizontally = this.canScrollHorizontally()
|
||||
canScrollVertically = this.canScrollVertically()
|
||||
horizontalScrollbarHeight =
|
||||
canScrollHorizontally
|
||||
? this.getHorizontalScrollbarHeight()
|
||||
: 0
|
||||
verticalScrollbarWidth =
|
||||
canScrollVertically
|
||||
? this.getVerticalScrollbarWidth()
|
||||
: 0
|
||||
horizontalScrollbarHeight = this.getHorizontalScrollbarHeight()
|
||||
verticalScrollbarWidth = this.getVerticalScrollbarWidth()
|
||||
forceScrollbarVisible = this.remeasureScrollbars
|
||||
} else {
|
||||
forceScrollbarVisible = true
|
||||
}
|
||||
|
||||
const dummyScrollbarVnodes = [
|
||||
return [
|
||||
$(DummyScrollbarComponent, {
|
||||
ref: 'verticalScrollbar',
|
||||
orientation: 'vertical',
|
||||
@@ -786,13 +783,10 @@ class TextEditorComponent {
|
||||
scrollLeft,
|
||||
verticalScrollbarWidth,
|
||||
forceScrollbarVisible
|
||||
})
|
||||
]
|
||||
}),
|
||||
|
||||
// If both scrollbars are visible, push a dummy element to force a "corner"
|
||||
// to render where the two scrollbars meet at the lower right
|
||||
if (verticalScrollbarWidth > 0 && horizontalScrollbarHeight > 0) {
|
||||
dummyScrollbarVnodes.push($.div(
|
||||
// Force a "corner" to render where the two scrollbars meet at the lower right
|
||||
$.div(
|
||||
{
|
||||
ref: 'scrollbarCorner',
|
||||
className: 'scrollbar-corner',
|
||||
@@ -805,10 +799,8 @@ class TextEditorComponent {
|
||||
overflow: 'scroll'
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
return dummyScrollbarVnodes
|
||||
)
|
||||
]
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
@@ -856,10 +848,7 @@ class TextEditorComponent {
|
||||
}
|
||||
|
||||
for (let i = 0; i < newClassList.length; i++) {
|
||||
const className = newClassList[i]
|
||||
if (!oldClassList || !oldClassList.includes(className)) {
|
||||
this.element.classList.add(className)
|
||||
}
|
||||
this.element.classList.add(newClassList[i])
|
||||
}
|
||||
|
||||
this.classList = newClassList
|
||||
@@ -2626,37 +2615,25 @@ class TextEditorComponent {
|
||||
|
||||
getScrollContainerHeight () {
|
||||
if (this.props.model.getAutoHeight()) {
|
||||
return this.getScrollHeight()
|
||||
return this.getScrollHeight() + this.getHorizontalScrollbarHeight()
|
||||
} else {
|
||||
return this.getClientContainerHeight()
|
||||
}
|
||||
}
|
||||
|
||||
getScrollContainerClientWidth () {
|
||||
if (this.canScrollVertically()) {
|
||||
return this.getScrollContainerWidth() - this.getVerticalScrollbarWidth()
|
||||
} else {
|
||||
return this.getScrollContainerWidth()
|
||||
}
|
||||
return this.getScrollContainerWidth() - this.getVerticalScrollbarWidth()
|
||||
}
|
||||
|
||||
getScrollContainerClientHeight () {
|
||||
if (this.canScrollHorizontally()) {
|
||||
return this.getScrollContainerHeight() - this.getHorizontalScrollbarHeight()
|
||||
} else {
|
||||
return this.getScrollContainerHeight()
|
||||
}
|
||||
return this.getScrollContainerHeight() - this.getHorizontalScrollbarHeight()
|
||||
}
|
||||
|
||||
canScrollVertically () {
|
||||
const {model} = this.props
|
||||
if (model.isMini()) return false
|
||||
if (model.getAutoHeight()) return false
|
||||
if (this.getContentHeight() > this.getScrollContainerHeight()) return true
|
||||
return (
|
||||
this.getContentWidth() > this.getScrollContainerWidth() &&
|
||||
this.getContentHeight() > (this.getScrollContainerHeight() - this.getHorizontalScrollbarHeight())
|
||||
)
|
||||
return this.getContentHeight() > this.getScrollContainerClientHeight()
|
||||
}
|
||||
|
||||
canScrollHorizontally () {
|
||||
@@ -2664,11 +2641,7 @@ class TextEditorComponent {
|
||||
if (model.isMini()) return false
|
||||
if (model.getAutoWidth()) return false
|
||||
if (model.isSoftWrapped()) return false
|
||||
if (this.getContentWidth() > this.getScrollContainerWidth()) return true
|
||||
return (
|
||||
this.getContentHeight() > this.getScrollContainerHeight() &&
|
||||
this.getContentWidth() > (this.getScrollContainerWidth() - this.getVerticalScrollbarWidth())
|
||||
)
|
||||
return this.getContentWidth() > this.getScrollContainerClientWidth()
|
||||
}
|
||||
|
||||
getScrollHeight () {
|
||||
|
||||
@@ -7,6 +7,7 @@ const ScopeDescriptor = require('./scope-descriptor')
|
||||
const NullGrammar = require('./null-grammar')
|
||||
const {OnigRegExp} = require('oniguruma')
|
||||
const {toFirstMateScopeId, fromFirstMateScopeId} = require('./first-mate-helpers')
|
||||
const {selectorMatchesAnyScope} = require('./selectors')
|
||||
|
||||
const NON_WHITESPACE_REGEX = /\S/
|
||||
|
||||
@@ -235,15 +236,18 @@ class TextMateLanguageMode {
|
||||
return this.buffer.getTextInRange([[0, 0], [10, 0]])
|
||||
}
|
||||
|
||||
hasTokenForSelector (selector) {
|
||||
updateForInjection (grammar) {
|
||||
if (!grammar.injectionSelector) return
|
||||
for (const tokenizedLine of this.tokenizedLines) {
|
||||
if (tokenizedLine) {
|
||||
for (let token of tokenizedLine.tokens) {
|
||||
if (selector.matches(token.scopes)) return true
|
||||
if (grammar.injectionSelector.matches(token.scopes)) {
|
||||
this.retokenizeLines()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
retokenizeLines () {
|
||||
@@ -723,14 +727,6 @@ class TextMateLanguageMode {
|
||||
|
||||
TextMateLanguageMode.prototype.chunkSize = 50
|
||||
|
||||
function selectorMatchesAnyScope (selector, scopes) {
|
||||
const targetClasses = selector.replace(/^\./, '').split('.')
|
||||
return scopes.some((scope) => {
|
||||
const scopeClasses = scope.split('.')
|
||||
return _.isSubset(targetClasses, scopeClasses)
|
||||
})
|
||||
}
|
||||
|
||||
class TextMateHighlightIterator {
|
||||
constructor (languageMode) {
|
||||
this.languageMode = languageMode
|
||||
|
||||
@@ -153,9 +153,11 @@ class TooltipManager {
|
||||
}
|
||||
|
||||
window.addEventListener('resize', hideTooltip)
|
||||
window.addEventListener('keydown', hideTooltip)
|
||||
|
||||
const disposable = new Disposable(() => {
|
||||
window.removeEventListener('resize', hideTooltip)
|
||||
window.removeEventListener('keydown', hideTooltip)
|
||||
hideTooltip()
|
||||
tooltip.destroy()
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@ class TreeSitterGrammar {
|
||||
this.name = params.name
|
||||
this.legacyScopeName = params.legacyScopeName
|
||||
if (params.contentRegExp) this.contentRegExp = new RegExp(params.contentRegExp)
|
||||
if (params.injectionRegExp) this.injectionRegExp = new RegExp(params.injectionRegExp)
|
||||
|
||||
this.folds = params.folds || []
|
||||
this.folds.forEach(normalizeFoldSpecification)
|
||||
|
||||
this.commentStrings = {
|
||||
commentStartString: params.comments && params.comments.start,
|
||||
@@ -28,6 +30,7 @@ class TreeSitterGrammar {
|
||||
|
||||
this.scopeMap = new SyntaxScopeMap(scopeSelectors)
|
||||
this.fileTypes = params.fileTypes
|
||||
this.injectionPoints = params.injectionPoints || []
|
||||
|
||||
// TODO - When we upgrade to a new enough version of node, use `require.resolve`
|
||||
// with the new `paths` option instead of this private API.
|
||||
@@ -70,3 +73,36 @@ class TreeSitterGrammar {
|
||||
if (this.registration) this.registration.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
const NODE_NAME_REGEX = /[\w_]+/
|
||||
|
||||
function matcherForSpec (spec) {
|
||||
if (typeof spec === 'string') {
|
||||
if (spec[0] === '"' && spec[spec.length - 1] === '"') {
|
||||
return {
|
||||
type: spec.substr(1, spec.length - 2),
|
||||
named: false
|
||||
}
|
||||
}
|
||||
|
||||
if (!NODE_NAME_REGEX.test(spec)) {
|
||||
return {type: spec, named: false}
|
||||
}
|
||||
|
||||
return {type: spec, named: true}
|
||||
}
|
||||
return spec
|
||||
}
|
||||
|
||||
function normalizeFoldSpecification (spec) {
|
||||
if (spec.type) {
|
||||
if (Array.isArray(spec.type)) {
|
||||
spec.matchers = spec.type.map(matcherForSpec)
|
||||
} else {
|
||||
spec.matchers = [matcherForSpec(spec.type)]
|
||||
}
|
||||
}
|
||||
|
||||
if (spec.start) normalizeFoldSpecification(spec.start)
|
||||
if (spec.end) normalizeFoldSpecification(spec.end)
|
||||
}
|
||||
|
||||
@@ -1268,7 +1268,8 @@ module.exports = class Workspace extends Model {
|
||||
|
||||
handleGrammarUsed (grammar) {
|
||||
if (grammar == null) { return }
|
||||
return this.packageManager.triggerActivationHook(`${grammar.packageName}:grammar-used`)
|
||||
this.packageManager.triggerActivationHook(`${grammar.scopeName}:root-scope-used`)
|
||||
this.packageManager.triggerActivationHook(`${grammar.packageName}:grammar-used`)
|
||||
}
|
||||
|
||||
// Public: Returns a {Boolean} that is `true` if `object` is a `TextEditor`.
|
||||
|
||||