Merge remote-tracking branch 'upstream/master' into move-lines-up-and-down-with-multiple-selections
@@ -1,2 +1 @@
|
||||
spec/fixtures
|
||||
benchmark/fixtures
|
||||
|
||||
1
.gitignore
vendored
@@ -10,6 +10,7 @@ npm-debug.log
|
||||
debug.log
|
||||
/tags
|
||||
/atom-shell/
|
||||
/electron/
|
||||
docs/output
|
||||
docs/includes
|
||||
spec/fixtures/evil-files/
|
||||
|
||||
@@ -10,6 +10,8 @@ env:
|
||||
- ATOM_ACCESS_TOKEN=da809a6077bb1b0aa7c5623f7b2d5f1fec2faae4
|
||||
- NODE_VERSION=0.12
|
||||
|
||||
compiler: clang
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
|
||||
191
CONTRIBUTING.md
@@ -8,7 +8,17 @@ These are just guidelines, not rules, use your best judgment and feel free to
|
||||
propose changes to this document in a pull request.
|
||||
|
||||
This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior to atom@github.com.
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior to [atom@github.com](mailto:atom@github.com).
|
||||
|
||||
#### Table Of Contents
|
||||
|
||||
* [Submitting Issues](#submitting-issues)
|
||||
* [Pull Requests](#pull-requests)
|
||||
* [Git Commit Messages](#git-commit-messages)
|
||||
* [CoffeeScript Styleguide](#coffeescript-styleguide)
|
||||
* [Specs Styleguide](#specs-styleguide)
|
||||
* [Documentation Styleguide](#documentation-styleguide)
|
||||
* [Issue and Pull Request Labels](#issue-and-pull-request-labels)
|
||||
|
||||
## Submitting Issues
|
||||
|
||||
@@ -163,3 +173,182 @@ describe 'a dog', ->
|
||||
# Returns `undefined`.
|
||||
disablePackage: (name, options, callback) ->
|
||||
```
|
||||
|
||||
## Issue and Pull Request Labels
|
||||
|
||||
This section lists the labels we use to help us track and manage issues and pull requests. Most labels are used across all Atom repositories, but some are specific to `atom/atom`.
|
||||
|
||||
[GitHub search](https://help.github.com/articles/searching-issues/) makes it easy to use labels for finding groups of issues or pull requests you're interested in. For example, you might be interested in [open issues across `atom/atom` and all Atom-owned packages which are labeled as bugs, but still need to be reliably reproduced](https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Abug+label%3Aneeds-reproduction) or perhaps [open pull requests in `atom/atom` which haven't been reviewed yet](https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Apr+repo%3Aatom%2Fatom+comments%3A0). To help you find issues and pull requests, each label is listed with search links for finding open items with that label in `atom/atom` only and also across all Atom repositories. We encourage you to read about [other search filters](https://help.github.com/articles/searching-issues/) which will help you write more focused queries.
|
||||
|
||||
The labels are loosely grouped by their purpose, but it's not required that every issue have a label from every group or that an issue can't have more than one label from the same group.
|
||||
|
||||
Please open an issue on `atom/atom` if you have suggestions for new labels, and if you notice some labels are missing on some repositories, then please open an issue on that repository.
|
||||
|
||||
#### Type of Issue and Issue State
|
||||
|
||||
| Label name | `atom/atom` :mag_right: | `atom`‑org :mag_right: | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `enhancement` | [search][search-atom-repo-label-enhancement] | [search][search-atom-org-label-enhancement] | Feature requests. |
|
||||
| `bug` | [search][search-atom-repo-label-bug] | [search][search-atom-org-label-bug] | Confirmed bugs or reports that are very likely to be bugs. |
|
||||
| `question` | [search][search-atom-repo-label-question] | [search][search-atom-org-label-question] | Questions more than bug reports or feature requests (e.g. how do I do X). |
|
||||
| `feedback` | [search][search-atom-repo-label-feedback] | [search][search-atom-org-label-feedback] | General feedback more than bug reports or feature requests. |
|
||||
| `help-wanted` | [search][search-atom-repo-label-help-wanted] | [search][search-atom-org-label-help-wanted] | The Atom core team would appreciate help from the community in resolving these issues. |
|
||||
| `beginner` | [search][search-atom-repo-label-beginner] | [search][search-atom-org-label-beginner] | Less complex issues which would be good first issues to work on for users who want to contribute to Atom. |
|
||||
| `more-information-needed` | [search][search-atom-repo-label-more-information-needed] | [search][search-atom-org-label-more-information-needed] | More information needs to be collected about these problems or feature requests (e.g. steps to reproduce). |
|
||||
| `needs-reproduction` | [search][search-atom-repo-label-needs-reproduction] | [search][search-atom-org-label-needs-reproduction] | Likely bugs, but haven't been reliably reproduced. |
|
||||
| `blocked` | [search][search-atom-repo-label-blocked] | [search][search-atom-org-label-blocked] | Issues blocked on other issues. |
|
||||
| `duplicate` | [search][search-atom-repo-label-duplicate] | [search][search-atom-org-label-duplicate] | Issues which are duplicates of other issues, i.e. they have been reported before. |
|
||||
| `wontfix` | [search][search-atom-repo-label-wontfix] | [search][search-atom-org-label-wontfix] | The Atom core team has decided not to fix these issues for now, either because they're working as intended or for some other reason. |
|
||||
| `invalid` | [search][search-atom-repo-label-invalid] | [search][search-atom-org-label-invalid] | Issues which are't valid (e.g. user errors). |
|
||||
| `package-idea` | [search][search-atom-repo-label-package-idea] | [search][search-atom-org-label-package-idea] | Feature request which might be good candidates for new packages, instead of extending Atom or core Atom packages. |
|
||||
| `wrong-repo` | [search][search-atom-repo-label-wrong-repo] | [search][search-atom-org-label-wrong-repo] | Issues reported on the wrong repository (e.g. a bug related to the [Settings View package](https://github.com/atom/settings-view) was reported on [Atom core](https://github.com/atom/atom)). |
|
||||
|
||||
#### Topic Categories
|
||||
|
||||
| Label name | `atom/atom` :mag_right: | `atom`‑org :mag_right: | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `windows` | [search][search-atom-repo-label-windows] | [search][search-atom-org-label-windows] | Related to Atom running on Windows. |
|
||||
| `linux` | [search][search-atom-repo-label-linux] | [search][search-atom-org-label-linux] | Related to Atom running on Linux. |
|
||||
| `mac` | [search][search-atom-repo-label-mac] | [search][search-atom-org-label-mac] | Related to Atom running on OSX. |
|
||||
| `documentation` | [search][search-atom-repo-label-documentation] | [search][search-atom-org-label-documentation] | Related to any type of documentation (e.g. [API documentation](https://atom.io/docs/api/latest/Atom) and the [flight manual](https://atom.io/docs/latest/)). |
|
||||
| `performance` | [search][search-atom-repo-label-performance] | [search][search-atom-org-label-performance] | Related to performance. |
|
||||
| `security` | [search][search-atom-repo-label-security] | [search][search-atom-org-label-security] | Related to security. |
|
||||
| `ui` | [search][search-atom-repo-label-ui] | [search][search-atom-org-label-ui] | Related to visual design. |
|
||||
| `api` | [search][search-atom-repo-label-api] | [search][search-atom-org-label-api] | Related to Atom's public APIs. |
|
||||
| `uncaught-exception` | [search][search-atom-repo-label-uncaught-exception] | [search][search-atom-org-label-uncaught-exception] | Issues about uncaught exceptions, normally created from the [Notifications package](https://github.com/atom/notifications). |
|
||||
| `crash` | [search][search-atom-repo-label-crash] | [search][search-atom-org-label-crash] | Reports of Atom completely crashing. |
|
||||
| `auto-indent` | [search][search-atom-repo-label-auto-indent] | [search][search-atom-org-label-auto-indent] | Related to auto-indenting text. |
|
||||
| `encoding` | [search][search-atom-repo-label-encoding] | [search][search-atom-org-label-encoding] | Related to character encoding. |
|
||||
| `network` | [search][search-atom-repo-label-network] | [search][search-atom-org-label-network] | Related to network problems or working with remote files (e.g. on network drives). |
|
||||
| `git` | [search][search-atom-repo-label-git] | [search][search-atom-org-label-git] | Related to Git functionality (e.g. problems with gitignore files or with showing the correct file status). |
|
||||
|
||||
#### `atom/atom` Topic Categories
|
||||
|
||||
| Label name | `atom/atom` :mag_right: | `atom`‑org :mag_right: | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `editor-rendering` | [search][search-atom-repo-label-editor-rendering] | [search][search-atom-org-label-editor-rendering] | Related to language-independent aspects of rendering text (e.g. scrolling, soft wrap, and font rendering). |
|
||||
| `build-error` | [search][search-atom-repo-label-build-error] | [search][search-atom-org-label-build-error] | Related to problems with building Atom from source. |
|
||||
| `error-from-pathwatcher` | [search][search-atom-repo-label-error-from-pathwatcher] | [search][search-atom-org-label-error-from-pathwatcher] | Related to errors thrown by the [pathwatcher library](https://github.com/atom/node-pathwatcher). |
|
||||
| `error-from-save` | [search][search-atom-repo-label-error-from-save] | [search][search-atom-org-label-error-from-save] | Related to errors thrown when saving files. |
|
||||
| `error-from-open` | [search][search-atom-repo-label-error-from-open] | [search][search-atom-org-label-error-from-open] | Related to errors thrown when opening files. |
|
||||
| `installer` | [search][search-atom-repo-label-installer] | [search][search-atom-org-label-installer] | Related to the Atom installers for different OSes. |
|
||||
| `auto-updater` | [search][search-atom-repo-label-auto-updater] | [search][search-atom-org-label-auto-updater] | Related to the auto-updater for different OSes. |
|
||||
| `deprecation-help` | [search][search-atom-repo-label-deprecation-help] | [search][search-atom-org-label-deprecation-help] | Issues for helping package authors remove usage of deprecated APIs in packages. |
|
||||
| `electron` | [search][search-atom-repo-label-electron] | [search][search-atom-org-label-electron] | Issues that require changes to [Electron](https://electron.atom.io) to fix or implement. |
|
||||
|
||||
#### Core Team Project Management
|
||||
|
||||
| Label name | `atom/atom` :mag_right: | `atom`‑org :mag_right: | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `in-progress` | [search][search-atom-repo-label-in-progress] | [search][search-atom-org-label-in-progress] | Tasks which the Atom core team is working on currently. |
|
||||
| `on-deck` | [search][search-atom-repo-label-on-deck] | [search][search-atom-org-label-on-deck] | Tasks which the Atom core team plans to work on next. |
|
||||
| `shipping` | [search][search-atom-repo-label-shipping] | [search][search-atom-org-label-shipping] | Tasks which the Atom core team completed and will be released in one of the next releases. |
|
||||
| `post-1.0-roadmap` | [search][search-atom-repo-label-post-1.0-roadmap] | [search][search-atom-org-label-post-1.0-roadmap] | The Atom core team's roadmap post version 1.0.0. |
|
||||
| `atom` | [search][search-atom-repo-label-atom] | [search][search-atom-org-label-atom] | Topics discussed for prioritization at the next meeting of Atom core team members. |
|
||||
|
||||
#### Pull Request Labels
|
||||
|
||||
| Label name | `atom/atom` :mag_right: | `atom`‑org :mag_right: | Description
|
||||
| --- | --- | --- | --- |
|
||||
| `work-in-progress` | [search][search-atom-repo-label-work-in-progress] | [search][search-atom-org-label-work-in-progress] | Pull requests which are still being worked on, more changes will follow. |
|
||||
| `needs-review` | [search][search-atom-repo-label-needs-review] | [search][search-atom-org-label-needs-review] | Pull requests which need code review, and approval from maintainers or Atom core team. |
|
||||
| `under-review` | [search][search-atom-repo-label-under-review] | [search][search-atom-org-label-under-review] | Pull requests being reviewed by maintainers or Atom core team. |
|
||||
| `requires-changes` | [search][search-atom-repo-label-requires-changes] | [search][search-atom-org-label-requires-changes] | Pull requests which need to be updated based on review comments and then reviewed again. |
|
||||
| `needs-testing` | [search][search-atom-repo-label-needs-testing] | [search][search-atom-org-label-needs-testing] | Pull requests which need manual testing. |
|
||||
|
||||
[search-atom-repo-label-enhancement]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aenhancement
|
||||
[search-atom-org-label-enhancement]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aenhancement
|
||||
[search-atom-repo-label-bug]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Abug
|
||||
[search-atom-org-label-bug]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Abug
|
||||
[search-atom-repo-label-question]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aquestion
|
||||
[search-atom-org-label-question]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aquestion
|
||||
[search-atom-repo-label-feedback]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Afeedback
|
||||
[search-atom-org-label-feedback]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Afeedback
|
||||
[search-atom-repo-label-help-wanted]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Ahelp-wanted
|
||||
[search-atom-org-label-help-wanted]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Ahelp-wanted
|
||||
[search-atom-repo-label-beginner]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Abeginner
|
||||
[search-atom-org-label-beginner]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Abeginner
|
||||
[search-atom-repo-label-more-information-needed]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Amore-information-needed
|
||||
[search-atom-org-label-more-information-needed]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Amore-information-needed
|
||||
[search-atom-repo-label-needs-reproduction]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aneeds-reproduction
|
||||
[search-atom-org-label-needs-reproduction]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aneeds-reproduction
|
||||
[search-atom-repo-label-triage-help-needed]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Atriage-help-needed
|
||||
[search-atom-org-label-triage-help-needed]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Atriage-help-needed
|
||||
[search-atom-repo-label-windows]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Awindows
|
||||
[search-atom-org-label-windows]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Awindows
|
||||
[search-atom-repo-label-linux]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Alinux
|
||||
[search-atom-org-label-linux]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Alinux
|
||||
[search-atom-repo-label-mac]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Amac
|
||||
[search-atom-org-label-mac]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Amac
|
||||
[search-atom-repo-label-documentation]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Adocumentation
|
||||
[search-atom-org-label-documentation]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Adocumentation
|
||||
[search-atom-repo-label-performance]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aperformance
|
||||
[search-atom-org-label-performance]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aperformance
|
||||
[search-atom-repo-label-security]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Asecurity
|
||||
[search-atom-org-label-security]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Asecurity
|
||||
[search-atom-repo-label-ui]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aui
|
||||
[search-atom-org-label-ui]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aui
|
||||
[search-atom-repo-label-api]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aapi
|
||||
[search-atom-org-label-api]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aapi
|
||||
[search-atom-repo-label-crash]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Acrash
|
||||
[search-atom-org-label-crash]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Acrash
|
||||
[search-atom-repo-label-auto-indent]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aauto-indent
|
||||
[search-atom-org-label-auto-indent]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aauto-indent
|
||||
[search-atom-repo-label-encoding]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aencoding
|
||||
[search-atom-org-label-encoding]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aencoding
|
||||
[search-atom-repo-label-network]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Anetwork
|
||||
[search-atom-org-label-network]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Anetwork
|
||||
[search-atom-repo-label-uncaught-exception]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Auncaught-exception
|
||||
[search-atom-org-label-uncaught-exception]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Auncaught-exception
|
||||
[search-atom-repo-label-git]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Agit
|
||||
[search-atom-org-label-git]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Agit
|
||||
[search-atom-repo-label-blocked]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Ablocked
|
||||
[search-atom-org-label-blocked]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Ablocked
|
||||
[search-atom-repo-label-duplicate]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aduplicate
|
||||
[search-atom-org-label-duplicate]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aduplicate
|
||||
[search-atom-repo-label-wontfix]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Awontfix
|
||||
[search-atom-org-label-wontfix]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Awontfix
|
||||
[search-atom-repo-label-invalid]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Ainvalid
|
||||
[search-atom-org-label-invalid]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Ainvalid
|
||||
[search-atom-repo-label-package-idea]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Apackage-idea
|
||||
[search-atom-org-label-package-idea]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Apackage-idea
|
||||
[search-atom-repo-label-wrong-repo]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Awrong-repo
|
||||
[search-atom-org-label-wrong-repo]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Awrong-repo
|
||||
[search-atom-repo-label-editor-rendering]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aeditor-rendering
|
||||
[search-atom-org-label-editor-rendering]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aeditor-rendering
|
||||
[search-atom-repo-label-build-error]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Abuild-error
|
||||
[search-atom-org-label-build-error]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Abuild-error
|
||||
[search-atom-repo-label-error-from-pathwatcher]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aerror-from-pathwatcher
|
||||
[search-atom-org-label-error-from-pathwatcher]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aerror-from-pathwatcher
|
||||
[search-atom-repo-label-error-from-save]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aerror-from-save
|
||||
[search-atom-org-label-error-from-save]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aerror-from-save
|
||||
[search-atom-repo-label-error-from-open]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aerror-from-open
|
||||
[search-atom-org-label-error-from-open]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aerror-from-open
|
||||
[search-atom-repo-label-installer]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Ainstaller
|
||||
[search-atom-org-label-installer]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Ainstaller
|
||||
[search-atom-repo-label-auto-updater]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aauto-updater
|
||||
[search-atom-org-label-auto-updater]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aauto-updater
|
||||
[search-atom-repo-label-deprecation-help]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Adeprecation-help
|
||||
[search-atom-org-label-deprecation-help]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Adeprecation-help
|
||||
[search-atom-repo-label-electron]: https://github.com/issues?q=is%3Aissue+repo%3Aatom%2Fatom+is%3Aopen+label%3Aelectron
|
||||
[search-atom-org-label-electron]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aelectron
|
||||
[search-atom-repo-label-on-deck]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aon-deck
|
||||
[search-atom-org-label-on-deck]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aon-deck
|
||||
[search-atom-repo-label-in-progress]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Ain-progress
|
||||
[search-atom-org-label-in-progress]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Ain-progress
|
||||
[search-atom-repo-label-shipping]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Ashipping
|
||||
[search-atom-org-label-shipping]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Ashipping
|
||||
[search-atom-repo-label-post-1.0-roadmap]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Apost-1.0-roadmap
|
||||
[search-atom-org-label-post-1.0-roadmap]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Apost-1.0-roadmap
|
||||
[search-atom-repo-label-atom]: https://github.com/issues?q=is%3Aopen+is%3Aissue+repo%3Aatom%2Fatom+label%3Aatom
|
||||
[search-atom-org-label-atom]: https://github.com/issues?q=is%3Aopen+is%3Aissue+user%3Aatom+label%3Aatom
|
||||
[search-atom-repo-label-work-in-progress]: https://github.com/pulls?q=is%3Aopen+is%3Apr+repo%3Aatom%2Fatom+label%3Awork-in-progress
|
||||
[search-atom-org-label-work-in-progress]: https://github.com/pulls?q=is%3Aopen+is%3Apr+user%3Aatom+label%3Awork-in-progress
|
||||
[search-atom-repo-label-needs-review]: https://github.com/pulls?q=is%3Aopen+is%3Apr+repo%3Aatom%2Fatom+label%3Aneeds-review
|
||||
[search-atom-org-label-needs-review]: https://github.com/pulls?q=is%3Aopen+is%3Apr+user%3Aatom+label%3Aneeds-review
|
||||
[search-atom-repo-label-under-review]: https://github.com/pulls?q=is%3Aopen+is%3Apr+repo%3Aatom%2Fatom+label%3Aunder-review
|
||||
[search-atom-org-label-under-review]: https://github.com/pulls?q=is%3Aopen+is%3Apr+user%3Aatom+label%3Aunder-review
|
||||
[search-atom-repo-label-requires-changes]: https://github.com/pulls?q=is%3Aopen+is%3Apr+repo%3Aatom%2Fatom+label%3Arequires-changes
|
||||
[search-atom-org-label-requires-changes]: https://github.com/pulls?q=is%3Aopen+is%3Apr+user%3Aatom+label%3Arequires-changes
|
||||
[search-atom-repo-label-needs-testing]: https://github.com/pulls?q=is%3Aopen+is%3Apr+repo%3Aatom%2Fatom+label%3Aneeds-testing
|
||||
[search-atom-org-label-needs-testing]: https://github.com/pulls?q=is%3Aopen+is%3Apr+user%3Aatom+label%3Aneeds-testing
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "1.0.4"
|
||||
"atom-package-manager": "1.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
require '../src/window'
|
||||
Atom = require '../src/atom'
|
||||
window.atom = Atom.loadOrCreate('spec')
|
||||
atom.show() unless atom.getLoadSettings().exitWhenDone
|
||||
window.atom = atom
|
||||
|
||||
{runSpecSuite} = require '../spec/jasmine-helper'
|
||||
|
||||
atom.openDevTools()
|
||||
|
||||
document.title = "Benchmark Suite"
|
||||
runSpecSuite('../benchmark/benchmark-suite', atom.getLoadSettings().logFile)
|
||||
@@ -1,115 +0,0 @@
|
||||
require '../spec/spec-helper'
|
||||
|
||||
path = require 'path'
|
||||
{$} = require '../src/space-pen-extensions'
|
||||
{Point} = require 'atom'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
Project = require '../src/project'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
defaultCount = 100
|
||||
window.pbenchmark = (args...) -> window.benchmark(args..., profile: true)
|
||||
window.fbenchmark = (args...) -> window.benchmark(args..., focused: true)
|
||||
window.fpbenchmark = (args...) -> window.benchmark(args..., profile: true, focused: true)
|
||||
window.pfbenchmark = window.fpbenchmark
|
||||
|
||||
window.benchmarkFixturesProject = new Project(path.join(__dirname, 'fixtures'))
|
||||
|
||||
beforeEach ->
|
||||
window.project = window.benchmarkFixturesProject
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
jasmine.unspy(window, 'clearTimeout')
|
||||
jasmine.unspy(TokenizedBuffer::, 'tokenizeInBackground')
|
||||
|
||||
window.benchmark = (args...) ->
|
||||
description = args.shift()
|
||||
if typeof args[0] is 'number'
|
||||
count = args.shift()
|
||||
else
|
||||
count = defaultCount
|
||||
[fn, options] = args
|
||||
{profile, focused} = (options ? {})
|
||||
|
||||
method = if focused then fit else it
|
||||
method description, ->
|
||||
total = measure ->
|
||||
console.profile(description) if profile
|
||||
_.times count, fn
|
||||
console.profileEnd(description) if profile
|
||||
avg = total / count
|
||||
|
||||
fullname = @getFullName().replace(/\s|\.$/g, "")
|
||||
report = "#{fullname}: #{total} / #{count} = #{avg}ms"
|
||||
console.log(report)
|
||||
|
||||
if atom.getLoadSettings().exitWhenDone
|
||||
url = "https://github.com/_stats"
|
||||
data = [type: 'timing', metric: "atom.#{fullname}", ms: avg]
|
||||
$.ajax url,
|
||||
async: false
|
||||
data: JSON.stringify(data)
|
||||
error: (args...) ->
|
||||
console.log "Failed to send atom.#{fullname}\n#{JSON.stringify(args)}"
|
||||
|
||||
window.measure = (fn) ->
|
||||
start = new Date().getTime()
|
||||
fn()
|
||||
new Date().getTime() - start
|
||||
|
||||
window.waitsForPromise = (fn) ->
|
||||
window.waitsFor (moveOn) ->
|
||||
fn().done(moveOn)
|
||||
|
||||
window.keyIdentifierForKey = (key) ->
|
||||
if key.length > 1 # named key
|
||||
key
|
||||
else
|
||||
charCode = key.toUpperCase().charCodeAt(0)
|
||||
"U+00" + charCode.toString(16)
|
||||
|
||||
window.keydownEvent = (key, properties={}) ->
|
||||
$.Event "keydown", _.extend({originalEvent: {keyIdentifier: keyIdentifierForKey(key)}}, properties)
|
||||
|
||||
window.clickEvent = (properties={}) ->
|
||||
$.Event "click", properties
|
||||
|
||||
window.mouseEvent = (type, properties) ->
|
||||
if properties.point
|
||||
{point, editorView} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editorView, point)
|
||||
properties.pageX = left + 1
|
||||
properties.pageY = top + 1
|
||||
properties.originalEvent ?= {detail: 1}
|
||||
$.Event type, properties
|
||||
|
||||
window.mousedownEvent = (properties={}) ->
|
||||
window.mouseEvent('mousedown', properties)
|
||||
|
||||
window.mousemoveEvent = (properties={}) ->
|
||||
window.mouseEvent('mousemove', properties)
|
||||
|
||||
window.pagePixelPositionForPoint = (editorView, point) ->
|
||||
point = Point.fromObject point
|
||||
top = editorView.lines.offset().top + point.row * editorView.lineHeight
|
||||
left = editorView.lines.offset().left + point.column * editorView.charWidth - editorView.lines.scrollLeft()
|
||||
{top, left}
|
||||
|
||||
window.seteditorViewWidthInChars = (editorView, widthInChars, charWidth=editorView.charWidth) ->
|
||||
editorView.width(charWidth * widthInChars + editorView.lines.position().left)
|
||||
|
||||
$.fn.resultOfTrigger = (type) ->
|
||||
event = $.Event(type)
|
||||
this.trigger(event)
|
||||
event.result
|
||||
|
||||
$.fn.enableKeymap = ->
|
||||
@on 'keydown', (e) -> window.keymap.handleKeyEvent(e)
|
||||
|
||||
$.fn.attachToDom = ->
|
||||
$('#jasmine-content').append(this)
|
||||
|
||||
$.fn.textInput = (data) ->
|
||||
event = document.createEvent 'TextEvent'
|
||||
event.initTextEvent('textInput', true, true, window, data)
|
||||
this.each -> this.dispatchEvent(event)
|
||||
@@ -1,219 +0,0 @@
|
||||
require './benchmark-helper'
|
||||
{$} = require '../src/space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
{WorkspaceView} = require 'atom'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
describe "editorView.", ->
|
||||
editorView = null
|
||||
|
||||
beforeEach ->
|
||||
atom.workspaceViewParentSelector = '#jasmine-content'
|
||||
atom.workspaceView = atom.views.getView(atom.workspace).__spacePenView
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
atom.workspaceView.width(1024)
|
||||
atom.workspaceView.height(768)
|
||||
atom.workspaceView.openSync()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
|
||||
afterEach ->
|
||||
if editorView.pendingDisplayUpdate
|
||||
waitsFor "editor to finish rendering", (done) ->
|
||||
editorView.on 'editor:display-updated', done
|
||||
|
||||
describe "keymap.", ->
|
||||
event = null
|
||||
|
||||
beforeEach ->
|
||||
event = keydownEvent('x', target: editorView.hiddenInput[0])
|
||||
|
||||
benchmark "keydown-event-with-no-binding", 10, ->
|
||||
keymap.handleKeyEvent(event)
|
||||
|
||||
describe "opening-buffers.", ->
|
||||
benchmark "300-line-file.", ->
|
||||
buffer = project.bufferForPathSync('medium.coffee')
|
||||
|
||||
describe "empty-file.", ->
|
||||
benchmark "insert-delete", ->
|
||||
editorView.insertText('x')
|
||||
editorView.backspace()
|
||||
|
||||
describe "300-line-file.", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.openSync('medium.coffee')
|
||||
|
||||
describe "at-begining.", ->
|
||||
benchmark "insert-delete", ->
|
||||
editorView.insertText('x')
|
||||
editorView.backspace()
|
||||
|
||||
benchmark "insert-delete-rehighlight", ->
|
||||
editorView.insertText('"')
|
||||
editorView.backspace()
|
||||
|
||||
describe "at-end.", ->
|
||||
beforeEach ->
|
||||
editorView.moveToBottom()
|
||||
|
||||
benchmark "insert-delete", ->
|
||||
editorView.insertText('"')
|
||||
editorView.backspace()
|
||||
|
||||
describe "empty-vs-set-innerHTML.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editorView.getModel().getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getModel().getLastVisibleScreenRow()
|
||||
|
||||
benchmark "build-gutter-html.", 1000, ->
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
|
||||
benchmark "set-innerHTML.", 1000, ->
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editorView.gutter.lineNumbers[0].innerHtml = ''
|
||||
|
||||
benchmark "empty.", 1000, ->
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editorView.gutter.lineNumbers.empty()
|
||||
|
||||
describe "positionLeftForLineAndColumn.", ->
|
||||
line = null
|
||||
beforeEach ->
|
||||
editorView.scrollTop(2000)
|
||||
editorView.resetDisplay()
|
||||
line = editorView.lineElementForScreenRow(106)[0]
|
||||
|
||||
describe "one-line.", ->
|
||||
beforeEach ->
|
||||
editorView.clearCharacterWidthCache()
|
||||
|
||||
benchmark "uncached", 5000, ->
|
||||
editorView.positionLeftForLineAndColumn(line, 106, 82)
|
||||
editorView.clearCharacterWidthCache()
|
||||
|
||||
benchmark "cached", 5000, ->
|
||||
editorView.positionLeftForLineAndColumn(line, 106, 82)
|
||||
|
||||
describe "multiple-lines.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editorView.getModel().getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getModel().getLastVisibleScreenRow()
|
||||
|
||||
benchmark "cache-entire-visible-area", 100, ->
|
||||
for i in [firstRow..lastRow]
|
||||
line = editorView.lineElementForScreenRow(i)[0]
|
||||
editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.getModel().lineTextForBufferRow(i).length))
|
||||
|
||||
describe "text-rendering.", ->
|
||||
beforeEach ->
|
||||
editorView.scrollTop(2000)
|
||||
|
||||
benchmark "resetDisplay", 50, ->
|
||||
editorView.resetDisplay()
|
||||
|
||||
benchmark "htmlForScreenRows", 1000, ->
|
||||
lastRow = editorView.getLastScreenRow()
|
||||
editorView.htmlForScreenRows(0, lastRow)
|
||||
|
||||
benchmark "htmlForScreenRows.htmlParsing", 50, ->
|
||||
lastRow = editorView.getLastScreenRow()
|
||||
html = editorView.htmlForScreenRows(0, lastRow)
|
||||
|
||||
div = document.createElement('div')
|
||||
div.innerHTML = html
|
||||
|
||||
describe "gutter-api.", ->
|
||||
describe "getLineNumberElementsForClass.", ->
|
||||
beforeEach ->
|
||||
editorView.gutter.addClassToLine(20, 'omgwow')
|
||||
editorView.gutter.addClassToLine(40, 'omgwow')
|
||||
|
||||
benchmark "DOM", 20000, ->
|
||||
editorView.gutter.getLineNumberElementsForClass('omgwow')
|
||||
|
||||
benchmark "getLineNumberElement.DOM", 20000, ->
|
||||
editorView.gutter.getLineNumberElement(12)
|
||||
|
||||
benchmark "toggle-class", 2000, ->
|
||||
editorView.gutter.addClassToLine(40, 'omgwow')
|
||||
editorView.gutter.removeClassFromLine(40, 'omgwow')
|
||||
|
||||
describe "find-then-unset.", ->
|
||||
classes = ['one', 'two', 'three', 'four']
|
||||
|
||||
benchmark "single-class", 200, ->
|
||||
editorView.gutter.addClassToLine(30, 'omgwow')
|
||||
editorView.gutter.addClassToLine(40, 'omgwow')
|
||||
editorView.gutter.removeClassFromAllLines('omgwow')
|
||||
|
||||
benchmark "multiple-class", 200, ->
|
||||
editorView.gutter.addClassToLine(30, 'one')
|
||||
editorView.gutter.addClassToLine(30, 'two')
|
||||
|
||||
editorView.gutter.addClassToLine(40, 'two')
|
||||
editorView.gutter.addClassToLine(40, 'three')
|
||||
editorView.gutter.addClassToLine(40, 'four')
|
||||
|
||||
for klass in classes
|
||||
editorView.gutter.removeClassFromAllLines(klass)
|
||||
|
||||
describe "line-htmlification.", ->
|
||||
div = null
|
||||
html = null
|
||||
beforeEach ->
|
||||
lastRow = editorView.getLastScreenRow()
|
||||
html = editorView.htmlForScreenRows(0, lastRow)
|
||||
div = document.createElement('div')
|
||||
|
||||
benchmark "setInnerHTML", 1, ->
|
||||
div.innerHTML = html
|
||||
|
||||
describe "9000-line-file.", ->
|
||||
benchmark "opening.", 5, ->
|
||||
atom.workspaceView.openSync('huge.js')
|
||||
|
||||
describe "after-opening.", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.openSync('huge.js')
|
||||
|
||||
benchmark "moving-to-eof.", 1, ->
|
||||
editorView.moveToBottom()
|
||||
|
||||
describe "on-first-line.", ->
|
||||
benchmark "inserting-newline", 5, ->
|
||||
editorView.insertNewline()
|
||||
|
||||
describe "on-last-visible-line.", ->
|
||||
beforeEach ->
|
||||
editorView.setCursorScreenPosition([editorView.getLastVisibleScreenRow(), 0])
|
||||
|
||||
benchmark "move-down-and-scroll", 300, ->
|
||||
editorView.trigger 'move-down'
|
||||
|
||||
describe "at-eof.", ->
|
||||
endPosition = null
|
||||
|
||||
beforeEach ->
|
||||
editorView.moveToBottom()
|
||||
endPosition = editorView.getCursorScreenPosition()
|
||||
|
||||
benchmark "move-to-beginning-of-word", ->
|
||||
editorView.moveToBeginningOfWord()
|
||||
editorView.setCursorScreenPosition(endPosition)
|
||||
|
||||
benchmark "insert", ->
|
||||
editorView.insertText('x')
|
||||
|
||||
describe "TokenizedBuffer.", ->
|
||||
describe "coffee-script-grammar.", ->
|
||||
[languageMode, buffer] = []
|
||||
|
||||
beforeEach ->
|
||||
editor = benchmarkFixturesProject.openSync('medium.coffee')
|
||||
{languageMode, buffer} = editor
|
||||
|
||||
benchmark "construction", 20, ->
|
||||
new TokenizedBuffer(buffer, {languageMode, tabLength: 2})
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
{spawn, exec} = require 'child_process'
|
||||
fs = require 'fs'
|
||||
os = require 'os'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
temp = require 'temp'
|
||||
|
||||
directoryToOpen = temp.mkdirSync('browser-process-startup-')
|
||||
socketPath = path.join(os.tmpdir(), "atom-#{process.env.USER}.sock")
|
||||
numberOfRuns = 10
|
||||
|
||||
deleteSocketFile = ->
|
||||
try
|
||||
fs.unlinkSync(socketPath) if fs.existsSync(socketPath)
|
||||
catch error
|
||||
console.error(error)
|
||||
|
||||
launchAtom = (callback) ->
|
||||
deleteSocketFile()
|
||||
|
||||
cmd = 'atom'
|
||||
args = ['--safe', '--new-window', '--foreground', directoryToOpen]
|
||||
atomProcess = spawn(cmd, args)
|
||||
|
||||
output = ''
|
||||
startupTimes = []
|
||||
dataListener = (data) ->
|
||||
output += data
|
||||
if match = /App load time: (\d+)/.exec(output)
|
||||
startupTime = parseInt(match[1])
|
||||
atomProcess.stderr.removeListener 'data', dataListener
|
||||
atomProcess.kill()
|
||||
exec 'pkill -9 Atom', (error) ->
|
||||
console.error(error) if error?
|
||||
callback(startupTime)
|
||||
|
||||
atomProcess.stderr.on 'data', dataListener
|
||||
|
||||
startupTimes = []
|
||||
collector = (startupTime) ->
|
||||
startupTimes.push(startupTime)
|
||||
if startupTimes.length < numberOfRuns
|
||||
launchAtom(collector)
|
||||
else
|
||||
maxTime = _.max(startupTimes)
|
||||
minTime = _.min(startupTimes)
|
||||
totalTime = startupTimes.reduce (previousValue=0, currentValue) -> previousValue + currentValue
|
||||
console.log "Startup Runs: #{startupTimes.length}"
|
||||
console.log "First run time: #{startupTimes[0]}ms"
|
||||
console.log "Max time: #{maxTime}ms"
|
||||
console.log "Min time: #{minTime}ms"
|
||||
console.log "Average time: #{Math.round(totalTime/startupTimes.length)}ms"
|
||||
|
||||
launchAtom(collector)
|
||||
@@ -1,240 +0,0 @@
|
||||
# **Docco** is a quick-and-dirty, hundred-line-long, literate-programming-style
|
||||
# documentation generator. It produces HTML
|
||||
# that displays your comments alongside your code. Comments are passed through
|
||||
# [Markdown](http://daringfireball.net/projects/markdown/syntax), and code is
|
||||
# passed through [Pygments](http://pygments.org/) syntax highlighting.
|
||||
# This page is the result of running Docco against its own source file.
|
||||
#
|
||||
# If you install Docco, you can run it from the command-line:
|
||||
#
|
||||
# docco src/*.coffee
|
||||
#
|
||||
# ...will generate an HTML documentation page for each of the named source files,
|
||||
# with a menu linking to the other pages, saving it into a `docs` folder.
|
||||
#
|
||||
# The [source for Docco](http://github.com/jashkenas/docco) is available on GitHub,
|
||||
# and released under the MIT license.
|
||||
#
|
||||
# To install Docco, first make sure you have [Node.js](http://nodejs.org/),
|
||||
# [Pygments](http://pygments.org/) (install the latest dev version of Pygments
|
||||
# from [its Mercurial repo](http://dev.pocoo.org/hg/pygments-main)), and
|
||||
# [CoffeeScript](http://coffeescript.org/). Then, with NPM:
|
||||
#
|
||||
# sudo npm install -g docco
|
||||
#
|
||||
# Docco can be used to process CoffeeScript, JavaScript, Ruby, Python, or TeX files.
|
||||
# Only single-line comments are processed -- block comments are ignored.
|
||||
#
|
||||
#### Partners in Crime:
|
||||
#
|
||||
# * If **Node.js** doesn't run on your platform, or you'd prefer a more
|
||||
# convenient package, get [Ryan Tomayko](http://github.com/rtomayko)'s
|
||||
# [Rocco](http://rtomayko.github.com/rocco/rocco.html), the Ruby port that's
|
||||
# available as a gem.
|
||||
#
|
||||
# * If you're writing shell scripts, try
|
||||
# [Shocco](http://rtomayko.github.com/shocco/), a port for the **POSIX shell**,
|
||||
# also by Mr. Tomayko.
|
||||
#
|
||||
# * If Python's more your speed, take a look at
|
||||
# [Nick Fitzgerald](http://github.com/fitzgen)'s [Pycco](http://fitzgen.github.com/pycco/).
|
||||
#
|
||||
# * For **Clojure** fans, [Fogus](http://blog.fogus.me/)'s
|
||||
# [Marginalia](http://fogus.me/fun/marginalia/) is a bit of a departure from
|
||||
# "quick-and-dirty", but it'll get the job done.
|
||||
#
|
||||
# * **Lua** enthusiasts can get their fix with
|
||||
# [Robert Gieseke](https://github.com/rgieseke)'s [Locco](http://rgieseke.github.com/locco/).
|
||||
#
|
||||
# * And if you happen to be a **.NET**
|
||||
# aficionado, check out [Don Wilson](https://github.com/dontangg)'s
|
||||
# [Nocco](http://dontangg.github.com/nocco/).
|
||||
|
||||
#### Main Documentation Generation Functions
|
||||
|
||||
# Generate the documentation for a source file by reading it in, splitting it
|
||||
# up into comment/code sections, highlighting them for the appropriate language,
|
||||
# and merging them into an HTML template.
|
||||
generate_documentation = (source, callback) ->
|
||||
fs.readFile source, "utf-8", (error, code) ->
|
||||
throw error if error
|
||||
sections = parse source, code
|
||||
highlight source, sections, ->
|
||||
generate_html source, sections
|
||||
callback()
|
||||
|
||||
# Given a string of source code, parse out each comment and the code that
|
||||
# follows it, and create an individual **section** for it.
|
||||
# Sections take the form:
|
||||
#
|
||||
# {
|
||||
# docs_text: ...
|
||||
# docs_html: ...
|
||||
# code_text: ...
|
||||
# code_html: ...
|
||||
# }
|
||||
#
|
||||
parse = (source, code) ->
|
||||
lines = code.split '\n'
|
||||
sections = []
|
||||
language = get_language source
|
||||
has_code = docs_text = code_text = ''
|
||||
|
||||
save = (docs, code) ->
|
||||
sections.push docs_text: docs, code_text: code
|
||||
|
||||
for line in lines
|
||||
if line.match(language.comment_matcher) and not line.match(language.comment_filter)
|
||||
if has_code
|
||||
save docs_text, code_text
|
||||
has_code = docs_text = code_text = ''
|
||||
docs_text += line.replace(language.comment_matcher, '') + '\n'
|
||||
else
|
||||
has_code = yes
|
||||
code_text += line + '\n'
|
||||
save docs_text, code_text
|
||||
sections
|
||||
|
||||
# Highlights a single chunk of CoffeeScript code, using **Pygments** over stdio,
|
||||
# and runs the text of its corresponding comment through **Markdown**, using
|
||||
# [Showdown.js](http://attacklab.net/showdown/).
|
||||
#
|
||||
# We process the entire file in a single call to Pygments by inserting little
|
||||
# marker comments between each section and then splitting the result string
|
||||
# wherever our markers occur.
|
||||
highlight = (source, sections, callback) ->
|
||||
language = get_language source
|
||||
pygments = spawn 'pygmentize', ['-l', language.name, '-f', 'html', '-O', 'encoding=utf-8,tabsize=2']
|
||||
output = ''
|
||||
|
||||
pygments.stderr.addListener 'data', (error) ->
|
||||
console.error error.toString() if error
|
||||
|
||||
pygments.stdin.addListener 'error', (error) ->
|
||||
console.error "Could not use Pygments to highlight the source."
|
||||
process.exit 1
|
||||
|
||||
pygments.stdout.addListener 'data', (result) ->
|
||||
output += result if result
|
||||
|
||||
pygments.addListener 'exit', ->
|
||||
output = output.replace(highlight_start, '').replace(highlight_end, '')
|
||||
fragments = output.split language.divider_html
|
||||
for section, i in sections
|
||||
section.code_html = highlight_start + fragments[i] + highlight_end
|
||||
section.docs_html = showdown.makeHtml section.docs_text
|
||||
callback()
|
||||
|
||||
if pygments.stdin.writable
|
||||
pygments.stdin.write((section.code_text for section in sections).join(language.divider_text))
|
||||
pygments.stdin.end()
|
||||
|
||||
# Once all of the code is finished highlighting, we can generate the HTML file
|
||||
# and write out the documentation. Pass the completed sections into the template
|
||||
# found in `resources/docco.jst`
|
||||
generate_html = (source, sections) ->
|
||||
title = path.basename source
|
||||
dest = destination source
|
||||
html = docco_template {
|
||||
title: title, sections: sections, sources: sources, path: path, destination: destination
|
||||
}
|
||||
console.log "docco: #{source} -> #{dest}"
|
||||
fs.writeFile dest, html
|
||||
|
||||
#### Helpers & Setup
|
||||
|
||||
# Require our external dependencies, including **Showdown.js**
|
||||
# (the JavaScript implementation of Markdown).
|
||||
fs = require 'fs-utils'
|
||||
path = require 'path'
|
||||
showdown = require('./../vendor/showdown').Showdown
|
||||
{spawn, exec} = require 'child_process'
|
||||
|
||||
# A list of the languages that Docco supports, mapping the file extension to
|
||||
# the name of the Pygments lexer and the symbol that indicates a comment. To
|
||||
# add another language to Docco's repertoire, add it here.
|
||||
languages =
|
||||
'.coffee':
|
||||
name: 'coffee-script', symbol: '#'
|
||||
'.js':
|
||||
name: 'javascript', symbol: '//'
|
||||
'.rb':
|
||||
name: 'ruby', symbol: '#'
|
||||
'.py':
|
||||
name: 'python', symbol: '#'
|
||||
'.tex':
|
||||
name: 'tex', symbol: '%'
|
||||
'.latex':
|
||||
name: 'tex', symbol: '%'
|
||||
'.c':
|
||||
name: 'c', symbol: '//'
|
||||
'.h':
|
||||
name: 'c', symbol: '//'
|
||||
|
||||
# Build out the appropriate matchers and delimiters for each language.
|
||||
for ext, l of languages
|
||||
|
||||
# Does the line begin with a comment?
|
||||
l.comment_matcher = new RegExp('^\\s*' + l.symbol + '\\s?')
|
||||
|
||||
# Ignore [hashbangs](http://en.wikipedia.org/wiki/Shebang_(Unix\))
|
||||
# and interpolations...
|
||||
l.comment_filter = new RegExp('(^#![/]|^\\s*#\\{)')
|
||||
|
||||
# The dividing token we feed into Pygments, to delimit the boundaries between
|
||||
# sections.
|
||||
l.divider_text = '\n' + l.symbol + 'DIVIDER\n'
|
||||
|
||||
# The mirror of `divider_text` that we expect Pygments to return. We can split
|
||||
# on this to recover the original sections.
|
||||
# Note: the class is "c" for Python and "c1" for the other languages
|
||||
l.divider_html = new RegExp('\\n*<span class="c1?">' + l.symbol + 'DIVIDER<\\/span>\\n*')
|
||||
|
||||
# Get the current language we're documenting, based on the extension.
|
||||
get_language = (source) -> languages[path.extname(source)]
|
||||
|
||||
# Compute the destination HTML path for an input source file path. If the source
|
||||
# is `lib/example.coffee`, the HTML will be at `docs/example.html`
|
||||
destination = (filepath) ->
|
||||
'docs/' + path.basename(filepath, path.extname(filepath)) + '.html'
|
||||
|
||||
# Ensure that the destination directory exists.
|
||||
ensure_directory = (dir, callback) ->
|
||||
exec "mkdir -p #{dir}", -> callback()
|
||||
|
||||
# Micro-templating, originally by John Resig, borrowed by way of
|
||||
# [Underscore.js](http://documentcloud.github.com/underscore/).
|
||||
template = (str) ->
|
||||
new Function 'obj',
|
||||
'var p=[],print=function(){p.push.apply(p,arguments);};' +
|
||||
'with(obj){p.push(\'' +
|
||||
str.replace(/[\r\t\n]/g, " ")
|
||||
.replace(/'(?=[^<]*%>)/g,"\t")
|
||||
.split("'").join("\\'")
|
||||
.split("\t").join("'")
|
||||
.replace(/<%=(.+?)%>/g, "',$1,'")
|
||||
.split('<%').join("');")
|
||||
.split('%>').join("p.push('") +
|
||||
"');}return p.join('');"
|
||||
|
||||
# Create the template that we will use to generate the Docco HTML page.
|
||||
docco_template = template fs.readFileSync(__dirname + '/../resources/docco.jst').toString()
|
||||
|
||||
# The CSS styles we'd like to apply to the documentation.
|
||||
docco_styles = fs.readFileSync(__dirname + '/../resources/docco.css').toString()
|
||||
|
||||
# The start of each Pygments highlight block.
|
||||
highlight_start = '<div class="highlight"><pre>'
|
||||
|
||||
# The end of each Pygments highlight block.
|
||||
highlight_end = '</pre></div>'
|
||||
|
||||
# Run the script.
|
||||
# For each source file passed in as an argument, generate the documentation.
|
||||
sources = process.ARGV.sort()
|
||||
if sources.length
|
||||
ensure_directory 'docs', ->
|
||||
fs.writeFile 'docs/docco.css', docco_styles
|
||||
files = sources.slice(0)
|
||||
next_file = -> generate_documentation files.shift(), next_file if files.length
|
||||
next_file()
|
||||
@@ -23,7 +23,7 @@ module.exports = (grunt) ->
|
||||
grunt.loadNpmTasks('grunt-contrib-coffee')
|
||||
grunt.loadNpmTasks('grunt-contrib-less')
|
||||
grunt.loadNpmTasks('grunt-shell')
|
||||
grunt.loadNpmTasks('grunt-download-atom-shell')
|
||||
grunt.loadNpmTasks('grunt-download-electron')
|
||||
grunt.loadNpmTasks('grunt-electron-installer')
|
||||
grunt.loadNpmTasks('grunt-peg')
|
||||
grunt.loadTasks('tasks')
|
||||
@@ -31,19 +31,18 @@ module.exports = (grunt) ->
|
||||
# This allows all subsequent paths to the relative to the root of the repo
|
||||
grunt.file.setBase(path.resolve('..'))
|
||||
|
||||
if not grunt.option('verbose')
|
||||
grunt.log.writeln = (args...) -> grunt.log
|
||||
grunt.log.write = (args...) -> grunt.log
|
||||
|
||||
[major, minor, patch] = packageJson.version.split('.')
|
||||
tmpDir = os.tmpdir()
|
||||
appName = if process.platform is 'darwin' then 'Atom.app' else 'Atom'
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
buildDir = path.resolve(buildDir)
|
||||
installDir = grunt.option('install-dir')
|
||||
|
||||
channel = grunt.option('channel')
|
||||
channel ?= process.env.JANKY_BRANCH if process.env.JANKY_BRANCH in ['stable', 'beta']
|
||||
channel ?= 'dev'
|
||||
|
||||
home = if process.platform is 'win32' then process.env.USERPROFILE else process.env.HOME
|
||||
atomShellDownloadDir = path.join(home, '.atom', 'atom-shell')
|
||||
electronDownloadDir = path.join(home, '.atom', 'electron')
|
||||
|
||||
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
@@ -100,7 +99,6 @@ module.exports = (grunt) ->
|
||||
prebuildLessConfig =
|
||||
src: [
|
||||
'static/**/*.less'
|
||||
'node_modules/atom-space-pen-views/stylesheets/**/*.less'
|
||||
]
|
||||
|
||||
csonConfig =
|
||||
@@ -157,7 +155,7 @@ module.exports = (grunt) ->
|
||||
grunt.initConfig
|
||||
pkg: grunt.file.readJSON('package.json')
|
||||
|
||||
atom: {appDir, appName, symbolsDir, buildDir, contentsDir, installDir, shellAppDir}
|
||||
atom: {appDir, appName, symbolsDir, buildDir, contentsDir, installDir, shellAppDir, channel}
|
||||
|
||||
docsOutputDir: 'docs/output'
|
||||
|
||||
@@ -225,11 +223,11 @@ module.exports = (grunt) ->
|
||||
'static/**/*.less'
|
||||
]
|
||||
|
||||
'download-atom-shell':
|
||||
version: packageJson.atomShellVersion
|
||||
outputDir: 'atom-shell'
|
||||
downloadDir: atomShellDownloadDir
|
||||
rebuild: true # rebuild native modules after atom-shell is updated
|
||||
'download-electron':
|
||||
version: packageJson.electronVersion
|
||||
outputDir: 'electron'
|
||||
downloadDir: electronDownloadDir
|
||||
rebuild: true # rebuild native modules after electron is updated
|
||||
token: process.env.ATOM_ACCESS_TOKEN
|
||||
|
||||
'create-windows-installer':
|
||||
@@ -238,8 +236,8 @@ module.exports = (grunt) ->
|
||||
outputDirectory: path.join(buildDir, 'installer')
|
||||
authors: 'GitHub Inc.'
|
||||
loadingGif: path.resolve(__dirname, '..', 'resources', 'win', 'loading.gif')
|
||||
iconUrl: 'https://raw.githubusercontent.com/atom/atom/master/resources/win/atom.ico'
|
||||
setupIcon: path.resolve(__dirname, '..', 'resources', 'win', 'atom.ico')
|
||||
iconUrl: 'https://raw.githubusercontent.com/atom/atom/master/resources/app-icons/stable/atom.ico'
|
||||
setupIcon: path.resolve(__dirname, '..', 'resources', 'app-icons', 'stable', 'atom.ico')
|
||||
remoteReleases: 'https://atom.io/api/updates'
|
||||
|
||||
shell:
|
||||
@@ -254,7 +252,7 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask('lint', ['standard', 'coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
|
||||
|
||||
ciTasks = ['output-disk-space', 'download-atom-shell', 'download-atom-shell-chromedriver', 'build']
|
||||
ciTasks = ['output-disk-space', 'download-electron', 'download-electron-chromedriver', 'build']
|
||||
ciTasks.push('dump-symbols') if process.platform isnt 'win32'
|
||||
ciTasks.push('set-version', 'check-licenses', 'lint', 'generate-asar')
|
||||
ciTasks.push('mkdeb') if process.platform is 'linux'
|
||||
@@ -266,6 +264,7 @@ module.exports = (grunt) ->
|
||||
ciTasks.push('publish-build') unless process.env.TRAVIS
|
||||
grunt.registerTask('ci', ciTasks)
|
||||
|
||||
defaultTasks = ['download-atom-shell', 'download-atom-shell-chromedriver', 'build', 'set-version', 'generate-asar']
|
||||
defaultTasks.push 'install' unless process.platform is 'linux'
|
||||
defaultTasks = ['download-electron', 'download-electron-chromedriver', 'build', 'set-version', 'generate-asar']
|
||||
unless process.platform is 'linux' or grunt.option('no-install')
|
||||
defaultTasks.push 'install'
|
||||
grunt.registerTask('default', defaultTasks)
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"asar": "^0.5.0",
|
||||
"asar": "^0.7.1",
|
||||
"async": "~0.2.9",
|
||||
"donna": "1.0.10",
|
||||
"formidable": "~1.0.14",
|
||||
"fs-plus": "2.x",
|
||||
"github-releases": "~0.2.0",
|
||||
"github-releases": "~0.3.0",
|
||||
"glob": "^5.0.14",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-babel": "^5.0.1",
|
||||
@@ -20,9 +20,9 @@
|
||||
"grunt-contrib-coffee": "~0.12.0",
|
||||
"grunt-contrib-csslint": "~0.2.0",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"grunt-cson": "0.14.0",
|
||||
"grunt-download-atom-shell": "~0.15.1",
|
||||
"grunt-electron-installer": "1.0.0",
|
||||
"grunt-cson": "0.16.0",
|
||||
"grunt-download-electron": "^2.1.1",
|
||||
"grunt-electron-installer": "1.0.3-0",
|
||||
"grunt-lesslint": "0.17.0",
|
||||
"grunt-peg": "~1.1.0",
|
||||
"grunt-shell": "~0.3.1",
|
||||
|
||||
@@ -15,9 +15,17 @@ module.exports = (grunt) ->
|
||||
mkdir path.dirname(buildDir)
|
||||
|
||||
if process.platform is 'darwin'
|
||||
cp 'atom-shell/Atom.app', shellAppDir, filter: /default_app/
|
||||
cp 'electron/Electron.app', shellAppDir, filter: /default_app/
|
||||
fs.renameSync path.join(shellAppDir, 'Contents', 'MacOS', 'Electron'), path.join(shellAppDir, 'Contents', 'MacOS', 'Atom')
|
||||
fs.renameSync path.join(shellAppDir, 'Contents', 'Frameworks', 'Electron Helper.app'), path.join(shellAppDir, 'Contents', 'Frameworks', 'Atom Helper.app')
|
||||
fs.renameSync path.join(shellAppDir, 'Contents', 'Frameworks', 'Atom Helper.app', 'Contents', 'MacOS', 'Electron Helper'), path.join(shellAppDir, 'Contents', 'Frameworks', 'Atom Helper.app', 'Contents', 'MacOS', 'Atom Helper')
|
||||
else
|
||||
cp 'atom-shell', shellAppDir, filter: /default_app/
|
||||
cp 'electron', shellAppDir, filter: /default_app/
|
||||
|
||||
if process.platform is 'win32'
|
||||
fs.renameSync path.join(shellAppDir, 'electron.exe'), path.join(shellAppDir, 'atom.exe')
|
||||
else
|
||||
fs.renameSync path.join(shellAppDir, 'electron'), path.join(shellAppDir, 'atom')
|
||||
|
||||
mkdir appDir
|
||||
|
||||
@@ -29,10 +37,8 @@ module.exports = (grunt) ->
|
||||
packageNames = []
|
||||
packageDirectories = []
|
||||
nonPackageDirectories = [
|
||||
'benchmark'
|
||||
'dot-atom'
|
||||
'vendor'
|
||||
'resources'
|
||||
]
|
||||
|
||||
{devDependencies} = grunt.file.readJSON('package.json')
|
||||
@@ -78,9 +84,6 @@ module.exports = (grunt) ->
|
||||
path.join('build', 'Release', 'obj')
|
||||
path.join('build', 'Release', '.deps')
|
||||
path.join('vendor', 'apm')
|
||||
path.join('resources', 'linux')
|
||||
path.join('resources', 'mac')
|
||||
path.join('resources', 'win')
|
||||
|
||||
# These are only require in dev mode when the grammar isn't precompiled
|
||||
path.join('snippets', 'node_modules', 'loophole')
|
||||
@@ -142,19 +145,14 @@ module.exports = (grunt) ->
|
||||
|
||||
testFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}_*te?sts?_*#{_.escapeRegExp(path.sep)}")
|
||||
exampleFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}examples?#{_.escapeRegExp(path.sep)}")
|
||||
benchmarkFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}benchmarks?#{_.escapeRegExp(path.sep)}")
|
||||
|
||||
nodeModulesFilter = new RegExp(ignoredPaths.join('|'))
|
||||
filterNodeModule = (pathToCopy) ->
|
||||
return true if benchmarkFolderPattern.test(pathToCopy)
|
||||
|
||||
pathToCopy = path.resolve(pathToCopy)
|
||||
nodeModulesFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy) or exampleFolderPattern.test(pathToCopy)
|
||||
|
||||
packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(cson|coffee)$)")
|
||||
filterPackage = (pathToCopy) ->
|
||||
return true if benchmarkFolderPattern.test(pathToCopy)
|
||||
|
||||
pathToCopy = path.resolve(pathToCopy)
|
||||
packageFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy) or exampleFolderPattern.test(pathToCopy)
|
||||
|
||||
@@ -171,10 +169,14 @@ module.exports = (grunt) ->
|
||||
if process.platform isnt 'win32'
|
||||
fs.symlinkSync(path.join('..', '..', 'bin', 'apm'), path.resolve(appDir, '..', 'new-app', 'apm', 'node_modules', '.bin', 'apm'))
|
||||
|
||||
channel = grunt.config.get('atom.channel')
|
||||
|
||||
cp path.join('resources', 'app-icons', channel, 'png', '1024.png'), path.join(appDir, 'resources', 'atom.png')
|
||||
|
||||
if process.platform is 'darwin'
|
||||
grunt.file.recurse path.join('resources', 'mac'), (sourcePath, rootDirectory, subDirectory='', filename) ->
|
||||
unless /.+\.plist/.test(sourcePath)
|
||||
grunt.file.copy(sourcePath, path.resolve(appDir, '..', subDirectory, filename))
|
||||
cp path.join('resources', 'app-icons', channel, 'atom.icns'), path.resolve(appDir, '..', 'atom.icns')
|
||||
cp path.join('resources', 'mac', 'file.icns'), path.resolve(appDir, '..', 'file.icns')
|
||||
cp path.join('resources', 'mac', 'speakeasy.pem'), path.resolve(appDir, '..', 'speakeasy.pem')
|
||||
|
||||
if process.platform is 'win32'
|
||||
cp path.join('resources', 'win', 'atom.cmd'), path.join(shellAppDir, 'resources', 'cli', 'atom.cmd')
|
||||
@@ -183,7 +185,7 @@ module.exports = (grunt) ->
|
||||
cp path.join('resources', 'win', 'apm.sh'), path.join(shellAppDir, 'resources', 'cli', 'apm.sh')
|
||||
|
||||
if process.platform is 'linux'
|
||||
cp path.join('resources', 'linux', 'icons'), path.join(buildDir, 'icons')
|
||||
cp path.join('resources', 'app-icons', channel, 'png'), path.join(buildDir, 'icons')
|
||||
|
||||
dependencies = ['compile', 'generate-license:save', 'generate-module-cache', 'compile-packages-slug']
|
||||
dependencies.push('copy-info-plist') if process.platform is 'darwin'
|
||||
|
||||
@@ -12,6 +12,7 @@ module.exports = (grunt) ->
|
||||
rm require('../src/less-compile-cache').cacheDir
|
||||
rm path.join(tmpdir, 'atom-cached-atom-shells')
|
||||
rm 'atom-shell'
|
||||
rm 'electron'
|
||||
|
||||
grunt.registerTask 'clean', 'Delete all the build files', ->
|
||||
homeDir = process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME']
|
||||
|
||||
@@ -6,17 +6,12 @@ _ = require 'underscore-plus'
|
||||
donna = require 'donna'
|
||||
tello = require 'tello'
|
||||
|
||||
moduleBlacklist = [
|
||||
'space-pen'
|
||||
]
|
||||
|
||||
module.exports = (grunt) ->
|
||||
getClassesToInclude = ->
|
||||
modulesPath = path.resolve(__dirname, '..', '..', 'node_modules')
|
||||
classes = {}
|
||||
fs.traverseTreeSync modulesPath, (modulePath) ->
|
||||
return false if modulePath.match(/node_modules/g).length > 1 # dont need the dependencies of the dependencies
|
||||
return false if path.basename(modulePath) in moduleBlacklist
|
||||
return true unless path.basename(modulePath) is 'package.json'
|
||||
return true unless fs.isFileSync(modulePath)
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask 'mkdeb', 'Create debian package', ->
|
||||
done = @async()
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
channel = grunt.config.get('atom.channel')
|
||||
|
||||
if process.arch is 'ia32'
|
||||
arch = 'i386'
|
||||
@@ -41,10 +42,10 @@ module.exports = (grunt) ->
|
||||
data = {name, version, description, section, arch, maintainer, installDir, iconName, installedSize, executable}
|
||||
controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data)
|
||||
desktopFilePath = fillTemplate(path.join('resources', 'linux', 'atom.desktop'), data)
|
||||
icon = path.join('resources', 'atom.png')
|
||||
iconPath = path.join('resources', 'app-icons', channel, 'png', '1024.png')
|
||||
|
||||
cmd = path.join('script', 'mkdeb')
|
||||
args = [version, arch, controlFilePath, desktopFilePath, icon, buildDir]
|
||||
args = [version, arch, controlFilePath, desktopFilePath, iconPath, buildDir]
|
||||
spawn {cmd, args}, (error) ->
|
||||
if error?
|
||||
done(error)
|
||||
|
||||
@@ -24,6 +24,13 @@ module.exports = (grunt) ->
|
||||
return done("Unsupported arch #{process.arch}")
|
||||
|
||||
{name, version, description} = grunt.file.readJSON('package.json')
|
||||
|
||||
# RPM versions can't have dashes in them.
|
||||
# * http://www.rpm.org/max-rpm/ch-rpm-file-format.html
|
||||
# * https://github.com/mojombo/semver/issues/145
|
||||
version = version.replace(/-beta/, "~beta")
|
||||
version = version.replace(/-dev/, "~dev")
|
||||
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
|
||||
rpmDir = path.join(buildDir, 'rpm')
|
||||
|
||||
@@ -23,8 +23,6 @@ module.exports = (grunt) ->
|
||||
importFallbackVariables = (lessFilePath) ->
|
||||
if lessFilePath.indexOf('static') is 0
|
||||
false
|
||||
else if lessFilePath.indexOf('atom-space-pen-views') isnt -1
|
||||
false
|
||||
else
|
||||
true
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ module.exports = (gruntObject) ->
|
||||
grunt.registerTask 'publish-build', 'Publish the built app', ->
|
||||
tasks = []
|
||||
tasks.push('build-docs', 'prepare-docs') if process.platform is 'darwin'
|
||||
tasks.push('upload-assets') if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
tasks.push('upload-assets')
|
||||
grunt.task.run(tasks)
|
||||
|
||||
grunt.registerTask 'prepare-docs', 'Move api.json to atom-api.json', ->
|
||||
@@ -31,6 +31,15 @@ module.exports = (gruntObject) ->
|
||||
cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json')
|
||||
|
||||
grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', ->
|
||||
branchName = process.env.JANKY_BRANCH
|
||||
switch branchName
|
||||
when 'stable'
|
||||
isPrerelease = false
|
||||
when 'beta'
|
||||
isPrerelease = true
|
||||
else
|
||||
return
|
||||
|
||||
doneCallback = @async()
|
||||
startTime = Date.now()
|
||||
done = (args...) ->
|
||||
@@ -46,7 +55,7 @@ module.exports = (gruntObject) ->
|
||||
|
||||
zipAssets buildDir, assets, (error) ->
|
||||
return done(error) if error?
|
||||
getAtomDraftRelease (error, release) ->
|
||||
getAtomDraftRelease isPrerelease, branchName, (error, release) ->
|
||||
return done(error) if error?
|
||||
assetNames = (asset.assetName for asset in assets)
|
||||
deleteExistingAssets release, assetNames, (error) ->
|
||||
@@ -120,9 +129,9 @@ zipAssets = (buildDir, assets, callback) ->
|
||||
tasks.push(zip.bind(this, buildDir, sourcePath, assetName))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
getAtomDraftRelease = (callback) ->
|
||||
getAtomDraftRelease = (isPrerelease, branchName, callback) ->
|
||||
atomRepo = new GitHub({repo: 'atom/atom', token})
|
||||
atomRepo.getReleases (error, releases=[]) ->
|
||||
atomRepo.getReleases {prerelease: isPrerelease}, (error, releases=[]) ->
|
||||
if error?
|
||||
logError('Fetching atom/atom releases failed', error, releases)
|
||||
callback(error)
|
||||
@@ -142,9 +151,9 @@ getAtomDraftRelease = (callback) ->
|
||||
firstDraft.assets = assets
|
||||
callback(null, firstDraft)
|
||||
else
|
||||
createAtomDraftRelease(callback)
|
||||
createAtomDraftRelease(isPrerelease, branchName, callback)
|
||||
|
||||
createAtomDraftRelease = (callback) ->
|
||||
createAtomDraftRelease = (isPrerelease, branchName, callback) ->
|
||||
{version} = require('../../package.json')
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom/releases'
|
||||
@@ -152,6 +161,8 @@ createAtomDraftRelease = (callback) ->
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
tag_name: "v#{version}"
|
||||
prerelease: isPrerelease
|
||||
target_commitish: branchName
|
||||
name: version
|
||||
draft: true
|
||||
body: """
|
||||
|
||||
@@ -4,9 +4,10 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask 'set-exe-icon', 'Set icon of the exe', ->
|
||||
done = @async()
|
||||
|
||||
channel = grunt.config.get('atom.channel')
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
shellExePath = path.join(shellAppDir, 'atom.exe')
|
||||
iconPath = path.resolve('resources', 'win', 'atom.ico')
|
||||
iconPath = path.resolve('resources', 'app-icons', channel, 'atom.ico')
|
||||
|
||||
rcedit = require('rcedit')
|
||||
rcedit(shellExePath, {'icon': iconPath}, done)
|
||||
|
||||
@@ -5,7 +5,7 @@ module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
getVersion = (callback) ->
|
||||
onBuildMachine = process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
onBuildMachine = process.env.JANKY_SHA1 and process.env.JANKY_BRANCH in ['stable', 'beta']
|
||||
inRepository = fs.existsSync(path.resolve(__dirname, '..', '..', '.git'))
|
||||
{version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
|
||||
if onBuildMachine or not inRepository
|
||||
|
||||
@@ -17,7 +17,7 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
### Ubuntu / Debian
|
||||
|
||||
* `sudo apt-get install build-essential git libgnome-keyring-dev fakeroot`
|
||||
* Instructions for [Node.js](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os).
|
||||
* Instructions for [Node.js](https://github.com/nodejs/node-v0.x-archive/wiki/Installing-Node.js-via-package-manager#debian-and-ubuntu-based-linux-distributions).
|
||||
* Make sure the command `node` is available after Node.js installation (some systems install it as `nodejs`).
|
||||
* Use `which node` to check if it is available.
|
||||
* Use `sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10` to update it.
|
||||
@@ -25,7 +25,7 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
### Fedora / CentOS / RHEL
|
||||
|
||||
* `sudo dnf --assumeyes install make gcc gcc-c++ glibc-devel git-core libgnome-keyring-devel rpmdevtools`
|
||||
* Instructions for [Node.js](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#fedora).
|
||||
* Instructions for [Node.js](https://github.com/nodejs/node-v0.x-archive/wiki/Installing-Node.js-via-package-manager#enterprise-linux-and-fedora).
|
||||
|
||||
### Arch
|
||||
|
||||
|
||||
@@ -14,19 +14,20 @@
|
||||
If it is installed elsewhere, you can create a symbolic link to the
|
||||
directory containing the python.exe using:
|
||||
`mklink /d %SystemDrive%\Python27 D:\elsewhere\Python27`
|
||||
* [GitHub for Windows](http://windows.github.com/)
|
||||
* [GitHub Desktop](http://desktop.github.com/)
|
||||
|
||||
### On Windows 8
|
||||
* [Visual Studio Express 2013 for Windows Desktop](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_2)
|
||||
### On Windows 8 or 10
|
||||
* [Visual Studio Express 2013 or 2015 for Windows Desktop](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_2)
|
||||
* **Be sure to customize the installation to include Visual C++, it's not installed by default**
|
||||
* [node.js](http://nodejs.org/download/) (0.10.x or 0.12.x) or [io.js](https://iojs.org) (1.x or 2.x)
|
||||
* [Python](https://www.python.org/downloads/) v2.7.x (required by [node-gyp](https://github.com/TooTallNate/node-gyp))
|
||||
* [GitHub for Windows](http://windows.github.com/)
|
||||
* [GitHub Desktop](http://desktop.github.com/)
|
||||
|
||||
## Instructions
|
||||
|
||||
```bash
|
||||
# Use the `Git Shell` program which was installed by GitHub for Windows.
|
||||
# Also make sure that you are logged into GitHub for Windows.
|
||||
# Use the `Git Shell` program which was installed by GitHub Desktop.
|
||||
# Also make sure that you are logged into GitHub Desktop.
|
||||
cd C:\
|
||||
git clone https://github.com/atom/atom/
|
||||
cd atom
|
||||
@@ -40,15 +41,14 @@ We will assume the git shell for these instructions.
|
||||
* `--build-dir` - Build the application in this directory.
|
||||
* `--verbose` - Verbose mode. A lot more information output.
|
||||
|
||||
## Why do I have to use GitHub for Windows?
|
||||
## Why do I have to use GitHub Desktop?
|
||||
|
||||
You don't. You can use your existing Git! GitHub for Windows's Git Shell is just
|
||||
easier to set up.
|
||||
You don't. You can use your existing Git! GitHub Desktop's Git Shell is just easier to set up.
|
||||
|
||||
If you _prefer_ using your existing Git installation, make sure git's cmd directory is in your PATH env variable (e.g. `C:\Program Files (x86)\Git\cmd`) before you open your powershell or command window.
|
||||
Note that you may have to open your command window as administrator. For powershell that doesn't seem to always be the case, though.
|
||||
|
||||
If none of this works, do install Github for Windows and use its Git shell. Makes life easier.
|
||||
If none of this works, do install Github Desktop and use its Git shell. Makes life easier.
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
@@ -59,7 +59,6 @@ If none of this works, do install Github for Windows and use its Git shell. Make
|
||||
* If you just installed node, you'll need to restart your computer before node is
|
||||
available on your Path.
|
||||
|
||||
|
||||
* `script/build` outputs only the Node and Python versions before returning
|
||||
|
||||
* Try moving the repository to `C:\atom`. Most likely, the path is too long.
|
||||
@@ -73,7 +72,7 @@ If none of this works, do install Github for Windows and use its Git shell. Make
|
||||
* https://github.com/TooTallNate/node-gyp/issues/297
|
||||
* https://code.google.com/p/gyp/issues/detail?id=393
|
||||
|
||||
* `script/build` stops at installing runas with 'Failed at the runas@0.5.4 install script.'
|
||||
* `script/build` stops at installing runas with 'Failed at the runas@x.y.z install script.'
|
||||
|
||||
See the next item.
|
||||
|
||||
|
||||
@@ -24,102 +24,6 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
||||
module.exports.Task = require '../src/task'
|
||||
module.exports.TextEditor = require '../src/text-editor'
|
||||
|
||||
if includeDeprecatedAPIs
|
||||
{$, $$, $$$, View} = require '../src/space-pen-extensions'
|
||||
|
||||
Object.defineProperty module.exports, 'Workspace', get: ->
|
||||
deprecate """
|
||||
Requiring `Workspace` from `atom` is no longer supported.
|
||||
If you need this, please open an issue on
|
||||
https://github.com/atom/atom/issues/new
|
||||
And let us know what you are using it for.
|
||||
"""
|
||||
require '../src/workspace'
|
||||
|
||||
Object.defineProperty module.exports, 'WorkspaceView', get: ->
|
||||
deprecate """
|
||||
Requiring `WorkspaceView` from `atom` is no longer supported.
|
||||
Use `atom.views.getView(atom.workspace)` instead.
|
||||
"""
|
||||
require '../src/workspace-view'
|
||||
|
||||
Object.defineProperty module.exports, '$', get: ->
|
||||
deprecate """
|
||||
Requiring `$` from `atom` is no longer supported.
|
||||
If you are using `space-pen`, please require `$` from `atom-space-pen-views`. Otherwise require `jquery` instead:
|
||||
`{$} = require 'atom-space-pen-views'`
|
||||
or
|
||||
`$ = require 'jquery'`
|
||||
Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies.
|
||||
Or add `"jquery": "^2"` to your package dependencies.
|
||||
"""
|
||||
$
|
||||
|
||||
Object.defineProperty module.exports, '$$', get: ->
|
||||
deprecate """
|
||||
Requiring `$$` from `atom` is no longer supported.
|
||||
Please require `atom-space-pen-views` instead:
|
||||
`{$$} = require 'atom-space-pen-views'`
|
||||
Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies.
|
||||
"""
|
||||
$$
|
||||
|
||||
Object.defineProperty module.exports, '$$$', get: ->
|
||||
deprecate """
|
||||
Requiring `$$$` from `atom` is no longer supported.
|
||||
Please require `atom-space-pen-views` instead:
|
||||
`{$$$} = require 'atom-space-pen-views'`
|
||||
Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies.
|
||||
"""
|
||||
$$$
|
||||
|
||||
Object.defineProperty module.exports, 'View', get: ->
|
||||
deprecate """
|
||||
Requiring `View` from `atom` is no longer supported.
|
||||
Please require `atom-space-pen-views` instead:
|
||||
`{View} = require 'atom-space-pen-views'`
|
||||
Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies.
|
||||
"""
|
||||
View
|
||||
|
||||
Object.defineProperty module.exports, 'EditorView', get: ->
|
||||
deprecate """
|
||||
Requiring `EditorView` from `atom` is no longer supported.
|
||||
Please require `TextEditorView` from `atom-space-pen-view` instead:
|
||||
`{TextEditorView} = require 'atom-space-pen-views'`
|
||||
Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies.
|
||||
"""
|
||||
require '../src/text-editor-view'
|
||||
|
||||
Object.defineProperty module.exports, 'TextEditorView', get: ->
|
||||
deprecate """
|
||||
Requiring `TextEditorView` from `atom` is no longer supported.
|
||||
Please require `TextEditorView` from `atom-space-pen-view` instead:
|
||||
`{TextEditorView} = require 'atom-space-pen-views'`
|
||||
Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies.
|
||||
"""
|
||||
require '../src/text-editor-view'
|
||||
|
||||
Object.defineProperty module.exports, 'ScrollView', get: ->
|
||||
deprecate """
|
||||
Requiring `ScrollView` from `atom` is no longer supported.
|
||||
Please require `ScrollView` from `atom-space-pen-view` instead:
|
||||
`{ScrollView} = require 'atom-space-pen-views'`
|
||||
Note that the API has changed slightly! Please read the docs at https://github.com/atom/atom-space-pen-views
|
||||
Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies.
|
||||
"""
|
||||
require '../src/scroll-view'
|
||||
|
||||
Object.defineProperty module.exports, 'SelectListView', get: ->
|
||||
deprecate """
|
||||
Requiring `SelectListView` from `atom` is no longer supported.
|
||||
Please require `SelectListView` from `atom-space-pen-view` instead:
|
||||
`{SelectListView} = require 'atom-space-pen-views'`
|
||||
Note that the API has changed slightly! Please read the docs at https://github.com/atom/atom-space-pen-views
|
||||
Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies.
|
||||
"""
|
||||
require '../src/select-list-view'
|
||||
|
||||
if includeDeprecatedAPIs
|
||||
Object.defineProperty module.exports, 'Git', get: ->
|
||||
deprecate "Please require `GitRepository` instead of `Git`: `{GitRepository} = require 'atom'`"
|
||||
|
||||
87
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.0.10",
|
||||
"version": "1.2.0-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -12,11 +12,10 @@
|
||||
"url": "https://github.com/atom/atom/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"atomShellVersion": "0.22.3",
|
||||
"electronVersion": "0.30.6",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^5.1.10",
|
||||
"atom-space-pen-views": "^2.1.0",
|
||||
"atom-keymap": "5.1.11",
|
||||
"babel-core": "^5.8.21",
|
||||
"bootstrap": "^3.3.4",
|
||||
"clear-cut": "^2.0.1",
|
||||
@@ -39,10 +38,9 @@
|
||||
"mixto": "^1",
|
||||
"normalize-package-data": "^2.0.0",
|
||||
"nslog": "^2.0.0",
|
||||
"oniguruma": "^4.1",
|
||||
"pathwatcher": "^4.4.3",
|
||||
"oniguruma": "^4.2.4",
|
||||
"pathwatcher": "^5.0.0",
|
||||
"property-accessors": "^1.1.3",
|
||||
"q": "^1.1.2",
|
||||
"random-words": "0.0.1",
|
||||
"runas": "2.0.0",
|
||||
"scandal": "2.1.2",
|
||||
@@ -53,10 +51,9 @@
|
||||
"serializable": "^1",
|
||||
"service-hub": "^0.6.2",
|
||||
"source-map-support": "^0.3.2",
|
||||
"space-pen": "3.8.2",
|
||||
"stacktrace-parser": "0.1.1",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "6.7.0",
|
||||
"text-buffer": "7.0.3",
|
||||
"theorist": "^1.0.2",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
@@ -64,88 +61,88 @@
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.27.0",
|
||||
"atom-dark-ui": "0.50.0",
|
||||
"atom-dark-ui": "0.51.0",
|
||||
"atom-light-syntax": "0.28.0",
|
||||
"atom-light-ui": "0.43.0",
|
||||
"base16-tomorrow-dark-theme": "0.27.0",
|
||||
"base16-tomorrow-light-theme": "0.9.0",
|
||||
"one-dark-ui": "1.0.3",
|
||||
"one-dark-ui": "1.1.3",
|
||||
"one-dark-syntax": "1.1.0",
|
||||
"one-light-syntax": "1.1.0",
|
||||
"one-light-ui": "1.0.3",
|
||||
"one-light-ui": "1.1.3",
|
||||
"solarized-dark-syntax": "0.38.1",
|
||||
"solarized-light-syntax": "0.22.1",
|
||||
"about": "1.1.0",
|
||||
"archive-view": "0.60.0",
|
||||
"archive-view": "0.61.0",
|
||||
"autocomplete-atom-api": "0.9.2",
|
||||
"autocomplete-css": "0.10.1",
|
||||
"autocomplete-css": "0.11.0",
|
||||
"autocomplete-html": "0.7.2",
|
||||
"autocomplete-plus": "2.19.0",
|
||||
"autocomplete-plus": "2.20.0",
|
||||
"autocomplete-snippets": "1.7.1",
|
||||
"autoflow": "0.25.0",
|
||||
"autosave": "0.22.0",
|
||||
"background-tips": "0.26.0",
|
||||
"bookmarks": "0.36.0",
|
||||
"bracket-matcher": "0.76.0",
|
||||
"bookmarks": "0.38.0",
|
||||
"bracket-matcher": "0.78.0",
|
||||
"command-palette": "0.36.0",
|
||||
"deprecation-cop": "0.54.0",
|
||||
"dev-live-reload": "0.46.0",
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.21.0",
|
||||
"exception-reporting": "0.36.0",
|
||||
"find-and-replace": "0.180.0",
|
||||
"fuzzy-finder": "0.88.0",
|
||||
"git-diff": "0.55.0",
|
||||
"exception-reporting": "0.37.0",
|
||||
"find-and-replace": "0.182.0",
|
||||
"fuzzy-finder": "0.89.0",
|
||||
"git-diff": "0.56.0",
|
||||
"go-to-line": "0.30.0",
|
||||
"grammar-selector": "0.47.0",
|
||||
"image-view": "0.54.0",
|
||||
"incompatible-packages": "0.24.1",
|
||||
"image-view": "0.55.0",
|
||||
"incompatible-packages": "0.25.0",
|
||||
"keybinding-resolver": "0.33.0",
|
||||
"line-ending-selector": "0.0.5",
|
||||
"link": "0.30.0",
|
||||
"markdown-preview": "0.150.0",
|
||||
"markdown-preview": "0.152.0",
|
||||
"metrics": "0.51.0",
|
||||
"notifications": "0.59.0",
|
||||
"open-on-github": "0.38.0",
|
||||
"package-generator": "0.40.0",
|
||||
"release-notes": "0.53.0",
|
||||
"settings-view": "0.216.0",
|
||||
"snippets": "0.95.0",
|
||||
"settings-view": "0.221.0",
|
||||
"snippets": "0.100.0",
|
||||
"spell-check": "0.59.0",
|
||||
"status-bar": "0.79.0",
|
||||
"styleguide": "0.44.0",
|
||||
"symbols-view": "0.103.0",
|
||||
"symbols-view": "0.107.0",
|
||||
"tabs": "0.84.0",
|
||||
"timecop": "0.31.0",
|
||||
"tree-view": "0.186.0",
|
||||
"timecop": "0.33.0",
|
||||
"tree-view": "0.188.0",
|
||||
"update-package-dependencies": "0.10.0",
|
||||
"welcome": "0.30.0",
|
||||
"whitespace": "0.30.0",
|
||||
"wrap-guide": "0.35.0",
|
||||
"language-c": "0.47.1",
|
||||
"language-clojure": "0.16.0",
|
||||
"whitespace": "0.31.0",
|
||||
"wrap-guide": "0.36.0",
|
||||
"language-c": "0.48.0",
|
||||
"language-clojure": "0.17.0",
|
||||
"language-coffee-script": "0.41.0",
|
||||
"language-csharp": "0.7.0",
|
||||
"language-css": "0.33.0",
|
||||
"language-gfm": "0.80.0",
|
||||
"language-csharp": "0.10.0",
|
||||
"language-css": "0.34.0",
|
||||
"language-gfm": "0.81.0",
|
||||
"language-git": "0.10.0",
|
||||
"language-go": "0.37.0",
|
||||
"language-go": "0.39.0",
|
||||
"language-html": "0.41.2",
|
||||
"language-hyperlink": "0.14.0",
|
||||
"language-java": "0.16.0",
|
||||
"language-javascript": "0.92.0",
|
||||
"language-javascript": "0.95.0",
|
||||
"language-json": "0.16.0",
|
||||
"language-less": "0.28.2",
|
||||
"language-make": "0.17.0",
|
||||
"language-mustache": "0.12.0",
|
||||
"language-objective-c": "0.15.0",
|
||||
"language-perl": "0.28.0",
|
||||
"language-php": "0.29.0",
|
||||
"language-perl": "0.29.0",
|
||||
"language-php": "0.30.0",
|
||||
"language-property-list": "0.8.0",
|
||||
"language-python": "0.39.0",
|
||||
"language-ruby": "0.57.0",
|
||||
"language-python": "0.40.0",
|
||||
"language-ruby": "0.59.0",
|
||||
"language-ruby-on-rails": "0.22.0",
|
||||
"language-sass": "0.40.1",
|
||||
"language-shellscript": "0.15.0",
|
||||
"language-sass": "0.41.0",
|
||||
"language-shellscript": "0.17.0",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.17.0",
|
||||
"language-text": "0.7.0",
|
||||
|
||||
BIN
resources/app-icons/beta/atom.icns
Normal file
BIN
resources/app-icons/beta/atom.ico
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
resources/app-icons/beta/png/1024.png
Normal file
|
After Width: | Height: | Size: 629 KiB |
BIN
resources/app-icons/beta/png/128.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
resources/app-icons/beta/png/16.png
Normal file
|
After Width: | Height: | Size: 907 B |
BIN
resources/app-icons/beta/png/24.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/app-icons/beta/png/256.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
resources/app-icons/beta/png/32.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
resources/app-icons/beta/png/48.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
resources/app-icons/beta/png/512.png
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
resources/app-icons/beta/png/64.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
resources/app-icons/dev/atom.icns
Normal file
BIN
resources/app-icons/dev/atom.ico
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
resources/app-icons/dev/png/1024.png
Normal file
|
After Width: | Height: | Size: 601 KiB |
BIN
resources/app-icons/dev/png/128.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
resources/app-icons/dev/png/16.png
Normal file
|
After Width: | Height: | Size: 905 B |
BIN
resources/app-icons/dev/png/24.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/app-icons/dev/png/256.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
resources/app-icons/dev/png/32.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
resources/app-icons/dev/png/48.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
resources/app-icons/dev/png/512.png
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
resources/app-icons/dev/png/64.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 628 KiB After Width: | Height: | Size: 628 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 944 B After Width: | Height: | Size: 944 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 628 KiB |
@@ -62,6 +62,8 @@ function bootstrap() {
|
||||
'temp'
|
||||
];
|
||||
|
||||
process.env.ATOM_RESOURCE_PATH = path.resolve(__dirname, '..');
|
||||
|
||||
var buildInstallCommand = initialNpmCommand + npmFlags + 'install';
|
||||
var buildInstallOptions = {cwd: path.resolve(__dirname, '..', 'build')};
|
||||
var apmInstallCommand = npmPath + npmFlags + '--target=0.10.35 ' + 'install';
|
||||
|
||||
@@ -22,11 +22,16 @@ function loadEnvironmentVariables(filePath) {
|
||||
}
|
||||
|
||||
function readEnvironmentVariables() {
|
||||
if (process.platform === 'win32')
|
||||
if (process.platform === 'win32') {
|
||||
loadEnvironmentVariables(path.resolve('/jenkins/config/atomcredentials'));
|
||||
else if (process.platform === 'darwin') {
|
||||
} else if (process.platform === 'darwin') {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials');
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain');
|
||||
} else if (process.platform === 'linux') {
|
||||
// Use Clang for building native code, the GCC on Precise is too old.
|
||||
process.env.CC = 'clang';
|
||||
process.env.CXX = 'clang++';
|
||||
process.env.npm_config_clang = '1';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ var commands = [
|
||||
[home, '.atom', '.npm'],
|
||||
[home, '.atom', 'compile-cache'],
|
||||
[home, '.atom', 'atom-shell'],
|
||||
[home, '.atom', 'electron'],
|
||||
[tmpdir, 'atom-build'],
|
||||
[tmpdir, 'atom-cached-atom-shells'],
|
||||
];
|
||||
|
||||
89
script/railcar
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var fs = require('fs')
|
||||
var exec = require('child_process').exec
|
||||
var series = require('async').series
|
||||
var semver = require('semver')
|
||||
|
||||
series([
|
||||
section('Preparing to roll the railcars'),
|
||||
checkCleanWorkingTree,
|
||||
run('git fetch origin master:master beta:beta stable:stable'),
|
||||
run('git fetch origin --tags'),
|
||||
|
||||
section('Updating stable branch'),
|
||||
run('git checkout stable'),
|
||||
run('git merge --ff-only origin/stable'),
|
||||
run('git merge --ff-only origin/beta'),
|
||||
bumpStableVersion,
|
||||
|
||||
section('Updating beta branch'),
|
||||
run('git checkout beta'),
|
||||
run('git merge --ff-only origin/beta'),
|
||||
run('git merge --ff-only origin/master'),
|
||||
run('git merge --strategy ours origin/stable'),
|
||||
bumpBetaVersion,
|
||||
|
||||
section('Updating master branch'),
|
||||
run('git checkout master'),
|
||||
run('git merge --ff-only origin/master'),
|
||||
run('git merge --strategy ours origin/beta'),
|
||||
bumpDevVersion,
|
||||
|
||||
section('Pushing changes upstream'),
|
||||
run('git push origin master:master beta:beta stable:stable'),
|
||||
run('git push origin --tags')
|
||||
], finish)
|
||||
|
||||
function checkCleanWorkingTree (next) {
|
||||
run('git status --porcelain')(function (error, output) {
|
||||
if (error) return next(error)
|
||||
if (output.trim().length > 0) return next(new Error('Cannot run the railcars with a dirty working tree'))
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
function bumpStableVersion (next) {
|
||||
run('npm version patch')(next)
|
||||
}
|
||||
|
||||
function bumpBetaVersion (next) {
|
||||
var newVersion = semver.inc(getCurrentVersion(), 'preminor', 'beta')
|
||||
run('npm version ' + newVersion)(next)
|
||||
}
|
||||
|
||||
function bumpDevVersion (next) {
|
||||
var newVersion = semver.inc(getCurrentVersion(), 'preminor', 'dev').replace(/\.0$/, '')
|
||||
series([
|
||||
run('npm --no-git-tag-version version ' + newVersion),
|
||||
run('git commit -am "' + newVersion + '"')
|
||||
], next)
|
||||
}
|
||||
|
||||
function finish (error) {
|
||||
if (error) {
|
||||
console.log('Error: ' + error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log('OK, now just wait for all CI builds to pass on beta and stable')
|
||||
}
|
||||
|
||||
function getCurrentVersion () {
|
||||
return JSON.parse(fs.readFileSync(require.resolve('../package.json'))).version
|
||||
}
|
||||
|
||||
function run (command) {
|
||||
return function (next) {
|
||||
console.log('>', command)
|
||||
exec(command, next)
|
||||
}
|
||||
}
|
||||
|
||||
function section (message) {
|
||||
return function (next) {
|
||||
console.log()
|
||||
console.log(message)
|
||||
next()
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,9 @@
|
||||
{$} = require '../src/space-pen-extensions'
|
||||
|
||||
describe '"atom" protocol URL', ->
|
||||
it 'sends the file relative in the package as response', ->
|
||||
called = false
|
||||
callback = -> called = true
|
||||
$.ajax
|
||||
url: 'atom://async/package.json'
|
||||
success: callback
|
||||
# In old versions of jQuery, ajax calls to custom protocol would always
|
||||
# be treated as error eventhough the browser thinks it's a success
|
||||
# request.
|
||||
error: callback
|
||||
request = new XMLHttpRequest()
|
||||
request.addEventListener('load', -> called = true)
|
||||
request.open('GET', 'atom://async/package.json', true)
|
||||
request.send()
|
||||
|
||||
waitsFor 'request to be done', -> called is true
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
{View, $, $$} = require '../src/space-pen-extensions'
|
||||
grim = require 'grim'
|
||||
marked = require 'marked'
|
||||
|
||||
listen = require '../src/delegated-listener'
|
||||
|
||||
formatStackTrace = (spec, message='', stackTrace) ->
|
||||
return stackTrace unless stackTrace
|
||||
|
||||
@@ -30,30 +31,43 @@ formatStackTrace = (spec, message='', stackTrace) ->
|
||||
lines.join('\n').trim()
|
||||
|
||||
module.exports =
|
||||
class AtomReporter extends View
|
||||
@content: ->
|
||||
@div class: 'spec-reporter', =>
|
||||
@div class: 'padded pull-right', =>
|
||||
@button outlet: 'reloadButton', class: 'btn btn-small reload-button', 'Reload Specs'
|
||||
@div outlet: 'coreArea', class: 'symbol-area', =>
|
||||
@div outlet: 'coreHeader', class: 'symbol-header'
|
||||
@ul outlet: 'coreSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: 'bundledArea', class: 'symbol-area', =>
|
||||
@div outlet: 'bundledHeader', class: 'symbol-header'
|
||||
@ul outlet: 'bundledSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: 'userArea', class: 'symbol-area', =>
|
||||
@div outlet: 'userHeader', class: 'symbol-header'
|
||||
@ul outlet: 'userSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: "status", class: 'status alert alert-info', =>
|
||||
@div outlet: "time", class: 'time'
|
||||
@div outlet: "specCount", class: 'spec-count'
|
||||
@div outlet: "message", class: 'message'
|
||||
@div outlet: "results", class: 'results'
|
||||
class AtomReporter
|
||||
|
||||
@div outlet: "deprecations", class: 'status alert alert-warning', style: 'display: none', =>
|
||||
@span outlet: 'deprecationStatus', '0 deprecations'
|
||||
@div class: 'deprecation-toggle'
|
||||
@div outlet: 'deprecationList', class: 'deprecation-list'
|
||||
constructor: ->
|
||||
@element = document.createElement('div')
|
||||
@element.innerHTML = """
|
||||
<div class="spec-reporter">
|
||||
<div class="padded pull-right">
|
||||
<button outlet="reloadButton" class="btn btn-small reload-button">Reload Specs</button>
|
||||
</div>
|
||||
<div outlet="coreArea" class="symbol-area">
|
||||
<div outlet="coreHeader" class="symbol-header"></div>
|
||||
<ul outlet="coreSummary"class="symbol-summary list-unstyled"></ul>
|
||||
</div>
|
||||
<div outlet="bundledArea" class="symbol-area">
|
||||
<div outlet="bundledHeader" class="symbol-header"></div>
|
||||
<ul outlet="bundledSummary"class="symbol-summary list-unstyled"></ul>
|
||||
</div>
|
||||
<div outlet="userArea" class="symbol-area">
|
||||
<div outlet="userHeader" class="symbol-header"></div>
|
||||
<ul outlet="userSummary"class="symbol-summary list-unstyled"></ul>
|
||||
</div>
|
||||
<div outlet="status" class="status alert alert-info">
|
||||
<div outlet="time" class="time"></div>
|
||||
<div outlet="specCount" class="spec-count"></div>
|
||||
<div outlet="message" class="message"></div>
|
||||
</div>
|
||||
<div outlet="results" class="results"></div>
|
||||
<div outlet="deprecations" class="status alert alert-warning" style="display: none">
|
||||
<span outlet="deprecationStatus">0 deprecations</span>
|
||||
<div class="deprecation-toggle"></div>
|
||||
</div>
|
||||
<div outlet="deprecationList" class="deprecation-list"></div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
for element in @element.querySelectorAll('[outlet]')
|
||||
this[element.getAttribute('outlet')] = element
|
||||
|
||||
startedAt: null
|
||||
runningSpecCount: 0
|
||||
@@ -71,20 +85,18 @@ class AtomReporter extends View
|
||||
specs = runner.specs()
|
||||
@totalSpecCount = specs.length
|
||||
@addSpecs(specs)
|
||||
$(document.body).append this
|
||||
|
||||
@on 'click', '.stack-trace', ->
|
||||
$(this).toggleClass('expanded')
|
||||
|
||||
@reloadButton.on 'click', -> require('ipc').send('call-window-method', 'restart')
|
||||
document.body.appendChild(@element)
|
||||
|
||||
reportRunnerResults: (runner) ->
|
||||
@updateSpecCounts()
|
||||
@status.addClass('alert-success').removeClass('alert-info') if @failedCount is 0
|
||||
if @failedCount is 0
|
||||
@status.classList.add('alert-success')
|
||||
@status.classList.remove('alert-info')
|
||||
|
||||
if @failedCount is 1
|
||||
@message.text "#{@failedCount} failure"
|
||||
@message.textContent = "#{@failedCount} failure"
|
||||
else
|
||||
@message.text "#{@failedCount} failures"
|
||||
@message.textConent = "#{@failedCount} failures"
|
||||
|
||||
reportSuiteResults: (suite) ->
|
||||
|
||||
@@ -100,170 +112,214 @@ class AtomReporter extends View
|
||||
addDeprecations: (spec) ->
|
||||
deprecations = grim.getDeprecations()
|
||||
@deprecationCount += deprecations.length
|
||||
@deprecations.show() if @deprecationCount > 0
|
||||
@deprecations.style.display = '' if @deprecationCount > 0
|
||||
if @deprecationCount is 1
|
||||
@deprecationStatus.text("1 deprecation")
|
||||
@deprecationStatus.textContent = "1 deprecation"
|
||||
else
|
||||
@deprecationStatus.text("#{@deprecationCount} deprecations")
|
||||
@deprecationStatus.textContent = "#{@deprecationCount} deprecations"
|
||||
|
||||
for deprecation in deprecations
|
||||
@deprecationList.append $$ ->
|
||||
@div class: 'padded', =>
|
||||
@div class: 'result-message fail deprecation-message', =>
|
||||
@raw marked(deprecation.message)
|
||||
@deprecationList.appendChild(@buildDeprecationElement(spec, deprecation))
|
||||
|
||||
for stack in deprecation.getStacks()
|
||||
fullStack = stack.map ({functionName, location}) ->
|
||||
if functionName is '<unknown>'
|
||||
" at #{location}"
|
||||
else
|
||||
" at #{functionName} (#{location})"
|
||||
@pre class: 'stack-trace padded', formatStackTrace(spec, deprecation.message, fullStack.join('\n'))
|
||||
grim.clearDeprecations()
|
||||
|
||||
handleEvents: ->
|
||||
$(document).on "click", ".spec-toggle", ({currentTarget}) ->
|
||||
element = $(currentTarget)
|
||||
specFailures = element.parent().find('.spec-failures')
|
||||
specFailures.toggle()
|
||||
element.toggleClass('folded')
|
||||
false
|
||||
buildDeprecationElement: (spec, deprecation) ->
|
||||
div = document.createElement('div')
|
||||
div.className = 'padded'
|
||||
div.innerHTML = """
|
||||
<div class="result-message fail deprecation-message">
|
||||
#{marked(deprecation.message)}
|
||||
</div>
|
||||
"""
|
||||
|
||||
$(document).on "click", ".deprecation-toggle", ({currentTarget}) ->
|
||||
element = $(currentTarget)
|
||||
deprecationList = $(document).find('.deprecation-list')
|
||||
deprecationList.toggle()
|
||||
element.toggleClass('folded')
|
||||
false
|
||||
for stack in deprecation.getStacks()
|
||||
fullStack = stack.map ({functionName, location}) ->
|
||||
if functionName is '<unknown>'
|
||||
" at #{location}"
|
||||
else
|
||||
" at #{functionName} (#{location})"
|
||||
pre = document.createElement('pre')
|
||||
pre.className = 'stack-trace padded'
|
||||
pre.textContent = formatStackTrace(spec, deprecation.message, fullStack.join('\n'))
|
||||
div.appendChild(pre)
|
||||
|
||||
div
|
||||
|
||||
handleEvents: ->
|
||||
listen document, 'click', '.spec-toggle', (event) ->
|
||||
specFailures = event.currentTarget.parentElement.querySelector('.spec-failures')
|
||||
|
||||
if specFailures.style.display is 'none'
|
||||
specFailures.style.display = ''
|
||||
event.currentTarget.classList.remove('folded')
|
||||
else
|
||||
specFailures.style.display = 'none'
|
||||
event.currentTarget.classList.add('folded')
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
listen document, 'click', '.deprecation-list', (event) ->
|
||||
deprecationList = event.currentTarget.parentElement.querySelector('.deprecation-list')
|
||||
|
||||
if deprecationList.style.display is 'none'
|
||||
deprecationList.style.display = ''
|
||||
event.currentTarget.classList.remove('folded')
|
||||
else
|
||||
deprecationList.style.display = 'none'
|
||||
event.currentTarget.classList.add('folded')
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
listen document, 'click', '.stack-trace', (event) ->
|
||||
event.currentTarget.classList.toggle('expanded')
|
||||
|
||||
@reloadButton.addEventListener('click', -> require('ipc').send('call-window-method', 'restart'))
|
||||
|
||||
updateSpecCounts: ->
|
||||
if @skippedCount
|
||||
specCount = "#{@completeSpecCount - @skippedCount}/#{@totalSpecCount - @skippedCount} (#{@skippedCount} skipped)"
|
||||
else
|
||||
specCount = "#{@completeSpecCount}/#{@totalSpecCount}"
|
||||
@specCount[0].textContent = specCount
|
||||
@specCount.textContent = specCount
|
||||
|
||||
updateStatusView: (spec) ->
|
||||
if @failedCount > 0
|
||||
@status.addClass('alert-danger').removeClass('alert-info')
|
||||
@status.classList.add('alert-danger')
|
||||
@status.classList.remove('alert-info')
|
||||
|
||||
@updateSpecCounts()
|
||||
|
||||
rootSuite = spec.suite
|
||||
rootSuite = rootSuite.parentSuite while rootSuite.parentSuite
|
||||
@message.text rootSuite.description
|
||||
@message.textContent = rootSuite.description
|
||||
|
||||
time = "#{Math.round((spec.endedAt - @startedAt) / 10)}"
|
||||
time = "0#{time}" if time.length < 3
|
||||
@time[0].textContent = "#{time[0...-2]}.#{time[-2..]}s"
|
||||
@time.textContent = "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
addSpecs: (specs) ->
|
||||
coreSpecs = 0
|
||||
bundledPackageSpecs = 0
|
||||
userPackageSpecs = 0
|
||||
for spec in specs
|
||||
symbol = $$ -> @li id: "spec-summary-#{spec.id}", class: "spec-summary pending"
|
||||
symbol = document.createElement('li')
|
||||
symbol.setAttribute('id', "spec-summary-#{spec.id}")
|
||||
symbol.className = "spec-summary pending"
|
||||
switch spec.specType
|
||||
when 'core'
|
||||
coreSpecs++
|
||||
@coreSummary.append symbol
|
||||
@coreSummary.appendChild symbol
|
||||
when 'bundled'
|
||||
bundledPackageSpecs++
|
||||
@bundledSummary.append symbol
|
||||
@bundledSummary.appendChild symbol
|
||||
when 'user'
|
||||
userPackageSpecs++
|
||||
@userSummary.append symbol
|
||||
@userSummary.appendChild symbol
|
||||
|
||||
if coreSpecs > 0
|
||||
@coreHeader.text("Core Specs (#{coreSpecs})")
|
||||
@coreHeader.textContent = "Core Specs (#{coreSpecs})"
|
||||
else
|
||||
@coreArea.hide()
|
||||
@coreArea.style.display = 'none'
|
||||
if bundledPackageSpecs > 0
|
||||
@bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs})")
|
||||
@bundledHeader.textContent = "Bundled Package Specs (#{bundledPackageSpecs})"
|
||||
else
|
||||
@bundledArea.hide()
|
||||
@bundledArea.style.display = 'none'
|
||||
if userPackageSpecs > 0
|
||||
if coreSpecs is 0 and bundledPackageSpecs is 0
|
||||
# Package specs being run, show a more descriptive label
|
||||
{specDirectory} = specs[0]
|
||||
packageFolderName = path.basename(path.dirname(specDirectory))
|
||||
packageName = _.undasherize(_.uncamelcase(packageFolderName))
|
||||
@userHeader.text("#{packageName} Specs")
|
||||
@userHeader.textContent = "#{packageName} Specs"
|
||||
else
|
||||
@userHeader.text("User Package Specs (#{userPackageSpecs})")
|
||||
@userHeader.textContent = "User Package Specs (#{userPackageSpecs})"
|
||||
else
|
||||
@userArea.hide()
|
||||
@userArea.style.display = 'none'
|
||||
|
||||
specStarted: (spec) ->
|
||||
@runningSpecCount++
|
||||
|
||||
specComplete: (spec) ->
|
||||
specSummaryElement = $("#spec-summary-#{spec.id}")
|
||||
specSummaryElement.removeClass('pending')
|
||||
specSummaryElement.setTooltip(title: spec.getFullName(), container: '.spec-reporter')
|
||||
specSummaryElement = document.getElementById("spec-summary-#{spec.id}")
|
||||
specSummaryElement.classList.remove('pending')
|
||||
|
||||
results = spec.results()
|
||||
if results.skipped
|
||||
specSummaryElement.addClass("skipped")
|
||||
specSummaryElement.classList.add("skipped")
|
||||
@skippedCount++
|
||||
else if results.passed()
|
||||
specSummaryElement.addClass("passed")
|
||||
specSummaryElement.classList.add("passed")
|
||||
@passedCount++
|
||||
else
|
||||
specSummaryElement.addClass("failed")
|
||||
specSummaryElement.classList.add("failed")
|
||||
|
||||
specView = new SpecResultView(spec)
|
||||
specView.attach()
|
||||
@failedCount++
|
||||
@addDeprecations(spec)
|
||||
|
||||
class SuiteResultView extends View
|
||||
@content: ->
|
||||
@div class: 'suite', =>
|
||||
@div outlet: 'description', class: 'description'
|
||||
|
||||
initialize: (@suite) ->
|
||||
@attr('id', "suite-view-#{@suite.id}")
|
||||
@description.text(@suite.description)
|
||||
class SuiteResultView
|
||||
constructor: (@suite) ->
|
||||
@element = document.createElement('div')
|
||||
@element.className = 'suite'
|
||||
@element.setAttribute('id', "suite-view-#{@suite.id}")
|
||||
@description = document.createElement('div')
|
||||
@description.className = 'description'
|
||||
@description.textContent = @suite.description
|
||||
@element.appendChild(@description)
|
||||
|
||||
attach: ->
|
||||
(@parentSuiteView() or $('.results')).append this
|
||||
(@parentSuiteView() or document.querySelector('.results')).appendChild(@element)
|
||||
|
||||
parentSuiteView: ->
|
||||
return unless @suite.parentSuite
|
||||
|
||||
if not suiteView = $("#suite-view-#{@suite.parentSuite.id}").view()
|
||||
unless suiteViewElement = document.querySelector("#suite-view-#{@suite.parentSuite.id}")
|
||||
suiteView = new SuiteResultView(@suite.parentSuite)
|
||||
suiteView.attach()
|
||||
suiteViewElement = suiteView.element
|
||||
|
||||
suiteView
|
||||
suiteViewElement
|
||||
|
||||
class SpecResultView extends View
|
||||
@content: ->
|
||||
@div class: 'spec', =>
|
||||
@div class: 'spec-toggle'
|
||||
@div outlet: 'description', class: 'description'
|
||||
@div outlet: 'specFailures', class: 'spec-failures'
|
||||
class SpecResultView
|
||||
constructor: (@spec) ->
|
||||
@element = document.createElement('div')
|
||||
@element.className = 'spec'
|
||||
@element.innerHTML = """
|
||||
<div class='spec-toggle'></div>
|
||||
<div outlet='description' class='description'></div>
|
||||
<div outlet='specFailures' class='spec-failures'></div>
|
||||
"""
|
||||
@description = @element.querySelector('[outlet="description"]')
|
||||
@specFailures = @element.querySelector('[outlet="specFailures"]')
|
||||
|
||||
initialize: (@spec) ->
|
||||
@addClass("spec-view-#{@spec.id}")
|
||||
@element.classList.add("spec-view-#{@spec.id}")
|
||||
|
||||
description = @spec.description
|
||||
description = "it #{description}" if description.indexOf('it ') isnt 0
|
||||
@description.text(description)
|
||||
@description.textContent = description
|
||||
|
||||
for result in @spec.results().getItems() when not result.passed()
|
||||
stackTrace = formatStackTrace(@spec, result.message, result.trace.stack)
|
||||
@specFailures.append $$ ->
|
||||
@div result.message, class: 'result-message fail'
|
||||
@pre stackTrace, class: 'stack-trace padded' if stackTrace
|
||||
|
||||
resultElement = document.createElement('div')
|
||||
resultElement.className = 'result-message fail'
|
||||
resultElement.textContent = result.message
|
||||
@specFailures.appendChild(resultElement)
|
||||
|
||||
if stackTrace
|
||||
traceElement = document.createElement('pre')
|
||||
traceElement.className = 'stack-trace padded'
|
||||
traceElement.textContent = stackTrace
|
||||
@specFailures.appendChild(traceElement)
|
||||
|
||||
attach: ->
|
||||
@parentSuiteView().append this
|
||||
@parentSuiteView().appendChild(@element)
|
||||
|
||||
parentSuiteView: ->
|
||||
if not suiteView = $("#suite-view-#{@spec.suite.id}").view()
|
||||
unless suiteViewElement = document.querySelector("#suite-view-#{@spec.suite.id}")
|
||||
suiteView = new SuiteResultView(@spec.suite)
|
||||
suiteView.attach()
|
||||
suiteViewElement = suiteView.element
|
||||
|
||||
suiteView
|
||||
suiteViewElement
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
Exec = require('child_process').exec
|
||||
path = require 'path'
|
||||
Package = require '../src/package'
|
||||
@@ -223,3 +222,31 @@ describe "the `atom` global", ->
|
||||
spyOn(atom, "pickFolder").andCallFake (callback) -> callback(null)
|
||||
atom.addProjectFolder()
|
||||
expect(atom.project.getPaths()).toEqual(initialPaths)
|
||||
|
||||
describe "::unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
workspaceState = atom.workspace.serialize()
|
||||
syntaxState = atom.grammars.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(atom.state.workspace).toEqual workspaceState
|
||||
expect(atom.state.grammars).toEqual syntaxState
|
||||
expect(atom.state.project).toEqual projectState
|
||||
expect(atom.saveSync).toHaveBeenCalled()
|
||||
|
||||
describe "::removeEditorWindow()", ->
|
||||
it "unsubscribes from all buffers", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js")
|
||||
|
||||
runs ->
|
||||
buffer = atom.workspace.getActivePaneItem().buffer
|
||||
pane = atom.workspace.getActivePane()
|
||||
pane.splitRight(copyActiveItem: true)
|
||||
expect(atom.workspace.getTextEditors().length).toBe 2
|
||||
|
||||
atom.removeEditorWindow()
|
||||
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
|
||||
@@ -115,6 +115,15 @@ describe "CommandRegistry", ->
|
||||
grandchild.dispatchEvent(dispatchedEvent)
|
||||
expect(dispatchedEvent.abortKeyBinding).toHaveBeenCalled()
|
||||
|
||||
it "copies non-standard properties from the original event to the synthetic event", ->
|
||||
syntheticEvent = null
|
||||
registry.add '.child', 'command', (event) -> syntheticEvent = event
|
||||
|
||||
dispatchedEvent = new CustomEvent('command', bubbles: true)
|
||||
dispatchedEvent.nonStandardProperty = 'testing'
|
||||
grandchild.dispatchEvent(dispatchedEvent)
|
||||
expect(syntheticEvent.nonStandardProperty).toBe 'testing'
|
||||
|
||||
it "allows listeners to be removed via a disposable returned by ::add", ->
|
||||
calls = []
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
{$$} = require '../src/space-pen-extensions'
|
||||
|
||||
ContextMenuManager = require '../src/context-menu-manager'
|
||||
|
||||
describe "ContextMenuManager", ->
|
||||
|
||||
@@ -281,6 +281,9 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe ".setEditorWidthInChars(length)", ->
|
||||
it "changes the length at which lines are wrapped and emits a change event for all screen lines", ->
|
||||
tokensText = (tokens) ->
|
||||
_.pluck(tokens, 'value').join('')
|
||||
|
||||
displayBuffer.setEditorWidthInChars(40)
|
||||
expect(tokensText displayBuffer.tokenizedLineForScreenRow(4).tokens).toBe ' left = [], right = [];'
|
||||
expect(tokensText displayBuffer.tokenizedLineForScreenRow(5).tokens).toBe ' while(items.length > 0) {'
|
||||
|
||||
55
spec/dom-element-pool-spec.coffee
Normal file
@@ -0,0 +1,55 @@
|
||||
DOMElementPool = require '../src/dom-element-pool'
|
||||
|
||||
describe "DOMElementPool", ->
|
||||
domElementPool = null
|
||||
|
||||
beforeEach ->
|
||||
domElementPool = new DOMElementPool
|
||||
|
||||
it "builds DOM nodes, recycling them when they are freed", ->
|
||||
[div, span1, span2, span3, span4, span5] = elements = [
|
||||
domElementPool.build("div")
|
||||
domElementPool.build("span")
|
||||
domElementPool.build("span")
|
||||
domElementPool.build("span")
|
||||
domElementPool.build("span")
|
||||
domElementPool.build("span")
|
||||
]
|
||||
|
||||
div.appendChild(span1)
|
||||
span1.appendChild(span2)
|
||||
div.appendChild(span3)
|
||||
span3.appendChild(span4)
|
||||
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
domElementPool.freeElementAndDescendants(span5)
|
||||
|
||||
expect(elements).toContain(domElementPool.build("div"))
|
||||
expect(elements).toContain(domElementPool.build("span"))
|
||||
expect(elements).toContain(domElementPool.build("span"))
|
||||
expect(elements).toContain(domElementPool.build("span"))
|
||||
expect(elements).toContain(domElementPool.build("span"))
|
||||
expect(elements).toContain(domElementPool.build("span"))
|
||||
|
||||
expect(elements).not.toContain(domElementPool.build("div"))
|
||||
expect(elements).not.toContain(domElementPool.build("span"))
|
||||
|
||||
it "forgets free nodes after being cleared", ->
|
||||
span = domElementPool.build("span")
|
||||
div = domElementPool.build("div")
|
||||
domElementPool.freeElementAndDescendants(span)
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
|
||||
domElementPool.clear()
|
||||
|
||||
expect(domElementPool.build("span")).not.toBe(span)
|
||||
expect(domElementPool.build("div")).not.toBe(div)
|
||||
|
||||
it "throws an error when trying to free the same node twice", ->
|
||||
div = domElementPool.build("div")
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
expect(-> domElementPool.freeElementAndDescendants(div)).toThrow()
|
||||
|
||||
it "throws an error when trying to free an invalid element", ->
|
||||
expect(-> domElementPool.freeElementAndDescendants(null)).toThrow()
|
||||
expect(-> domElementPool.freeElementAndDescendants(undefined)).toThrow()
|
||||
0
spec/fixtures/packages/package-with-cached-incompatible-native-module/main.js
vendored
Normal file
12
spec/fixtures/packages/package-with-cached-incompatible-native-module/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "package-with-cached-incompatible-native-module",
|
||||
"version": "1.0.0",
|
||||
"main": "./main.js",
|
||||
"_atomModuleCache": {
|
||||
"extensions": {
|
||||
".node": [
|
||||
"node_modules/native-module/build/Release/native.node"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
0
spec/fixtures/packages/package-with-ignored-incompatible-native-module/main.js
vendored
Normal file
0
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/compatible-native-module/main.js
generated
vendored
Normal file
4
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/compatible-native-module/package.json
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "compatible-native-module",
|
||||
"main": "./main.js"
|
||||
}
|
||||
0
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/native-module/build/Release/native.node
generated
vendored
Normal file
1
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/native-module/main.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
throw new Error("this simulates a native module's failure to load")
|
||||
4
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/native-module/package.json
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "native-module",
|
||||
"main": "./main.js"
|
||||
}
|
||||
12
spec/fixtures/packages/package-with-ignored-incompatible-native-module/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "package-with-ignored-incompatible-native-module",
|
||||
"version": "1.0.0",
|
||||
"main": "./main.js",
|
||||
"_atomModuleCache": {
|
||||
"extensions": {
|
||||
".node": [
|
||||
"node_modules/compatible-native-module/build/Release/native.node"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
Gutter = require '../src/gutter'
|
||||
GutterContainerComponent = require '../src/gutter-container-component'
|
||||
DOMElementPool = require '../src/dom-element-pool'
|
||||
|
||||
describe "GutterContainerComponent", ->
|
||||
[gutterContainerComponent] = []
|
||||
@@ -22,9 +23,10 @@ describe "GutterContainerComponent", ->
|
||||
mockTestState
|
||||
|
||||
beforeEach ->
|
||||
domElementPool = new DOMElementPool
|
||||
mockEditor = {}
|
||||
mockMouseDown = ->
|
||||
gutterContainerComponent = new GutterContainerComponent({editor: mockEditor, onMouseDown: mockMouseDown})
|
||||
gutterContainerComponent = new GutterContainerComponent({editor: mockEditor, onMouseDown: mockMouseDown, domElementPool})
|
||||
|
||||
it "creates a DOM node with no child gutter nodes when it is initialized", ->
|
||||
expect(gutterContainerComponent.getDomNode() instanceof HTMLElement).toBe true
|
||||
|
||||
@@ -9,7 +9,7 @@ webdriverio = require "../../../build/node_modules/webdriverio"
|
||||
|
||||
AtomPath = remote.process.argv[0]
|
||||
AtomLauncherPath = path.join(__dirname, "..", "helpers", "atom-launcher.sh")
|
||||
ChromedriverPath = path.resolve(__dirname, '..', '..', '..', 'atom-shell', 'chromedriver', 'chromedriver')
|
||||
ChromedriverPath = path.resolve(__dirname, '..', '..', '..', 'electron', 'chromedriver', 'chromedriver')
|
||||
SocketPath = path.join(temp.mkdirSync("socket-dir"), "atom-#{process.env.USER}.sock")
|
||||
ChromedriverPort = 9515
|
||||
ChromedriverURLBase = "/wd/hub"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
fs = require 'fs'
|
||||
|
||||
module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
window[key] = value for key, value of require '../vendor/jasmine'
|
||||
|
||||
{TerminalReporter} = require 'jasmine-tagged'
|
||||
@@ -47,7 +45,9 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
jasmineEnv.addReporter(timeReporter)
|
||||
jasmineEnv.setIncludedTags([process.platform])
|
||||
|
||||
$('body').append $$ -> @div id: 'jasmine-content'
|
||||
jasmineContent = document.createElement('div')
|
||||
jasmineContent.setAttribute('id', 'jasmine-content')
|
||||
document.body.appendChild(jasmineContent)
|
||||
|
||||
jasmineEnv.execute()
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
path = require 'path'
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
Package = require '../src/package'
|
||||
{Disposable} = require 'atom'
|
||||
|
||||
describe "PackageManager", ->
|
||||
workspaceElement = null
|
||||
|
||||
createTestElement = (className) ->
|
||||
element = document.createElement('div')
|
||||
element.className = className
|
||||
element
|
||||
|
||||
beforeEach ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
|
||||
@@ -209,33 +213,31 @@ describe "PackageManager", ->
|
||||
mainModule = null
|
||||
|
||||
it "defers requiring/activating the main module until an activation event bubbles to the root view", ->
|
||||
expect(promise.isFulfilled()).not.toBeTruthy()
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 0
|
||||
|
||||
workspaceElement.dispatchEvent(new CustomEvent('activation-command', bubbles: true))
|
||||
|
||||
waitsForPromise ->
|
||||
promise
|
||||
|
||||
runs ->
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 1
|
||||
|
||||
it "triggers the activation event on all handlers registered during activation", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
runs ->
|
||||
editorView = atom.views.getView(atom.workspace.getActiveTextEditor()).__spacePenView
|
||||
legacyCommandListener = jasmine.createSpy("legacyCommandListener")
|
||||
editorView.command 'activation-command', legacyCommandListener
|
||||
editorElement = atom.views.getView(atom.workspace.getActiveTextEditor())
|
||||
editorCommandListener = jasmine.createSpy("editorCommandListener")
|
||||
atom.commands.add 'atom-text-editor', 'activation-command', editorCommandListener
|
||||
atom.commands.dispatch(editorView[0], 'activation-command')
|
||||
atom.commands.dispatch(editorElement, 'activation-command')
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
expect(mainModule.legacyActivationCommandCallCount).toBe 1
|
||||
expect(mainModule.activationCommandCallCount).toBe 1
|
||||
expect(legacyCommandListener.callCount).toBe 1
|
||||
expect(editorCommandListener.callCount).toBe 1
|
||||
expect(workspaceCommandListener.callCount).toBe 1
|
||||
atom.commands.dispatch(editorView[0], 'activation-command')
|
||||
expect(mainModule.legacyActivationCommandCallCount).toBe 2
|
||||
atom.commands.dispatch(editorElement, 'activation-command')
|
||||
expect(mainModule.activationCommandCallCount).toBe 2
|
||||
expect(legacyCommandListener.callCount).toBe 2
|
||||
expect(editorCommandListener.callCount).toBe 2
|
||||
expect(workspaceCommandListener.callCount).toBe 2
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
@@ -301,8 +303,8 @@ describe "PackageManager", ->
|
||||
promise = atom.packages.activatePackage('package-with-activation-hooks')
|
||||
|
||||
it "defers requiring/activating the main module until an triggering of an activation hook occurs", ->
|
||||
expect(promise.isFulfilled()).not.toBeTruthy()
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 0
|
||||
|
||||
atom.packages.triggerActivationHook('language-fictitious:grammar-used')
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -399,36 +401,36 @@ describe "PackageManager", ->
|
||||
describe "keymap loading", ->
|
||||
describe "when the metadata does not contain a 'keymaps' manifest", ->
|
||||
it "loads all the .cson/.json files in the keymaps directory", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element2 = $$ -> @div class: 'test-2'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
element1 = createTestElement('test-1')
|
||||
element2 = createTestElement('test-2')
|
||||
element3 = createTestElement('test-3')
|
||||
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2)).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3)).toHaveLength 0
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-keymaps")
|
||||
|
||||
runs ->
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])[0].command).toBe "test-1"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2[0])[0].command).toBe "test-2"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe "test-1"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2)[0].command).toBe "test-2"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3)).toHaveLength 0
|
||||
|
||||
describe "when the metadata contains a 'keymaps' manifest", ->
|
||||
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
element1 = createTestElement('test-1')
|
||||
element3 = createTestElement('test-3')
|
||||
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-keymaps-manifest")
|
||||
|
||||
runs ->
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-n', target: element1[0])[0].command).toBe 'keymap-2'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-y', target: element3[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-n', target: element1)[0].command).toBe 'keymap-2'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-y', target: element3)).toHaveLength 0
|
||||
|
||||
describe "when the keymap file is empty", ->
|
||||
it "does not throw an error on activation", ->
|
||||
@@ -440,9 +442,9 @@ describe "PackageManager", ->
|
||||
|
||||
describe "when the package's keymaps have been disabled", ->
|
||||
it "does not add the keymaps", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element1 = createTestElement('test-1')
|
||||
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
|
||||
atom.config.set("core.packagesWithKeymapsDisabled", ["package-with-keymaps-manifest"])
|
||||
|
||||
@@ -450,11 +452,11 @@ describe "PackageManager", ->
|
||||
atom.packages.activatePackage("package-with-keymaps-manifest")
|
||||
|
||||
runs ->
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
|
||||
describe "when the package's keymaps are disabled and re-enabled after it is activated", ->
|
||||
it "removes and re-adds the keymaps", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element1 = createTestElement('test-1')
|
||||
atom.packages.observePackagesWithKeymapsDisabled()
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -462,10 +464,10 @@ describe "PackageManager", ->
|
||||
|
||||
runs ->
|
||||
atom.config.set("core.packagesWithKeymapsDisabled", ['package-with-keymaps-manifest'])
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
|
||||
atom.config.set("core.packagesWithKeymapsDisabled", [])
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe 'keymap-1'
|
||||
|
||||
describe "menu loading", ->
|
||||
beforeEach ->
|
||||
@@ -474,7 +476,7 @@ describe "PackageManager", ->
|
||||
|
||||
describe "when the metadata does not contain a 'menus' manifest", ->
|
||||
it "loads all the .cson/.json files in the menus directory", ->
|
||||
element = ($$ -> @div class: 'test-1')[0]
|
||||
element = createTestElement('test-1')
|
||||
|
||||
expect(atom.contextMenu.templateForElement(element)).toEqual []
|
||||
|
||||
@@ -491,7 +493,7 @@ describe "PackageManager", ->
|
||||
|
||||
describe "when the metadata contains a 'menus' manifest", ->
|
||||
it "loads only the menus specified by the manifest, in the specified order", ->
|
||||
element = ($$ -> @div class: 'test-1')[0]
|
||||
element = createTestElement('test-1')
|
||||
|
||||
expect(atom.contextMenu.templateForElement(element)).toEqual []
|
||||
|
||||
@@ -535,7 +537,8 @@ describe "PackageManager", ->
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
expect($('#jasmine-content').css('font-size')).toBe '1px'
|
||||
|
||||
expect(getComputedStyle(document.querySelector('#jasmine-content')).fontSize).toBe '1px'
|
||||
|
||||
describe "when the metadata does not contain a 'styleSheets' manifest", ->
|
||||
it "loads all style sheets from the styles directory", ->
|
||||
@@ -562,7 +565,7 @@ describe "PackageManager", ->
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(four)).not.toBeNull()
|
||||
expect($('#jasmine-content').css('font-size')).toBe '3px'
|
||||
expect(getComputedStyle(document.querySelector('#jasmine-content')).fontSize).toBe '3px'
|
||||
|
||||
it "assigns the stylesheet's context based on the filename", ->
|
||||
waitsForPromise ->
|
||||
@@ -747,8 +750,8 @@ describe "PackageManager", ->
|
||||
|
||||
runs ->
|
||||
atom.packages.deactivatePackage('package-with-keymaps')
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: ($$ -> @div class: 'test-1')[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: ($$ -> @div class: 'test-2')[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: createTestElement('test-1'))).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: createTestElement('test-2'))).toHaveLength 0
|
||||
|
||||
it "removes the package's stylesheets", ->
|
||||
waitsForPromise ->
|
||||
@@ -890,7 +893,7 @@ describe "PackageManager", ->
|
||||
# enabling of theme
|
||||
pack = atom.packages.enablePackage(packageName)
|
||||
|
||||
waitsFor ->
|
||||
waitsFor 'theme to enable', 500, ->
|
||||
pack in atom.packages.getActivePackages()
|
||||
|
||||
runs ->
|
||||
@@ -903,7 +906,7 @@ describe "PackageManager", ->
|
||||
|
||||
pack = atom.packages.disablePackage(packageName)
|
||||
|
||||
waitsFor ->
|
||||
waitsFor 'did-change-active-themes event to fire', 500, ->
|
||||
didChangeActiveThemesHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{$} = require '../src/space-pen-extensions'
|
||||
path = require 'path'
|
||||
Package = require '../src/package'
|
||||
ThemePackage = require '../src/theme-package'
|
||||
@@ -7,6 +6,10 @@ describe "Package", ->
|
||||
describe "when the package contains incompatible native modules", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'inDevMode').andReturn(false)
|
||||
items = {}
|
||||
spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item; undefined
|
||||
spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null
|
||||
spyOn(global.localStorage, 'removeItem').andCallFake (key) -> delete items[key]; undefined
|
||||
|
||||
it "does not activate it", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
|
||||
@@ -15,16 +18,18 @@ describe "Package", ->
|
||||
expect(pack.incompatibleModules[0].name).toBe 'native-module'
|
||||
expect(pack.incompatibleModules[0].path).toBe path.join(packagePath, 'node_modules', 'native-module')
|
||||
|
||||
it "utilizes _atomModuleCache if present to determine the package's native dependencies", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-ignored-incompatible-native-module')
|
||||
pack = new Package(packagePath)
|
||||
expect(pack.getNativeModuleDependencyPaths().length).toBe(1) # doesn't see the incompatible module
|
||||
expect(pack.isCompatible()).toBe true
|
||||
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-cached-incompatible-native-module')
|
||||
pack = new Package(packagePath)
|
||||
expect(pack.isCompatible()).toBe false
|
||||
|
||||
it "caches the incompatible native modules in local storage", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
|
||||
cacheKey = null
|
||||
cacheItem = null
|
||||
|
||||
spyOn(global.localStorage, 'setItem').andCallFake (key, item) ->
|
||||
cacheKey = key
|
||||
cacheItem = item
|
||||
spyOn(global.localStorage, 'getItem').andCallFake (key) ->
|
||||
return cacheItem if cacheKey is key
|
||||
|
||||
expect(new Package(packagePath).isCompatible()).toBe false
|
||||
expect(global.localStorage.getItem.callCount).toBe 1
|
||||
@@ -34,55 +39,121 @@ describe "Package", ->
|
||||
expect(global.localStorage.getItem.callCount).toBe 2
|
||||
expect(global.localStorage.setItem.callCount).toBe 1
|
||||
|
||||
describe "::rebuild()", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'inDevMode').andReturn(false)
|
||||
items = {}
|
||||
spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item; undefined
|
||||
spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null
|
||||
spyOn(global.localStorage, 'removeItem').andCallFake (key) -> delete items[key]; undefined
|
||||
|
||||
it "returns a promise resolving to the results of `apm rebuild`", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-index')
|
||||
pack = new Package(packagePath)
|
||||
rebuildCallbacks = []
|
||||
spyOn(pack, 'runRebuildProcess').andCallFake ((callback) -> rebuildCallbacks.push(callback))
|
||||
|
||||
promise = pack.rebuild()
|
||||
rebuildCallbacks[0]({code: 0, stdout: 'stdout output', stderr: 'stderr output'})
|
||||
|
||||
waitsFor (done) ->
|
||||
promise.then (result) ->
|
||||
expect(result).toEqual {code: 0, stdout: 'stdout output', stderr: 'stderr output'}
|
||||
done()
|
||||
|
||||
it "persists build failures in local storage", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-index')
|
||||
pack = new Package(packagePath)
|
||||
|
||||
expect(pack.isCompatible()).toBe true
|
||||
expect(pack.getBuildFailureOutput()).toBeNull()
|
||||
|
||||
rebuildCallbacks = []
|
||||
spyOn(pack, 'runRebuildProcess').andCallFake ((callback) -> rebuildCallbacks.push(callback))
|
||||
|
||||
pack.rebuild()
|
||||
rebuildCallbacks[0]({code: 13, stderr: 'It is broken'})
|
||||
|
||||
expect(pack.getBuildFailureOutput()).toBe 'It is broken'
|
||||
expect(pack.getIncompatibleNativeModules()).toEqual []
|
||||
expect(pack.isCompatible()).toBe false
|
||||
|
||||
# A different package instance has the same failure output (simulates reload)
|
||||
pack2 = new Package(packagePath)
|
||||
expect(pack2.getBuildFailureOutput()).toBe 'It is broken'
|
||||
expect(pack2.isCompatible()).toBe false
|
||||
|
||||
# Clears the build failure after a successful build
|
||||
pack.rebuild()
|
||||
rebuildCallbacks[1]({code: 0, stdout: 'It worked'})
|
||||
|
||||
expect(pack.getBuildFailureOutput()).toBeNull()
|
||||
expect(pack2.getBuildFailureOutput()).toBeNull()
|
||||
|
||||
it "sets cached incompatible modules to an empty array when the rebuild completes (there may be a build error, but rebuilding *deletes* native modules)", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
|
||||
pack = new Package(packagePath)
|
||||
|
||||
expect(pack.getIncompatibleNativeModules().length).toBeGreaterThan(0)
|
||||
|
||||
rebuildCallbacks = []
|
||||
spyOn(pack, 'runRebuildProcess').andCallFake ((callback) -> rebuildCallbacks.push(callback))
|
||||
|
||||
pack.rebuild()
|
||||
expect(pack.getIncompatibleNativeModules().length).toBeGreaterThan(0)
|
||||
rebuildCallbacks[0]({code: 0, stdout: 'It worked'})
|
||||
expect(pack.getIncompatibleNativeModules().length).toBe(0)
|
||||
|
||||
describe "theme", ->
|
||||
theme = null
|
||||
[editorElement, theme] = []
|
||||
|
||||
beforeEach ->
|
||||
$("#jasmine-content").append $("<atom-text-editor></atom-text-editor>")
|
||||
editorElement = document.createElement('atom-text-editor')
|
||||
jasmine.attachToDOM(editorElement)
|
||||
|
||||
afterEach ->
|
||||
theme.deactivate() if theme?
|
||||
|
||||
describe "when the theme contains a single style file", ->
|
||||
it "loads and applies css", ->
|
||||
expect($("atom-text-editor").css("padding-bottom")).not.toBe "1234px"
|
||||
expect(getComputedStyle(editorElement).paddingBottom).not.toBe "1234px"
|
||||
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-index-css')
|
||||
theme = new ThemePackage(themePath)
|
||||
theme.activate()
|
||||
expect($("atom-text-editor").css("padding-top")).toBe "1234px"
|
||||
expect(getComputedStyle(editorElement).paddingTop).toBe "1234px"
|
||||
|
||||
it "parses, loads and applies less", ->
|
||||
expect($("atom-text-editor").css("padding-bottom")).not.toBe "1234px"
|
||||
expect(getComputedStyle(editorElement).paddingBottom).not.toBe "1234px"
|
||||
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-index-less')
|
||||
theme = new ThemePackage(themePath)
|
||||
theme.activate()
|
||||
expect($("atom-text-editor").css("padding-top")).toBe "4321px"
|
||||
expect(getComputedStyle(editorElement).paddingTop).toBe "4321px"
|
||||
|
||||
describe "when the theme contains a package.json file", ->
|
||||
it "loads and applies stylesheets from package.json in the correct order", ->
|
||||
expect($("atom-text-editor").css("padding-top")).not.toBe("101px")
|
||||
expect($("atom-text-editor").css("padding-right")).not.toBe("102px")
|
||||
expect($("atom-text-editor").css("padding-bottom")).not.toBe("103px")
|
||||
expect(getComputedStyle(editorElement).paddingTop).not.toBe("101px")
|
||||
expect(getComputedStyle(editorElement).paddingRight).not.toBe("102px")
|
||||
expect(getComputedStyle(editorElement).paddingBottom).not.toBe("103px")
|
||||
|
||||
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-package-file')
|
||||
theme = new ThemePackage(themePath)
|
||||
theme.activate()
|
||||
expect($("atom-text-editor").css("padding-top")).toBe("101px")
|
||||
expect($("atom-text-editor").css("padding-right")).toBe("102px")
|
||||
expect($("atom-text-editor").css("padding-bottom")).toBe("103px")
|
||||
expect(getComputedStyle(editorElement).paddingTop).toBe("101px")
|
||||
expect(getComputedStyle(editorElement).paddingRight).toBe("102px")
|
||||
expect(getComputedStyle(editorElement).paddingBottom).toBe("103px")
|
||||
|
||||
describe "when the theme does not contain a package.json file and is a directory", ->
|
||||
it "loads all stylesheet files in the directory", ->
|
||||
expect($("atom-text-editor").css("padding-top")).not.toBe "10px"
|
||||
expect($("atom-text-editor").css("padding-right")).not.toBe "20px"
|
||||
expect($("atom-text-editor").css("padding-bottom")).not.toBe "30px"
|
||||
expect(getComputedStyle(editorElement).paddingTop).not.toBe "10px"
|
||||
expect(getComputedStyle(editorElement).paddingRight).not.toBe "20px"
|
||||
expect(getComputedStyle(editorElement).paddingBottom).not.toBe "30px"
|
||||
|
||||
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-without-package-file')
|
||||
theme = new ThemePackage(themePath)
|
||||
theme.activate()
|
||||
expect($("atom-text-editor").css("padding-top")).toBe "10px"
|
||||
expect($("atom-text-editor").css("padding-right")).toBe "20px"
|
||||
expect($("atom-text-editor").css("padding-bottom")).toBe "30px"
|
||||
expect(getComputedStyle(editorElement).paddingTop).toBe "10px"
|
||||
expect(getComputedStyle(editorElement).paddingRight).toBe "20px"
|
||||
expect(getComputedStyle(editorElement).paddingBottom).toBe "30px"
|
||||
|
||||
describe "reloading a theme", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -4,16 +4,13 @@ PaneAxis = require '../src/pane-axis'
|
||||
|
||||
describe "PaneContainerElement", ->
|
||||
describe "when panes are added or removed", ->
|
||||
[paneAxisElement, paneAxis] = []
|
||||
it "inserts or removes resize elements", ->
|
||||
childTagNames = ->
|
||||
child.nodeName.toLowerCase() for child in paneAxisElement.children
|
||||
|
||||
beforeEach ->
|
||||
paneAxis = new PaneAxis
|
||||
paneAxisElement = new PaneAxisElement().initialize(paneAxis)
|
||||
|
||||
childTagNames = ->
|
||||
child.nodeName.toLowerCase() for child in paneAxisElement.children
|
||||
|
||||
it "inserts or removes resize elements", ->
|
||||
expect(childTagNames()).toEqual []
|
||||
|
||||
paneAxis.addChild(new PaneAxis)
|
||||
@@ -44,6 +41,45 @@ describe "PaneContainerElement", ->
|
||||
'atom-pane-axis'
|
||||
]
|
||||
|
||||
it "transfers focus to the next pane if a focused pane is removed", ->
|
||||
container = new PaneContainer
|
||||
containerElement = atom.views.getView(container)
|
||||
leftPane = container.getActivePane()
|
||||
leftPaneElement = atom.views.getView(leftPane)
|
||||
rightPane = leftPane.splitRight()
|
||||
rightPaneElement = atom.views.getView(rightPane)
|
||||
|
||||
jasmine.attachToDOM(containerElement)
|
||||
rightPaneElement.focus()
|
||||
expect(document.activeElement).toBe rightPaneElement
|
||||
|
||||
rightPane.destroy()
|
||||
expect(document.activeElement).toBe leftPaneElement
|
||||
|
||||
describe "when a pane is split", ->
|
||||
it "builds appropriately-oriented atom-pane-axis elements", ->
|
||||
container = new PaneContainer
|
||||
containerElement = atom.views.getView(container)
|
||||
|
||||
pane1 = container.getRoot()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitDown()
|
||||
|
||||
horizontalPanes = containerElement.querySelectorAll('atom-pane-container > atom-pane-axis.horizontal > atom-pane')
|
||||
expect(horizontalPanes.length).toBe 1
|
||||
expect(horizontalPanes[0]).toBe atom.views.getView(pane1)
|
||||
|
||||
verticalPanes = containerElement.querySelectorAll('atom-pane-container > atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane')
|
||||
expect(verticalPanes.length).toBe 2
|
||||
expect(verticalPanes[0]).toBe atom.views.getView(pane2)
|
||||
expect(verticalPanes[1]).toBe atom.views.getView(pane3)
|
||||
|
||||
pane1.destroy()
|
||||
verticalPanes = containerElement.querySelectorAll('atom-pane-container > atom-pane-axis.vertical > atom-pane')
|
||||
expect(verticalPanes.length).toBe 2
|
||||
expect(verticalPanes[0]).toBe atom.views.getView(pane2)
|
||||
expect(verticalPanes[1]).toBe atom.views.getView(pane3)
|
||||
|
||||
describe "when the resize element is dragged ", ->
|
||||
[container, containerElement] = []
|
||||
|
||||
@@ -194,3 +230,96 @@ describe "PaneContainerElement", ->
|
||||
atom.commands.dispatch(atom.views.getView(rightPane), 'pane:decrease-size')
|
||||
expect(leftPane.getFlexScale()).toBe 1/1.1
|
||||
expect(rightPane.getFlexScale()).toBe 1/1.1
|
||||
|
||||
describe "changing focus directionally between panes", ->
|
||||
[containerElement, pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = []
|
||||
|
||||
beforeEach ->
|
||||
# Set up a grid of 9 panes, in the following arrangement, where the
|
||||
# numbers correspond to the variable names below.
|
||||
#
|
||||
# -------
|
||||
# |1|2|3|
|
||||
# -------
|
||||
# |4|5|6|
|
||||
# -------
|
||||
# |7|8|9|
|
||||
# -------
|
||||
|
||||
buildElement = (id) ->
|
||||
element = document.createElement('div')
|
||||
element.textContent = id
|
||||
element.tabIndex = -1
|
||||
element
|
||||
|
||||
container = new PaneContainer
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(buildElement('1'))
|
||||
pane4 = pane1.splitDown(items: [buildElement('4')])
|
||||
pane7 = pane4.splitDown(items: [buildElement('7')])
|
||||
|
||||
pane2 = pane1.splitRight(items: [buildElement('2')])
|
||||
pane3 = pane2.splitRight(items: [buildElement('3')])
|
||||
|
||||
pane5 = pane4.splitRight(items: [buildElement('5')])
|
||||
pane6 = pane5.splitRight(items: [buildElement('6')])
|
||||
|
||||
pane8 = pane7.splitRight(items: [buildElement('8')])
|
||||
pane9 = pane8.splitRight(items: [buildElement('9')])
|
||||
|
||||
containerElement = atom.views.getView(container)
|
||||
containerElement.style.height = '400px'
|
||||
containerElement.style.width = '400px'
|
||||
jasmine.attachToDOM(containerElement)
|
||||
|
||||
describe "::focusPaneViewAbove()", ->
|
||||
describe "when there are multiple rows above the focused pane", ->
|
||||
it "focuses up to the adjacent row", ->
|
||||
pane8.activate()
|
||||
containerElement.focusPaneViewAbove()
|
||||
expect(document.activeElement).toBe pane5.getActiveItem()
|
||||
|
||||
describe "when there are no rows above the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane2.activate()
|
||||
containerElement.focusPaneViewAbove()
|
||||
expect(document.activeElement).toBe pane2.getActiveItem()
|
||||
|
||||
describe "::focusPaneViewBelow()", ->
|
||||
describe "when there are multiple rows below the focused pane", ->
|
||||
it "focuses down to the adjacent row", ->
|
||||
pane2.activate()
|
||||
containerElement.focusPaneViewBelow()
|
||||
expect(document.activeElement).toBe pane5.getActiveItem()
|
||||
|
||||
describe "when there are no rows below the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane8.activate()
|
||||
containerElement.focusPaneViewBelow()
|
||||
expect(document.activeElement).toBe pane8.getActiveItem()
|
||||
|
||||
describe "::focusPaneViewOnLeft()", ->
|
||||
describe "when there are multiple columns to the left of the focused pane", ->
|
||||
it "focuses left to the adjacent column", ->
|
||||
pane6.activate()
|
||||
containerElement.focusPaneViewOnLeft()
|
||||
expect(document.activeElement).toBe pane5.getActiveItem()
|
||||
|
||||
describe "when there are no columns to the left of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane4.activate()
|
||||
containerElement.focusPaneViewOnLeft()
|
||||
expect(document.activeElement).toBe pane4.getActiveItem()
|
||||
|
||||
describe "::focusPaneViewOnRight()", ->
|
||||
describe "when there are multiple columns to the right of the focused pane", ->
|
||||
it "focuses right to the adjacent column", ->
|
||||
pane4.activate()
|
||||
containerElement.focusPaneViewOnRight()
|
||||
expect(document.activeElement).toBe pane5.getActiveItem()
|
||||
|
||||
describe "when there are no columns to the right of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane6.activate()
|
||||
containerElement.focusPaneViewOnRight()
|
||||
expect(document.activeElement).toBe pane6.getActiveItem()
|
||||
|
||||
@@ -40,6 +40,32 @@ describe "PaneContainer", ->
|
||||
containerB = atom.deserializers.deserialize(state)
|
||||
expect(containerB.getActivePane()).toBe containerB.getPanes()[0]
|
||||
|
||||
describe "if there are empty panes after deserialization", ->
|
||||
beforeEach ->
|
||||
pane3A.getItems()[0].serialize = -> deserializer: 'Bogus'
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is false (the default)", ->
|
||||
it "leaves the empty panes intact", ->
|
||||
state = containerA.serialize()
|
||||
containerB = atom.deserializers.deserialize(state)
|
||||
[leftPane, column] = containerB.getRoot().getChildren()
|
||||
[topPane, bottomPane] = column.getChildren()
|
||||
|
||||
expect(leftPane.getItems().length).toBe 1
|
||||
expect(topPane.getItems().length).toBe 1
|
||||
expect(bottomPane.getItems().length).toBe 0
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is true", ->
|
||||
it "removes empty panes on deserialization", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
|
||||
state = containerA.serialize()
|
||||
containerB = atom.deserializers.deserialize(state)
|
||||
[leftPane, rightPane] = containerB.getRoot().getChildren()
|
||||
|
||||
expect(leftPane.getItems().length).toBe 1
|
||||
expect(rightPane.getItems().length).toBe 1
|
||||
|
||||
it "does not allow the root pane to be destroyed", ->
|
||||
container = new PaneContainer
|
||||
container.getRoot().destroy()
|
||||
@@ -223,3 +249,19 @@ describe "PaneContainer", ->
|
||||
['will', {item: item2, pane: pane2, index: 0}]
|
||||
['did', {item: item2, pane: pane2, index: 0}]
|
||||
]
|
||||
|
||||
describe "::saveAll()", ->
|
||||
it "saves all open pane items", ->
|
||||
container = new PaneContainer
|
||||
pane1 = container.getRoot()
|
||||
pane2 = pane1.splitRight()
|
||||
|
||||
pane1.addItem(item1 = {getURI: (-> ''), save: -> @saved = true})
|
||||
pane1.addItem(item2 = {getURI: (-> ''), save: -> @saved = true})
|
||||
pane2.addItem(item3 = {getURI: (-> ''), save: -> @saved = true})
|
||||
|
||||
container.saveAll()
|
||||
|
||||
expect(item1.saved).toBe true
|
||||
expect(item2.saved).toBe true
|
||||
expect(item3.saved).toBe true
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
PaneContainer = require '../src/pane-container'
|
||||
PaneContainerView = require '../src/pane-container-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
{Disposable} = require 'event-kit'
|
||||
{$, View, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
describe "PaneContainerView", ->
|
||||
[TestView, container, pane1, pane2, pane3, deserializerDisposable] = []
|
||||
|
||||
beforeEach ->
|
||||
class TestView extends View
|
||||
deserializerDisposable = atom.deserializers.add(this)
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
serialize: -> {deserializer: 'TestView', @name}
|
||||
getURI: -> path.join(temp.dir, @name)
|
||||
save: -> @saved = true
|
||||
isEqual: (other) -> @name is other?.name
|
||||
onDidChangeTitle: -> new Disposable(->)
|
||||
onDidChangeModified: -> new Disposable(->)
|
||||
|
||||
container = atom.views.getView(atom.workspace.paneContainer).__spacePenView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(new TestView('1'))
|
||||
pane2 = pane1.splitRight(new TestView('2'))
|
||||
pane3 = pane2.splitDown(new TestView('3'))
|
||||
|
||||
afterEach ->
|
||||
deserializerDisposable.dispose()
|
||||
|
||||
describe ".getActivePaneView()", ->
|
||||
it "returns the most-recently focused pane", ->
|
||||
focusStealer = $$ -> @div tabindex: -1, "focus stealer"
|
||||
focusStealer.attachToDom()
|
||||
container.attachToDom()
|
||||
|
||||
pane2.focus()
|
||||
expect(container.getFocusedPane()).toBe pane2
|
||||
expect(container.getActivePaneView()).toBe pane2
|
||||
|
||||
focusStealer.focus()
|
||||
expect(container.getFocusedPane()).toBeUndefined()
|
||||
expect(container.getActivePaneView()).toBe pane2
|
||||
|
||||
pane3.focus()
|
||||
expect(container.getFocusedPane()).toBe pane3
|
||||
expect(container.getActivePaneView()).toBe pane3
|
||||
|
||||
describe ".eachPaneView(callback)", ->
|
||||
it "runs the callback with all current and future panes until the subscription is cancelled", ->
|
||||
panes = []
|
||||
subscription = container.eachPaneView (pane) -> panes.push(pane)
|
||||
expect(panes).toEqual [pane1, pane2, pane3]
|
||||
|
||||
panes = []
|
||||
pane4 = pane3.splitRight(pane3.copyActiveItem())
|
||||
expect(panes).toEqual [pane4]
|
||||
|
||||
panes = []
|
||||
subscription.off()
|
||||
pane4.splitDown()
|
||||
expect(panes).toEqual []
|
||||
|
||||
describe ".saveAll()", ->
|
||||
it "saves all open pane items", ->
|
||||
pane1.activateItem(new TestView('4'))
|
||||
|
||||
container.saveAll()
|
||||
|
||||
for pane in container.getPaneViews()
|
||||
for item in pane.getItems()
|
||||
expect(item.saved).toBeTruthy()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > :contains(1)')).toExist()
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > :contains(2)')).toExist()
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > :contains(3)')).toExist()
|
||||
|
||||
newContainer.height(200).width(300).attachToDom()
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > :contains(1)').width()).toBe 150
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > :contains(2)').height()).toBe 100
|
||||
|
||||
describe "if there are empty panes after deserialization", ->
|
||||
beforeEach ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is false (the default)", ->
|
||||
it "leaves the empty panes intact", ->
|
||||
newContainer = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > :contains(1)')).toExist()
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane').length).toBe 2
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is true", ->
|
||||
it "removes empty panes on deserialization", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
newContainer = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
expect(newContainer.find('atom-pane-axis.horizontal, atom-pane-axis.vertical')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
||||
describe "pane-container:active-pane-item-changed", ->
|
||||
[pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = []
|
||||
beforeEach ->
|
||||
item1a = new TestView('1a')
|
||||
item1b = new TestView('1b')
|
||||
item2a = new TestView('2a')
|
||||
item2b = new TestView('2b')
|
||||
item3a = new TestView('3a')
|
||||
|
||||
container = atom.views.getView(new PaneContainer).__spacePenView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(item1a)
|
||||
container.attachToDom()
|
||||
|
||||
activeItemChangedHandler = jasmine.createSpy("activeItemChangedHandler")
|
||||
container.on 'pane-container:active-pane-item-changed', activeItemChangedHandler
|
||||
|
||||
describe "when there is one pane", ->
|
||||
it "is triggered when a new pane item is added", ->
|
||||
pane1.activateItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1b
|
||||
|
||||
it "is not triggered when the active pane item is shown again", ->
|
||||
pane1.activateItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when switching to an existing pane item", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.activateItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is triggered when the active pane item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when all pane items are destroyed", ->
|
||||
pane1.destroyItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined
|
||||
|
||||
describe "when there are two panes", ->
|
||||
[pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitLeft(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane item is added to the active pane", ->
|
||||
pane2.activateItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2b
|
||||
|
||||
it "is not triggered when a new pane item is added to an inactive pane", ->
|
||||
pane1.activateItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane's active item is destroyed", ->
|
||||
pane2.activateItem(item2b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane2.destroyItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2a
|
||||
|
||||
it "is not triggered when an inactive pane's active item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is destroyed", ->
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane is destroyed", ->
|
||||
pane1.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is changed", ->
|
||||
pane1.activate()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
describe "when there are multiple panes", ->
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitRight(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane is added", ->
|
||||
pane2.splitDown(item3a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item3a
|
||||
|
||||
it "is not triggered when an inactive pane is destroyed", ->
|
||||
pane3 = pane2.splitDown(item3a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.remove()
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe ".focusNextPaneView()", ->
|
||||
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.focusNextPaneView()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPaneView()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPaneView()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPaneView()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPreviousPaneView()", ->
|
||||
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.getPaneViews()[0].focus() # activate first pane
|
||||
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "changing focus directionally between panes", ->
|
||||
[pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = []
|
||||
|
||||
beforeEach ->
|
||||
# Set up a grid of 9 panes, in the following arrangement, where the
|
||||
# numbers correspond to the variable names below.
|
||||
#
|
||||
# -------
|
||||
# |1|2|3|
|
||||
# -------
|
||||
# |4|5|6|
|
||||
# -------
|
||||
# |7|8|9|
|
||||
# -------
|
||||
|
||||
container = atom.views.getView(new PaneContainer).__spacePenView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(new TestView('1'))
|
||||
pane4 = pane1.splitDown(new TestView('4'))
|
||||
pane7 = pane4.splitDown(new TestView('7'))
|
||||
|
||||
pane2 = pane1.splitRight(new TestView('2'))
|
||||
pane3 = pane2.splitRight(new TestView('3'))
|
||||
|
||||
pane5 = pane4.splitRight(new TestView('5'))
|
||||
pane6 = pane5.splitRight(new TestView('6'))
|
||||
|
||||
pane8 = pane7.splitRight(new TestView('8'))
|
||||
pane9 = pane8.splitRight(new TestView('9'))
|
||||
|
||||
container.height(400)
|
||||
container.width(400)
|
||||
container.attachToDom()
|
||||
|
||||
describe ".focusPaneViewAbove()", ->
|
||||
describe "when there are multiple rows above the focused pane", ->
|
||||
it "focuses up to the adjacent row", ->
|
||||
pane8.focus()
|
||||
container.focusPaneViewAbove()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no rows above the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane2.focus()
|
||||
container.focusPaneViewAbove()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPaneViewBelow()", ->
|
||||
describe "when there are multiple rows below the focused pane", ->
|
||||
it "focuses down to the adjacent row", ->
|
||||
pane2.focus()
|
||||
container.focusPaneViewBelow()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no rows below the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane8.focus()
|
||||
container.focusPaneViewBelow()
|
||||
expect(pane8.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPaneViewOnLeft()", ->
|
||||
describe "when there are multiple columns to the left of the focused pane", ->
|
||||
it "focuses left to the adjacent column", ->
|
||||
pane6.focus()
|
||||
container.focusPaneViewOnLeft()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no columns to the left of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane4.focus()
|
||||
container.focusPaneViewOnLeft()
|
||||
expect(pane4.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPaneViewOnRight()", ->
|
||||
describe "when there are multiple columns to the right of the focused pane", ->
|
||||
it "focuses right to the adjacent column", ->
|
||||
pane4.focus()
|
||||
container.focusPaneViewOnRight()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no columns to the right of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane6.focus()
|
||||
container.focusPaneViewOnRight()
|
||||
expect(pane6.activeItem).toMatchSelector ':focus'
|
||||
197
spec/pane-element-spec.coffee
Normal file
@@ -0,0 +1,197 @@
|
||||
PaneContainer = require '../src/pane-container'
|
||||
|
||||
describe "PaneElement", ->
|
||||
[paneElement, container, pane] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer
|
||||
pane = container.getRoot()
|
||||
paneElement = atom.views.getView(pane)
|
||||
|
||||
describe "when the pane's active status changes", ->
|
||||
it "adds or removes the .active class as appropriate", ->
|
||||
pane2 = pane.splitRight()
|
||||
expect(pane2.isActive()).toBe true
|
||||
|
||||
expect(paneElement.className).not.toMatch /active/
|
||||
pane.activate()
|
||||
expect(paneElement.className).toMatch /active/
|
||||
pane2.activate()
|
||||
expect(paneElement.className).not.toMatch /active/
|
||||
|
||||
describe "when the active item changes", ->
|
||||
it "hides all item elements except the active one", ->
|
||||
item1 = document.createElement('div')
|
||||
item2 = document.createElement('div')
|
||||
item3 = document.createElement('div')
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
pane.addItem(item3)
|
||||
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(item1.parentElement).toBeDefined()
|
||||
expect(item1.style.display).toBe ''
|
||||
expect(item2.parentElement).toBeNull()
|
||||
expect(item3.parentElement).toBeNull()
|
||||
|
||||
pane.activateItem(item2)
|
||||
expect(item2.parentElement).toBeDefined()
|
||||
expect(item1.style.display).toBe 'none'
|
||||
expect(item2.style.display).toBe ''
|
||||
expect(item3.parentElement).toBeNull()
|
||||
|
||||
pane.activateItem(item3)
|
||||
expect(item3.parentElement).toBeDefined()
|
||||
expect(item1.style.display).toBe 'none'
|
||||
expect(item2.style.display).toBe 'none'
|
||||
expect(item3.style.display).toBe ''
|
||||
|
||||
it "transfers focus to the new item if the previous item was focused", ->
|
||||
item1 = document.createElement('div')
|
||||
item1.tabIndex = -1
|
||||
item2 = document.createElement('div')
|
||||
item2.tabIndex = -1
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
jasmine.attachToDOM(paneElement)
|
||||
paneElement.focus()
|
||||
|
||||
expect(document.activeElement).toBe item1
|
||||
pane.activateItem(item2)
|
||||
expect(document.activeElement).toBe item2
|
||||
|
||||
describe "if the active item is a model object", ->
|
||||
it "retrieves the associated view from atom.views and appends it to the itemViews div", ->
|
||||
class TestModel
|
||||
|
||||
atom.views.addViewProvider TestModel, (model) ->
|
||||
view = document.createElement('div')
|
||||
view.model = model
|
||||
view
|
||||
|
||||
item1 = new TestModel
|
||||
item2 = new TestModel
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
|
||||
expect(paneElement.itemViews.children[0].model).toBe item1
|
||||
expect(paneElement.itemViews.children[0].style.display).toBe ''
|
||||
pane.activateItem(item2)
|
||||
expect(paneElement.itemViews.children[1].model).toBe item2
|
||||
expect(paneElement.itemViews.children[0].style.display).toBe 'none'
|
||||
expect(paneElement.itemViews.children[1].style.display).toBe ''
|
||||
|
||||
describe "when the new active implements .getPath()", ->
|
||||
it "adds the file path and file name as a data attribute on the pane", ->
|
||||
item1 = document.createElement('div')
|
||||
item1.getPath = -> '/foo/bar.txt'
|
||||
item2 = document.createElement('div')
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
|
||||
expect(paneElement.dataset.activeItemPath).toBe '/foo/bar.txt'
|
||||
expect(paneElement.dataset.activeItemName).toBe 'bar.txt'
|
||||
|
||||
pane.activateItem(item2)
|
||||
|
||||
expect(paneElement.dataset.activeItemPath).toBeUndefined()
|
||||
expect(paneElement.dataset.activeItemName).toBeUndefined()
|
||||
|
||||
pane.activateItem(item1)
|
||||
expect(paneElement.dataset.activeItemPath).toBe '/foo/bar.txt'
|
||||
expect(paneElement.dataset.activeItemName).toBe 'bar.txt'
|
||||
|
||||
pane.destroyItems()
|
||||
expect(paneElement.dataset.activeItemPath).toBeUndefined()
|
||||
expect(paneElement.dataset.activeItemName).toBeUndefined()
|
||||
|
||||
describe "when an item is removed from the pane", ->
|
||||
describe "when the destroyed item is an element", ->
|
||||
it "removes the item from the itemViews div", ->
|
||||
item1 = document.createElement('div')
|
||||
item2 = document.createElement('div')
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
paneElement = atom.views.getView(pane)
|
||||
|
||||
expect(item1.parentElement).toBe paneElement.itemViews
|
||||
pane.destroyItem(item1)
|
||||
expect(item1.parentElement).toBeNull()
|
||||
expect(item2.parentElement).toBe paneElement.itemViews
|
||||
pane.destroyItem(item2)
|
||||
expect(item2.parentElement).toBeNull()
|
||||
|
||||
describe "when the destroyed item is a model", ->
|
||||
it "removes the model's associated view", ->
|
||||
class TestModel
|
||||
|
||||
atom.views.addViewProvider TestModel, (model) ->
|
||||
view = document.createElement('div')
|
||||
model.element = view
|
||||
view.model = model
|
||||
view
|
||||
|
||||
item1 = new TestModel
|
||||
item2 = new TestModel
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
|
||||
expect(item1.element.parentElement).toBe paneElement.itemViews
|
||||
pane.destroyItem(item1)
|
||||
expect(item1.element.parentElement).toBeNull()
|
||||
expect(item2.element.parentElement).toBe paneElement.itemViews
|
||||
pane.destroyItem(item2)
|
||||
expect(item2.element.parentElement).toBeNull()
|
||||
|
||||
describe "when the pane element is focused", ->
|
||||
it "transfers focus to the active view", ->
|
||||
item = document.createElement('div')
|
||||
item.tabIndex = -1
|
||||
pane.activateItem(item)
|
||||
jasmine.attachToDOM(paneElement)
|
||||
|
||||
expect(document.activeElement).toBe document.body
|
||||
paneElement.focus()
|
||||
expect(document.activeElement).toBe item
|
||||
|
||||
it "makes the pane active", ->
|
||||
pane.splitRight()
|
||||
expect(pane.isActive()).toBe false
|
||||
|
||||
jasmine.attachToDOM(paneElement)
|
||||
paneElement.focus()
|
||||
|
||||
expect(pane.isActive()).toBe true
|
||||
|
||||
describe "when the pane element is attached", ->
|
||||
it "focuses the pane element if isFocused() returns true on its model", ->
|
||||
pane.focus()
|
||||
jasmine.attachToDOM(paneElement)
|
||||
expect(document.activeElement).toBe paneElement
|
||||
|
||||
describe "drag and drop", ->
|
||||
buildDragEvent = (type, files) ->
|
||||
dataTransfer =
|
||||
files: files
|
||||
data: {}
|
||||
setData: (key, value) -> @data[key] = value
|
||||
getData: (key) -> @data[key]
|
||||
|
||||
event = new CustomEvent("drop")
|
||||
event.dataTransfer = dataTransfer
|
||||
event
|
||||
|
||||
describe "when a file is dragged to the pane", ->
|
||||
it "opens it", ->
|
||||
spyOn(atom, "open")
|
||||
event = buildDragEvent("drop", [{path: "/fake1"}, {path: "/fake2"}])
|
||||
paneElement.dispatchEvent(event)
|
||||
expect(atom.open.callCount).toBe 1
|
||||
expect(atom.open.argsForCall[0][0]).toEqual pathsToOpen: ['/fake1', '/fake2']
|
||||
|
||||
describe "when a non-file is dragged to the pane", ->
|
||||
it "does nothing", ->
|
||||
spyOn(atom, "open")
|
||||
event = buildDragEvent("drop", [])
|
||||
paneElement.dispatchEvent(event)
|
||||
expect(atom.open).not.toHaveBeenCalled()
|
||||
@@ -1,389 +0,0 @@
|
||||
PaneContainer = require '../src/pane-container'
|
||||
PaneView = require '../src/pane-view'
|
||||
fs = require 'fs-plus'
|
||||
{Emitter, Disposable} = require 'event-kit'
|
||||
{$, View} = require '../src/space-pen-extensions'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "PaneView", ->
|
||||
[container, containerModel, view1, view2, editor1, editor2, pane, paneModel, deserializerDisposable] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({id, text}) -> new TestView({id, text})
|
||||
@content: ({id, text}) -> @div class: 'test-view', id: id, tabindex: -1, text
|
||||
initialize: ({@id, @text}) ->
|
||||
@emitter = new Emitter
|
||||
serialize: -> {deserializer: 'TestView', @id, @text}
|
||||
getURI: -> @id
|
||||
isEqual: (other) -> other? and @id is other.id and @text is other.text
|
||||
changeTitle: ->
|
||||
@emitter.emit 'did-change-title', 'title'
|
||||
onDidChangeTitle: (callback) ->
|
||||
@emitter.on 'did-change-title', callback
|
||||
onDidChangeModified: -> new Disposable(->)
|
||||
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
deserializerDisposable = atom.deserializers.add(TestView)
|
||||
container = atom.views.getView(new PaneContainer).__spacePenView
|
||||
containerModel = container.model
|
||||
view1 = new TestView(id: 'view-1', text: 'View 1')
|
||||
view2 = new TestView(id: 'view-2', text: 'View 2')
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor1 = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
pane = container.getRoot()
|
||||
paneModel = pane.getModel()
|
||||
paneModel.addItems([view1, editor1, view2, editor2])
|
||||
|
||||
afterEach ->
|
||||
deserializerDisposable.dispose()
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
describe "when the active pane item changes", ->
|
||||
it "hides all item views except the active one", ->
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
expect(view1.css('display')).not.toBe 'none'
|
||||
|
||||
pane.activateItem(view2)
|
||||
expect(view1.css('display')).toBe 'none'
|
||||
expect(view2.css('display')).not.toBe 'none'
|
||||
|
||||
it "triggers 'pane:active-item-changed'", ->
|
||||
itemChangedHandler = jasmine.createSpy("itemChangedHandler")
|
||||
container.on 'pane:active-item-changed', itemChangedHandler
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
paneModel.activateItem(view2)
|
||||
paneModel.activateItem(view2)
|
||||
|
||||
expect(itemChangedHandler.callCount).toBe 1
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe view2
|
||||
itemChangedHandler.reset()
|
||||
|
||||
paneModel.activateItem(editor1)
|
||||
expect(itemChangedHandler).toHaveBeenCalled()
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe editor1
|
||||
itemChangedHandler.reset()
|
||||
|
||||
it "transfers focus to the new active view if the previous view was focused", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
expect(pane.activeView).not.toBe view2
|
||||
expect(pane.activeView).toMatchSelector ':focus'
|
||||
paneModel.activateItem(view2)
|
||||
expect(view2).toMatchSelector ':focus'
|
||||
|
||||
describe "when the new activeItem is a model", ->
|
||||
it "shows the item's view or creates and shows a new view for the item if none exists", ->
|
||||
initialViewCount = pane.itemViews.find('.test-view').length
|
||||
|
||||
model1 =
|
||||
id: 'test-model-1'
|
||||
text: 'Test Model 1'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
model2 =
|
||||
id: 'test-model-2'
|
||||
text: 'Test Model 2'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
paneModel.activateItem(model1)
|
||||
paneModel.activateItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
paneModel.activatePreviousItem()
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
paneModel.destroyItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1
|
||||
|
||||
paneModel.destroyItem(model1)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount
|
||||
|
||||
describe "when the new activeItem is a view", ->
|
||||
it "appends it to the itemViews div if it hasn't already been appended and shows it", ->
|
||||
expect(pane.itemViews.find('#view-2')).not.toExist()
|
||||
paneModel.activateItem(view2)
|
||||
expect(pane.itemViews.find('#view-2')).toExist()
|
||||
paneModel.activateItem(view1)
|
||||
paneModel.activateItem(view2)
|
||||
expect(pane.itemViews.find('#view-2').length).toBe 1
|
||||
|
||||
describe "when the new activeItem implements ::getPath", ->
|
||||
beforeEach ->
|
||||
paneModel.activateItem(editor1)
|
||||
|
||||
it "adds the file path as a data attribute to the pane", ->
|
||||
expect(pane).toHaveAttr('data-active-item-path')
|
||||
|
||||
it "adds the file name as a data attribute to the pane", ->
|
||||
expect(pane).toHaveAttr('data-active-item-name')
|
||||
|
||||
describe "when the activeItem is destroyed", ->
|
||||
it "removes the data attributes", ->
|
||||
pane.destroyItems()
|
||||
expect(pane).not.toHaveAttr('data-active-item-path')
|
||||
expect(pane).not.toHaveAttr('data-active-item-name')
|
||||
|
||||
describe "when the new activeItem does not implement ::getPath", ->
|
||||
beforeEach ->
|
||||
paneModel.activateItem(editor1)
|
||||
paneModel.activateItem(document.createElement('div'))
|
||||
|
||||
it "does not add the file path as a data attribute to the pane", ->
|
||||
expect(pane).not.toHaveAttr('data-active-item-path')
|
||||
|
||||
it "does not add the file name as data attribute to the pane", ->
|
||||
expect(pane).not.toHaveAttr('data-active-item-name')
|
||||
|
||||
describe "when an item is destroyed", ->
|
||||
it "triggers the 'pane:item-removed' event with the item and its former index", ->
|
||||
itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
|
||||
pane.on 'pane:item-removed', itemRemovedHandler
|
||||
paneModel.destroyItem(editor1)
|
||||
expect(itemRemovedHandler).toHaveBeenCalled()
|
||||
expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1]
|
||||
|
||||
describe "when the destroyed item is a view", ->
|
||||
it "removes the item from the 'item-views' div", ->
|
||||
expect(view1.parent()).toMatchSelector pane.itemViews
|
||||
paneModel.destroyItem(view1)
|
||||
expect(view1.parent()).not.toMatchSelector pane.itemViews
|
||||
|
||||
describe "when the destroyed item is a model", ->
|
||||
it "removes the associated view", ->
|
||||
paneModel.activateItem(editor1)
|
||||
expect(pane.itemViews.find('atom-text-editor').length).toBe 1
|
||||
pane.destroyItem(editor1)
|
||||
expect(pane.itemViews.find('atom-text-editor').length).toBe 0
|
||||
|
||||
describe "when an item is moved within the same pane", ->
|
||||
it "emits a 'pane:item-moved' event with the item and the new index", ->
|
||||
pane.on 'pane:item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler")
|
||||
paneModel.moveItem(view1, 2)
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2]
|
||||
|
||||
describe "when an item is moved to another pane", ->
|
||||
it "detaches the item's view rather than removing it", ->
|
||||
container.attachToDom()
|
||||
expect(view1.is(':visible')).toBe true
|
||||
paneModel2 = paneModel.splitRight()
|
||||
view1.data('preservative', 1234)
|
||||
paneModel.moveItemToPane(view1, paneModel2, 1)
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
paneModel2.activateItemAtIndex(1)
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
expect(view1.is(':visible')).toBe true
|
||||
|
||||
describe "when the title of the active item changes", ->
|
||||
describe 'when there is no onDidChangeTitle method (deprecated)', ->
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
view1.onDidChangeTitle = null
|
||||
view2.onDidChangeTitle = null
|
||||
|
||||
pane.activateItem(view2)
|
||||
pane.activateItem(view1)
|
||||
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
view1.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe 'when there is a onDidChangeTitle method', ->
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
view2.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
view1.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when an unmodifed buffer's path is deleted", ->
|
||||
it "removes the pane item", ->
|
||||
editor = null
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
filePath = path.join(temp.mkdirSync(), 'file.txt')
|
||||
fs.writeFileSync(filePath, '')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
pane.activateItem(editor)
|
||||
expect(pane.items).toHaveLength(5)
|
||||
fs.removeSync(filePath)
|
||||
|
||||
waitsFor ->
|
||||
pane.items.length is 4
|
||||
|
||||
describe "when a pane is destroyed", ->
|
||||
[pane2, pane2Model] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2Model = paneModel.splitRight() # Can't destroy the last pane, so we add another
|
||||
pane2 = atom.views.getView(pane2Model).__spacePenView
|
||||
|
||||
it "triggers a 'pane:removed' event with the pane", ->
|
||||
removedHandler = jasmine.createSpy("removedHandler")
|
||||
container.on 'pane:removed', removedHandler
|
||||
paneModel.destroy()
|
||||
expect(removedHandler).toHaveBeenCalled()
|
||||
expect(removedHandler.argsForCall[0][1]).toBe pane
|
||||
|
||||
describe "if the destroyed pane has focus", ->
|
||||
[paneToLeft, paneToRight] = []
|
||||
|
||||
it "focuses the next pane", ->
|
||||
container.attachToDom()
|
||||
pane2.activate()
|
||||
expect(pane.hasFocus()).toBe false
|
||||
expect(pane2.hasFocus()).toBe true
|
||||
pane2Model.destroy()
|
||||
expect(pane.hasFocus()).toBe true
|
||||
|
||||
describe "::getNextPane()", ->
|
||||
it "returns the next pane if one exists, wrapping around from the last pane to the first", ->
|
||||
pane.activateItem(editor1)
|
||||
expect(pane.getNextPane()).toBeUndefined
|
||||
pane2 = pane.splitRight(pane.copyActiveItem())
|
||||
expect(pane.getNextPane()).toBe pane2
|
||||
expect(pane2.getNextPane()).toBe pane
|
||||
|
||||
describe "when the pane's active status changes", ->
|
||||
[pane2, pane2Model] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2Model = paneModel.splitRight(items: [pane.copyActiveItem()])
|
||||
pane2 = atom.views.getView(pane2Model).__spacePenView
|
||||
expect(pane2Model.isActive()).toBe true
|
||||
|
||||
it "adds or removes the .active class as appropriate", ->
|
||||
expect(pane).not.toHaveClass('active')
|
||||
paneModel.activate()
|
||||
expect(pane).toHaveClass('active')
|
||||
pane2Model.activate()
|
||||
expect(pane).not.toHaveClass('active')
|
||||
|
||||
it "triggers 'pane:became-active' or 'pane:became-inactive' according to the current status", ->
|
||||
pane.on 'pane:became-active', becameActiveHandler = jasmine.createSpy("becameActiveHandler")
|
||||
pane.on 'pane:became-inactive', becameInactiveHandler = jasmine.createSpy("becameInactiveHandler")
|
||||
paneModel.activate()
|
||||
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
expect(becameInactiveHandler.callCount).toBe 0
|
||||
|
||||
pane2Model.activate()
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
expect(becameInactiveHandler.callCount).toBe 1
|
||||
|
||||
describe "when the pane is focused", ->
|
||||
beforeEach ->
|
||||
container.attachToDom()
|
||||
|
||||
it "transfers focus to the active view", ->
|
||||
focusHandler = jasmine.createSpy("focusHandler")
|
||||
pane.getActiveItem().on 'focus', focusHandler
|
||||
pane.focus()
|
||||
expect(focusHandler).toHaveBeenCalled()
|
||||
|
||||
it "makes the pane active", ->
|
||||
paneModel.splitRight(items: [pane.copyActiveItem()])
|
||||
expect(paneModel.isActive()).toBe false
|
||||
pane.focus()
|
||||
expect(paneModel.isActive()).toBe true
|
||||
|
||||
describe "when a pane is split", ->
|
||||
it "builds the appropriateatom-pane-axis.horizontal and pane-column views", ->
|
||||
pane1 = pane
|
||||
pane1Model = pane.getModel()
|
||||
pane.activateItem(editor1)
|
||||
|
||||
pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()])
|
||||
pane3Model = pane2Model.splitDown(items: [pane2Model.copyActiveItem()])
|
||||
pane2 = pane2Model._view
|
||||
pane2 = atom.views.getView(pane2Model).__spacePenView
|
||||
pane3 = atom.views.getView(pane3Model).__spacePenView
|
||||
|
||||
expect(container.find('> atom-pane-axis.horizontal > atom-pane').toArray()).toEqual [pane1[0]]
|
||||
expect(container.find('> atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane').toArray()).toEqual [pane2[0], pane3[0]]
|
||||
|
||||
pane1Model.destroy()
|
||||
expect(container.find('> atom-pane-axis.vertical > atom-pane').toArray()).toEqual [pane2[0], pane3[0]]
|
||||
|
||||
describe "serialization", ->
|
||||
it "focuses the pane after attach only if had focus when serialized", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
|
||||
container2 = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
pane2 = container2.getRoot()
|
||||
container2.attachToDom()
|
||||
expect(pane2).toMatchSelector(':has(:focus)')
|
||||
|
||||
$(document.activeElement).blur()
|
||||
container3 = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
pane3 = container3.getRoot()
|
||||
container3.attachToDom()
|
||||
expect(pane3).not.toMatchSelector(':has(:focus)')
|
||||
|
||||
describe "drag and drop", ->
|
||||
buildDragEvent = (type, files) ->
|
||||
dataTransfer =
|
||||
files: files
|
||||
data: {}
|
||||
setData: (key, value) -> @data[key] = value
|
||||
getData: (key) -> @data[key]
|
||||
|
||||
event = new CustomEvent("drop")
|
||||
event.dataTransfer = dataTransfer
|
||||
event
|
||||
|
||||
describe "when a file is dragged to window", ->
|
||||
it "opens it", ->
|
||||
spyOn(atom, "open")
|
||||
event = buildDragEvent("drop", [ {path: "/fake1"}, {path: "/fake2"} ])
|
||||
pane[0].dispatchEvent(event)
|
||||
expect(atom.open.callCount).toBe 1
|
||||
expect(atom.open.argsForCall[0][0]).toEqual pathsToOpen: ['/fake1', '/fake2']
|
||||
|
||||
describe "when a non-file is dragged to window", ->
|
||||
it "does nothing", ->
|
||||
spyOn(atom, "open")
|
||||
event = buildDragEvent("drop", [])
|
||||
pane[0].dispatchEvent(event)
|
||||
expect(atom.open).not.toHaveBeenCalled()
|
||||
@@ -201,18 +201,6 @@ describe "Project", ->
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
|
||||
it "returns number of read bytes as progress indicator", ->
|
||||
filePath = atom.project.getDirectories()[0]?.resolve 'a'
|
||||
totalBytes = 0
|
||||
promise = atom.project.open(filePath)
|
||||
promise.progress (bytesRead) -> totalBytes = bytesRead
|
||||
|
||||
waitsForPromise ->
|
||||
promise
|
||||
|
||||
runs ->
|
||||
expect(totalBytes).toBe fs.statSync(filePath).size
|
||||
|
||||
describe ".bufferForPath(path)", ->
|
||||
[buffer] = []
|
||||
beforeEach ->
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
SelectListView = require '../src/select-list-view'
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
describe "SelectListView", ->
|
||||
[selectList, items, list, filterEditorView] = []
|
||||
|
||||
beforeEach ->
|
||||
items = [
|
||||
["A", "Alpha"], ["B", "Bravo"], ["C", "Charlie"],
|
||||
["D", "Delta"], ["E", "Echo"], ["F", "Foxtrot"]
|
||||
]
|
||||
|
||||
selectList = new SelectListView
|
||||
selectList.setMaxItems(4)
|
||||
selectList.getFilterKey = -> 1
|
||||
selectList.viewForItem = (item) ->
|
||||
$$ -> @li item[1], class: item[0]
|
||||
|
||||
selectList.confirmed = jasmine.createSpy('confirmed hook')
|
||||
selectList.cancelled = jasmine.createSpy('cancelled hook')
|
||||
|
||||
selectList.setItems(items)
|
||||
{list, filterEditorView} = selectList
|
||||
|
||||
describe "when an array is assigned", ->
|
||||
it "populates the list with up to maxItems items, based on the liForElement function", ->
|
||||
expect(list.find('li').length).toBe selectList.maxItems
|
||||
expect(list.find('li:eq(0)')).toHaveText 'Alpha'
|
||||
expect(list.find('li:eq(0)')).toHaveClass 'A'
|
||||
|
||||
describe "viewForItem(item)", ->
|
||||
it "allows raw DOM elements to be returned", ->
|
||||
selectList.viewForItem = (item) ->
|
||||
li = document.createElement('li')
|
||||
li.classList.add(item[0])
|
||||
li.innerText = item[1]
|
||||
li
|
||||
|
||||
selectList.setItems(items)
|
||||
|
||||
expect(list.find('li').length).toBe selectList.maxItems
|
||||
expect(list.find('li:eq(0)')).toHaveText 'Alpha'
|
||||
expect(list.find('li:eq(0)')).toHaveClass 'A'
|
||||
expect(selectList.getSelectedItem()).toBe items[0]
|
||||
|
||||
it "allows raw HTML to be returned", ->
|
||||
selectList.viewForItem = (item) ->
|
||||
"<li>#{item}</li>"
|
||||
|
||||
selectList.setItems(['Bermuda', 'Bahama'])
|
||||
|
||||
expect(list.find('li:eq(0)')).toHaveText 'Bermuda'
|
||||
expect(selectList.getSelectedItem()).toBe 'Bermuda'
|
||||
|
||||
describe "when the text of the mini editor changes", ->
|
||||
beforeEach ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "filters the elements in the list based on the scoreElement function and selects the first item", ->
|
||||
filterEditorView.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
expect(list.find('li:contains(Alpha)')).toExist()
|
||||
expect(list.find('li:contains(Delta)')).toExist()
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
expect(selectList.error).not.toBeVisible()
|
||||
|
||||
it "displays an error if there are no matches, removes error when there are matches", ->
|
||||
filterEditorView.getEditor().insertText('nothing will match this')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
expect(selectList.error).not.toBeHidden()
|
||||
|
||||
filterEditorView.getEditor().setText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
expect(selectList.error).not.toBeVisible()
|
||||
|
||||
it "displays no elements until the array has been set on the list", ->
|
||||
selectList.items = null
|
||||
selectList.list.empty()
|
||||
filterEditorView.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
expect(selectList.error).toBeHidden()
|
||||
selectList.setItems(items)
|
||||
expect(list.find('li').length).toBe 2
|
||||
|
||||
describe "when core:move-up / core:move-down are triggered on the filterEditorView", ->
|
||||
it "selects the previous / next item in the list, or wraps around to the other side", ->
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-up'
|
||||
|
||||
expect(list.find('li:first')).not.toHaveClass 'selected'
|
||||
expect(list.find('li:last')).toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
expect(list.find('li:last')).not.toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
|
||||
expect(list.find('li:eq(0)')).not.toHaveClass 'selected'
|
||||
expect(list.find('li:eq(1)')).toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
|
||||
expect(list.find('li:eq(1)')).not.toHaveClass 'selected'
|
||||
expect(list.find('li:eq(2)')).toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-up'
|
||||
|
||||
expect(list.find('li:eq(2)')).not.toHaveClass 'selected'
|
||||
expect(list.find('li:eq(1)')).toHaveClass 'selected'
|
||||
|
||||
it "scrolls to keep the selected item in view", ->
|
||||
selectList.attachToDom()
|
||||
itemHeight = list.find('li').outerHeight()
|
||||
list.height(itemHeight * 2)
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
expect(list.scrollBottom()).toBe itemHeight * 3
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
expect(list.scrollBottom()).toBe itemHeight * 4
|
||||
|
||||
filterEditorView.trigger 'core:move-up'
|
||||
filterEditorView.trigger 'core:move-up'
|
||||
expect(list.scrollTop()).toBe itemHeight
|
||||
|
||||
describe "the core:confirm event", ->
|
||||
describe "when there is an item selected (because the list in not empty)", ->
|
||||
it "triggers the selected hook with the selected array element", ->
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
filterEditorView.trigger 'core:confirm'
|
||||
expect(selectList.confirmed).toHaveBeenCalledWith(items[2])
|
||||
|
||||
describe "when there is no item selected (because the list is empty)", ->
|
||||
beforeEach ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "does not trigger the confirmed hook", ->
|
||||
filterEditorView.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
filterEditorView.trigger 'core:confirm'
|
||||
expect(selectList.confirmed).not.toHaveBeenCalled()
|
||||
|
||||
it "does trigger the cancelled hook", ->
|
||||
filterEditorView.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
filterEditorView.trigger 'core:confirm'
|
||||
expect(selectList.cancelled).toHaveBeenCalled()
|
||||
|
||||
describe "when a list item is clicked", ->
|
||||
it "selects the item on mousedown and confirms it on mouseup", ->
|
||||
item = list.find('li:eq(1)')
|
||||
|
||||
item.mousedown()
|
||||
expect(item).toHaveClass 'selected'
|
||||
item.mouseup()
|
||||
|
||||
expect(selectList.confirmed).toHaveBeenCalledWith(items[1])
|
||||
|
||||
describe "the core:cancel event", ->
|
||||
it "triggers the cancelled hook and detaches and empties the select list", ->
|
||||
spyOn(selectList, 'detach')
|
||||
filterEditorView.trigger 'core:cancel'
|
||||
expect(selectList.cancelled).toHaveBeenCalled()
|
||||
expect(selectList.detach).toHaveBeenCalled()
|
||||
expect(selectList.list).toBeEmpty()
|
||||
|
||||
describe "when the mini editor loses focus", ->
|
||||
it "triggers the cancelled hook and detaches the select list", ->
|
||||
spyOn(selectList, 'detach')
|
||||
filterEditorView.trigger 'blur'
|
||||
expect(selectList.cancelled).toHaveBeenCalled()
|
||||
expect(selectList.detach).toHaveBeenCalled()
|
||||
|
||||
describe "the core:move-to-top event", ->
|
||||
it "scrolls to the top, selects the first element, and does not bubble the event", ->
|
||||
selectList.attachToDom()
|
||||
moveToTopHandler = jasmine.createSpy("moveToTopHandler")
|
||||
selectList.parent().on 'core:move-to-top', moveToTopHandler
|
||||
|
||||
selectList.trigger 'core:move-down'
|
||||
expect(list.find('li:eq(1)')).toHaveClass 'selected'
|
||||
selectList.trigger 'core:move-to-top'
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
expect(moveToTopHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "the core:move-to-bottom event", ->
|
||||
it "scrolls to the bottom, selects the last element, and does not bubble the event", ->
|
||||
selectList.attachToDom()
|
||||
moveToBottomHandler = jasmine.createSpy("moveToBottomHandler")
|
||||
selectList.parent().on 'core:move-to-bottom', moveToBottomHandler
|
||||
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
selectList.trigger 'core:move-to-bottom'
|
||||
expect(list.find('li:last')).toHaveClass 'selected'
|
||||
expect(moveToBottomHandler).not.toHaveBeenCalled()
|
||||
@@ -56,6 +56,19 @@ describe "Selection", ->
|
||||
selection.selectToScreenPosition([0, 25])
|
||||
expect(selection.isReversed()).toBeFalsy()
|
||||
|
||||
describe ".selectLine(row)", ->
|
||||
describe "when passed a row", ->
|
||||
it "selects the specified row", ->
|
||||
selection.setBufferRange([[2, 4], [3, 4]])
|
||||
selection.selectLine(5)
|
||||
expect(selection.getBufferRange()).toEqual [[5, 0], [6, 0]]
|
||||
|
||||
describe "when not passed a row", ->
|
||||
it "selects all rows spanned by the selection", ->
|
||||
selection.setBufferRange([[2, 4], [3, 4]])
|
||||
selection.selectLine()
|
||||
expect(selection.getBufferRange()).toEqual [[2, 0], [4, 0]]
|
||||
|
||||
describe "when only the selection's tail is moved (regression)", ->
|
||||
it "notifies ::onDidChangeRange observers", ->
|
||||
selection.setBufferRange([[2, 0], [2, 10]], reversed: true)
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
{View, $, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
describe "SpacePen extensions", ->
|
||||
class TestView extends View
|
||||
@content: -> @div()
|
||||
|
||||
[view, parent] = []
|
||||
|
||||
beforeEach ->
|
||||
view = new TestView
|
||||
parent = $$ -> @div()
|
||||
parent.append(view)
|
||||
|
||||
describe "View.subscribe(eventEmitter, eventName, callback)", ->
|
||||
[emitter, eventHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
eventHandler = jasmine.createSpy 'eventHandler'
|
||||
emitter = $$ -> @div()
|
||||
view.subscribe emitter, 'foo', eventHandler
|
||||
|
||||
it "subscribes to the given event emitter and unsubscribes when unsubscribe is called", ->
|
||||
emitter.trigger "foo"
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
describe "tooltips", ->
|
||||
describe "when the window is resized", ->
|
||||
it "hides the tooltips", ->
|
||||
class TooltipView extends View
|
||||
@content: ->
|
||||
@div()
|
||||
|
||||
view = new TooltipView()
|
||||
view.attachToDom()
|
||||
view.setTooltip('this is a tip')
|
||||
|
||||
view.tooltip('show')
|
||||
expect($(document.body).find('.tooltip')).toBeVisible()
|
||||
|
||||
$(window).trigger('resize')
|
||||
expect($(document.body).find('.tooltip')).not.toExist()
|
||||
@@ -10,16 +10,12 @@ fs = require 'fs-plus'
|
||||
Grim = require 'grim'
|
||||
KeymapManager = require '../src/keymap-extensions'
|
||||
|
||||
# FIXME: Remove jquery from this
|
||||
{$} = require '../src/space-pen-extensions'
|
||||
|
||||
Config = require '../src/config'
|
||||
{Point} = require 'text-buffer'
|
||||
Project = require '../src/project'
|
||||
Workspace = require '../src/workspace'
|
||||
ServiceHub = require 'service-hub'
|
||||
TextEditor = require '../src/text-editor'
|
||||
TextEditorView = require '../src/text-editor-view'
|
||||
TextEditorElement = require '../src/text-editor-element'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
TextEditorComponent = require '../src/text-editor-component'
|
||||
@@ -41,7 +37,9 @@ window.addEventListener 'core:close', -> window.close()
|
||||
window.addEventListener 'beforeunload', ->
|
||||
atom.storeWindowDimensions()
|
||||
atom.saveSync()
|
||||
$('html,body').css('overflow', 'auto')
|
||||
|
||||
document.querySelector('html').style.overflow = 'auto'
|
||||
document.body.style.overflow = 'auto'
|
||||
|
||||
# Allow document.title to be assigned in specs without screwing up spec window title
|
||||
documentTitle = null
|
||||
@@ -91,7 +89,6 @@ if specDirectory
|
||||
isCoreSpec = specDirectory is fs.realpathSync(__dirname)
|
||||
|
||||
beforeEach ->
|
||||
$.fx.off = true
|
||||
documentTitle = null
|
||||
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
|
||||
atom.packages.serviceHub = new ServiceHub
|
||||
@@ -102,7 +99,7 @@ beforeEach ->
|
||||
atom.styles.restoreSnapshot(styleElementsToRestore)
|
||||
atom.views.clearDocumentRequests()
|
||||
|
||||
atom.workspaceViewParentSelector = '#jasmine-content'
|
||||
atom.workspaceParentSelectorctor = '#jasmine-content'
|
||||
|
||||
window.resetTimeouts()
|
||||
spyOn(_._, "now").andCallFake -> window.now
|
||||
@@ -171,7 +168,6 @@ afterEach ->
|
||||
|
||||
atom.workspace?.destroy()
|
||||
atom.workspace = null
|
||||
atom.__workspaceView = null
|
||||
delete atom.state.workspace
|
||||
|
||||
atom.project?.destroy()
|
||||
@@ -181,7 +177,7 @@ afterEach ->
|
||||
|
||||
delete atom.state.packageStates
|
||||
|
||||
$('#jasmine-content').empty() unless window.debugContent
|
||||
document.getElementById('jasmine-content').innerHTML = '' unless window.debugContent
|
||||
|
||||
jasmine.unspy(atom, 'saveSync')
|
||||
ensureNoPathSubscriptions()
|
||||
@@ -279,43 +275,6 @@ addCustomMatchers = (spec) ->
|
||||
@message = -> return "Expected element '#{element}' or its descendants#{notText} to show."
|
||||
element.style.display in ['block', 'inline-block', 'static', 'fixed']
|
||||
|
||||
window.keyIdentifierForKey = (key) ->
|
||||
if key.length > 1 # named key
|
||||
key
|
||||
else
|
||||
charCode = key.toUpperCase().charCodeAt(0)
|
||||
"U+00" + charCode.toString(16)
|
||||
|
||||
window.keydownEvent = (key, properties={}) ->
|
||||
originalEventProperties = {}
|
||||
originalEventProperties.ctrl = properties.ctrlKey
|
||||
originalEventProperties.alt = properties.altKey
|
||||
originalEventProperties.shift = properties.shiftKey
|
||||
originalEventProperties.cmd = properties.metaKey
|
||||
originalEventProperties.target = properties.target?[0] ? properties.target
|
||||
originalEventProperties.which = properties.which
|
||||
originalEvent = KeymapManager.buildKeydownEvent(key, originalEventProperties)
|
||||
properties = $.extend({originalEvent}, properties)
|
||||
$.Event("keydown", properties)
|
||||
|
||||
window.mouseEvent = (type, properties) ->
|
||||
if properties.point
|
||||
{point, editorView} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editorView, point)
|
||||
properties.pageX = left + 1
|
||||
properties.pageY = top + 1
|
||||
properties.originalEvent ?= {detail: 1}
|
||||
$.Event type, properties
|
||||
|
||||
window.clickEvent = (properties={}) ->
|
||||
window.mouseEvent("click", properties)
|
||||
|
||||
window.mousedownEvent = (properties={}) ->
|
||||
window.mouseEvent('mousedown', properties)
|
||||
|
||||
window.mousemoveEvent = (properties={}) ->
|
||||
window.mouseEvent('mousemove', properties)
|
||||
|
||||
window.waitsForPromise = (args...) ->
|
||||
if args.length > 1
|
||||
{shouldReject, timeout} = args[0]
|
||||
@@ -374,45 +333,3 @@ window.advanceClock = (delta=1) ->
|
||||
true
|
||||
|
||||
callback() for callback in callbacks
|
||||
|
||||
window.pagePixelPositionForPoint = (editorView, point) ->
|
||||
point = Point.fromObject point
|
||||
top = editorView.renderedLines.offset().top + point.row * editorView.lineHeight
|
||||
left = editorView.renderedLines.offset().left + point.column * editorView.charWidth - editorView.renderedLines.scrollLeft()
|
||||
{top, left}
|
||||
|
||||
window.tokensText = (tokens) ->
|
||||
_.pluck(tokens, 'value').join('')
|
||||
|
||||
window.setEditorWidthInChars = (editorView, widthInChars, charWidth=editorView.charWidth) ->
|
||||
editorView.width(charWidth * widthInChars + editorView.gutter.outerWidth())
|
||||
$(window).trigger 'resize' # update width of editor view's on-screen lines
|
||||
|
||||
window.setEditorHeightInLines = (editorView, heightInLines, lineHeight=editorView.lineHeight) ->
|
||||
editorView.height(editorView.getEditor().getLineHeightInPixels() * heightInLines)
|
||||
editorView.component?.measureDimensions()
|
||||
|
||||
$.fn.resultOfTrigger = (type) ->
|
||||
event = $.Event(type)
|
||||
this.trigger(event)
|
||||
event.result
|
||||
|
||||
$.fn.enableKeymap = ->
|
||||
@on 'keydown', (e) ->
|
||||
originalEvent = e.originalEvent ? e
|
||||
Object.defineProperty(originalEvent, 'target', get: -> e.target) unless originalEvent.target?
|
||||
atom.keymaps.handleKeyboardEvent(originalEvent)
|
||||
not e.originalEvent.defaultPrevented
|
||||
|
||||
$.fn.attachToDom = ->
|
||||
@appendTo($('#jasmine-content')) unless @isOnDom()
|
||||
|
||||
$.fn.simulateDomAttachment = ->
|
||||
$('<html>').append(this)
|
||||
|
||||
$.fn.textInput = (data) ->
|
||||
this.each ->
|
||||
event = document.createEvent('TextEvent')
|
||||
event.initTextEvent('textInput', true, true, window, data)
|
||||
event = $.event.fix(event)
|
||||
$(this).trigger(event)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
_ = require 'underscore-plus'
|
||||
{extend, flatten, toArray, last} = _
|
||||
|
||||
TextEditorView = require '../src/text-editor-view'
|
||||
TextEditorComponent = require '../src/text-editor-component'
|
||||
TextEditorElement = require '../src/text-editor-element'
|
||||
nbsp = String.fromCharCode(160)
|
||||
|
||||
describe "TextEditorComponent", ->
|
||||
[contentNode, editor, wrapperView, wrapperNode, component, componentNode, verticalScrollbarNode, horizontalScrollbarNode] = []
|
||||
[contentNode, editor, wrapperNode, component, componentNode, verticalScrollbarNode, horizontalScrollbarNode] = []
|
||||
[lineHeightInPixels, charWidth, nextAnimationFrame, noAnimationFrame, tileSize, tileHeightInPixels] = []
|
||||
|
||||
beforeEach ->
|
||||
@@ -34,12 +33,13 @@ describe "TextEditorComponent", ->
|
||||
contentNode = document.querySelector('#jasmine-content')
|
||||
contentNode.style.width = '1000px'
|
||||
|
||||
wrapperView = new TextEditorView(editor, {tileSize})
|
||||
wrapperView.attachToDom()
|
||||
wrapperNode = wrapperView.element
|
||||
wrapperNode = new TextEditorElement()
|
||||
wrapperNode.tileSize = tileSize
|
||||
wrapperNode.initialize(editor)
|
||||
wrapperNode.setUpdatedSynchronously(false)
|
||||
jasmine.attachToDOM(wrapperNode)
|
||||
|
||||
{component} = wrapperView
|
||||
{component} = wrapperNode
|
||||
component.setFontFamily('monospace')
|
||||
component.setLineHeight(1.3)
|
||||
component.setFontSize(20)
|
||||
@@ -108,7 +108,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes[0].style.zIndex).toBe("2")
|
||||
expect(tilesNodes[1].style.zIndex).toBe("1")
|
||||
@@ -118,7 +118,7 @@ describe "TextEditorComponent", ->
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes[0].style.zIndex).toBe("3")
|
||||
expect(tilesNodes[1].style.zIndex).toBe("2")
|
||||
@@ -130,7 +130,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes.length).toBe(3)
|
||||
|
||||
@@ -158,7 +158,7 @@ describe "TextEditorComponent", ->
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(component.lineNodeForScreenRow(2)).toBeUndefined()
|
||||
expect(tilesNodes.length).toBe(3)
|
||||
@@ -187,7 +187,7 @@ describe "TextEditorComponent", ->
|
||||
editor.getBuffer().deleteRows(0, 1)
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
|
||||
expectTileContainsRow(tilesNodes[0], 0, top: 0 * lineHeightInPixels)
|
||||
@@ -202,7 +202,7 @@ describe "TextEditorComponent", ->
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
|
||||
expectTileContainsRow(tilesNodes[0], 0, top: 0 * lineHeightInPixels)
|
||||
@@ -294,7 +294,7 @@ describe "TextEditorComponent", ->
|
||||
editorFullWidth = editor.getScrollWidth() + editor.getVerticalScrollbarWidth()
|
||||
|
||||
for lineNode in lineNodes
|
||||
expect(lineNode.style.width).toBe editorFullWidth + 'px'
|
||||
expect(lineNode.getBoundingClientRect().width).toBe(editorFullWidth)
|
||||
|
||||
componentNode.style.width = gutterWidth + editor.getScrollWidth() + 100 + 'px'
|
||||
component.measureDimensions()
|
||||
@@ -302,7 +302,7 @@ describe "TextEditorComponent", ->
|
||||
scrollViewWidth = scrollViewNode.offsetWidth
|
||||
|
||||
for lineNode in lineNodes
|
||||
expect(lineNode.style.width).toBe scrollViewWidth + 'px'
|
||||
expect(lineNode.getBoundingClientRect().width).toBe(scrollViewWidth)
|
||||
|
||||
it "renders an nbsp on empty lines when no line-ending character is defined", ->
|
||||
atom.config.set("editor.showInvisibles", false)
|
||||
@@ -313,7 +313,7 @@ describe "TextEditorComponent", ->
|
||||
backgroundColor = getComputedStyle(wrapperNode).backgroundColor
|
||||
expect(linesNode.style.backgroundColor).toBe backgroundColor
|
||||
|
||||
for tileNode in linesNode.querySelectorAll(".tile")
|
||||
for tileNode in component.tileNodesForLines()
|
||||
expect(tileNode.style.backgroundColor).toBe(backgroundColor)
|
||||
|
||||
wrapperNode.style.backgroundColor = 'rgb(255, 0, 0)'
|
||||
@@ -322,7 +322,7 @@ describe "TextEditorComponent", ->
|
||||
advanceClock(atom.views.documentPollingInterval)
|
||||
nextAnimationFrame()
|
||||
expect(linesNode.style.backgroundColor).toBe 'rgb(255, 0, 0)'
|
||||
for tileNode in linesNode.querySelectorAll(".tile")
|
||||
for tileNode in component.tileNodesForLines()
|
||||
expect(tileNode.style.backgroundColor).toBe("rgb(255, 0, 0)")
|
||||
|
||||
|
||||
@@ -370,6 +370,24 @@ describe "TextEditorComponent", ->
|
||||
expect(leafNodes[0].classList.contains('trailing-whitespace')).toBe true
|
||||
expect(leafNodes[0].classList.contains('leading-whitespace')).toBe false
|
||||
|
||||
it "keeps rebuilding lines when continuous reflow is on", ->
|
||||
wrapperNode.setContinuousReflow(true)
|
||||
|
||||
oldLineNodes = componentNode.querySelectorAll(".line")
|
||||
|
||||
advanceClock(10)
|
||||
expect(nextAnimationFrame).toBe(noAnimationFrame)
|
||||
|
||||
advanceClock(component.presenter.minimumReflowInterval - 10)
|
||||
nextAnimationFrame()
|
||||
|
||||
newLineNodes = componentNode.querySelectorAll(".line")
|
||||
expect(oldLineNodes).not.toEqual(newLineNodes)
|
||||
|
||||
wrapperNode.setContinuousReflow(false)
|
||||
advanceClock(component.presenter.minimumReflowInterval)
|
||||
expect(nextAnimationFrame).toBe(noAnimationFrame)
|
||||
|
||||
describe "when showInvisibles is enabled", ->
|
||||
invisibles = null
|
||||
|
||||
@@ -606,7 +624,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLineNumbers()
|
||||
|
||||
expect(tilesNodes[0].style.zIndex).toBe("2")
|
||||
expect(tilesNodes[1].style.zIndex).toBe("1")
|
||||
@@ -616,33 +634,13 @@ describe "TextEditorComponent", ->
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLineNumbers()
|
||||
|
||||
expect(tilesNodes[0].style.zIndex).toBe("3")
|
||||
expect(tilesNodes[1].style.zIndex).toBe("2")
|
||||
expect(tilesNodes[2].style.zIndex).toBe("1")
|
||||
expect(tilesNodes[3].style.zIndex).toBe("0")
|
||||
|
||||
it "renders higher line numbers in front of lower ones", ->
|
||||
wrapperNode.style.height = 6.5 * lineHeightInPixels + 'px'
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
# Tile 0
|
||||
expect(component.lineNumberNodeForScreenRow(0).style.zIndex).toBe("2")
|
||||
expect(component.lineNumberNodeForScreenRow(1).style.zIndex).toBe("1")
|
||||
expect(component.lineNumberNodeForScreenRow(2).style.zIndex).toBe("0")
|
||||
|
||||
# Tile 1
|
||||
expect(component.lineNumberNodeForScreenRow(3).style.zIndex).toBe("2")
|
||||
expect(component.lineNumberNodeForScreenRow(4).style.zIndex).toBe("1")
|
||||
expect(component.lineNumberNodeForScreenRow(5).style.zIndex).toBe("0")
|
||||
|
||||
# Tile 2
|
||||
expect(component.lineNumberNodeForScreenRow(6).style.zIndex).toBe("2")
|
||||
expect(component.lineNumberNodeForScreenRow(7).style.zIndex).toBe("1")
|
||||
expect(component.lineNumberNodeForScreenRow(8).style.zIndex).toBe("0")
|
||||
|
||||
it "gives the line numbers container the same height as the wrapper node", ->
|
||||
linesNode = componentNode.querySelector(".line-numbers")
|
||||
|
||||
@@ -663,7 +661,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLineNumbers()
|
||||
|
||||
expect(tilesNodes.length).toBe(3)
|
||||
expect(tilesNodes[0].style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
|
||||
@@ -689,7 +687,7 @@ describe "TextEditorComponent", ->
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".line-numbers").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLineNumbers()
|
||||
|
||||
expect(component.lineNumberNodeForScreenRow(2)).toBeUndefined()
|
||||
expect(tilesNodes.length).toBe(3)
|
||||
@@ -791,7 +789,7 @@ describe "TextEditorComponent", ->
|
||||
lineNumbersNode = gutterNode.querySelector('.line-numbers')
|
||||
{backgroundColor} = getComputedStyle(wrapperNode)
|
||||
expect(lineNumbersNode.style.backgroundColor).toBe backgroundColor
|
||||
for tileNode in lineNumbersNode.querySelectorAll(".tile")
|
||||
for tileNode in component.tileNodesForLineNumbers()
|
||||
expect(tileNode.style.backgroundColor).toBe(backgroundColor)
|
||||
|
||||
# favor gutter color if it's assigned
|
||||
@@ -800,7 +798,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
nextAnimationFrame()
|
||||
expect(lineNumbersNode.style.backgroundColor).toBe 'rgb(255, 0, 0)'
|
||||
for tileNode in lineNumbersNode.querySelectorAll(".tile")
|
||||
for tileNode in component.tileNodesForLineNumbers()
|
||||
expect(tileNode.style.backgroundColor).toBe("rgb(255, 0, 0)")
|
||||
|
||||
it "hides or shows the gutter based on the '::isLineNumberGutterVisible' property on the model and the global 'editor.showLineNumbers' config setting", ->
|
||||
@@ -827,6 +825,24 @@ describe "TextEditorComponent", ->
|
||||
expect(componentNode.querySelector('.gutter').style.display).toBe ''
|
||||
expect(component.lineNumberNodeForScreenRow(3)?).toBe true
|
||||
|
||||
it "keeps rebuilding line numbers when continuous reflow is on", ->
|
||||
wrapperNode.setContinuousReflow(true)
|
||||
|
||||
oldLineNodes = componentNode.querySelectorAll(".line-number")
|
||||
|
||||
advanceClock(10)
|
||||
expect(nextAnimationFrame).toBe(noAnimationFrame)
|
||||
|
||||
advanceClock(component.presenter.minimumReflowInterval - 10)
|
||||
nextAnimationFrame()
|
||||
|
||||
newLineNodes = componentNode.querySelectorAll(".line-number")
|
||||
expect(oldLineNodes).not.toEqual(newLineNodes)
|
||||
|
||||
wrapperNode.setContinuousReflow(false)
|
||||
advanceClock(component.presenter.minimumReflowInterval)
|
||||
expect(nextAnimationFrame).toBe(noAnimationFrame)
|
||||
|
||||
describe "fold decorations", ->
|
||||
describe "rendering fold decorations", ->
|
||||
it "adds the foldable class to line numbers when the line is foldable", ->
|
||||
@@ -1006,10 +1022,11 @@ describe "TextEditorComponent", ->
|
||||
cursor = componentNode.querySelector('.cursor')
|
||||
cursorRect = cursor.getBoundingClientRect()
|
||||
|
||||
cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.source.js').firstChild
|
||||
cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.source.js').childNodes[2]
|
||||
|
||||
range = document.createRange()
|
||||
range.setStart(cursorLocationTextNode, 3)
|
||||
range.setEnd(cursorLocationTextNode, 4)
|
||||
range.setStart(cursorLocationTextNode, 0)
|
||||
range.setEnd(cursorLocationTextNode, 1)
|
||||
rangeRect = range.getBoundingClientRect()
|
||||
|
||||
expect(cursorRect.left).toBe rangeRect.left
|
||||
@@ -1133,7 +1150,7 @@ describe "TextEditorComponent", ->
|
||||
it "renders 2 regions for 2-line selections", ->
|
||||
editor.setSelectedScreenRange([[1, 6], [2, 10]])
|
||||
nextAnimationFrame()
|
||||
tileNode = componentNode.querySelector(".lines").querySelectorAll(".tile")[0]
|
||||
tileNode = component.tileNodesForLines()[0]
|
||||
regions = tileNode.querySelectorAll('.selection .region')
|
||||
expect(regions.length).toBe 2
|
||||
|
||||
@@ -1154,7 +1171,7 @@ describe "TextEditorComponent", ->
|
||||
nextAnimationFrame()
|
||||
|
||||
# Tile 0
|
||||
tileNode = componentNode.querySelector(".lines").querySelectorAll(".tile")[0]
|
||||
tileNode = component.tileNodesForLines()[0]
|
||||
regions = tileNode.querySelectorAll('.selection .region')
|
||||
expect(regions.length).toBe(3)
|
||||
|
||||
@@ -1177,7 +1194,7 @@ describe "TextEditorComponent", ->
|
||||
expect(region3Rect.right).toBe tileNode.getBoundingClientRect().right
|
||||
|
||||
# Tile 3
|
||||
tileNode = componentNode.querySelector(".lines").querySelectorAll(".tile")[1]
|
||||
tileNode = component.tileNodesForLines()[1]
|
||||
regions = tileNode.querySelectorAll('.selection .region')
|
||||
expect(regions.length).toBe(3)
|
||||
|
||||
@@ -1850,7 +1867,7 @@ describe "TextEditorComponent", ->
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [10, 0]]
|
||||
|
||||
it "autoscrolls when the cursor exceeds the boundaries of the editor", ->
|
||||
it "autoscrolls when the cursor approaches the boundaries of the editor", ->
|
||||
wrapperNode.style.height = '100px'
|
||||
wrapperNode.style.width = '100px'
|
||||
component.measureDimensions()
|
||||
@@ -1860,26 +1877,26 @@ describe "TextEditorComponent", ->
|
||||
expect(editor.getScrollLeft()).toBe(0)
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', {clientX: 0, clientY: 0}, which: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 150, clientY: 50}, which: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 100, clientY: 50}, which: 1))
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(editor.getScrollTop()).toBe(0)
|
||||
expect(editor.getScrollLeft()).toBeGreaterThan(0)
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 150, clientY: 150}, which: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 100, clientY: 100}, which: 1))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getScrollTop()).toBeGreaterThan(0)
|
||||
|
||||
previousScrollTop = editor.getScrollTop()
|
||||
previousScrollLeft = editor.getScrollLeft()
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: -20, clientY: 50}, which: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 10, clientY: 50}, which: 1))
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(editor.getScrollTop()).toBe(previousScrollTop)
|
||||
expect(editor.getScrollLeft()).toBeLessThan(previousScrollLeft)
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: -20, clientY: -20}, which: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 10, clientY: 10}, which: 1))
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(editor.getScrollTop()).toBeLessThan(previousScrollTop)
|
||||
@@ -1898,16 +1915,13 @@ describe "TextEditorComponent", ->
|
||||
expect(nextAnimationFrame).toBe noAnimationFrame
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [6, 8]]
|
||||
|
||||
it "stops selecting if a textInput event occurs during the drag", ->
|
||||
it "stops selecting before the buffer is modified during the drag", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([2, 4]), which: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([6, 8]), which: 1))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [6, 8]]
|
||||
|
||||
inputEvent = new Event('textInput')
|
||||
inputEvent.data = 'x'
|
||||
Object.defineProperty(inputEvent, 'target', get: -> componentNode.querySelector('.hidden-input'))
|
||||
componentNode.dispatchEvent(inputEvent)
|
||||
editor.insertText('x')
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 5], [2, 5]]
|
||||
@@ -1916,6 +1930,20 @@ describe "TextEditorComponent", ->
|
||||
expect(nextAnimationFrame).toBe noAnimationFrame
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 5], [2, 5]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([2, 4]), which: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([5, 4]), which: 1))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [5, 4]]
|
||||
|
||||
editor.delete()
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [2, 4]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([8, 0]), which: 1))
|
||||
expect(nextAnimationFrame).toBe noAnimationFrame
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [2, 4]]
|
||||
|
||||
describe "when the command key is held down", ->
|
||||
it "adds a new selection and selects to the nearest screen position, then merges intersecting selections when the mouse button is released", ->
|
||||
editor.setSelectedScreenRange([[4, 4], [4, 9]])
|
||||
@@ -1995,7 +2023,7 @@ describe "TextEditorComponent", ->
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([8, 4]), which: 1))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [9, 0]]
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [8, 0]]
|
||||
expect(editor.getScrollTop()).toBe maximalScrollTop # does not autoscroll upward (regression)
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenPosition([9, 3]), which: 1))
|
||||
@@ -2086,7 +2114,7 @@ describe "TextEditorComponent", ->
|
||||
nextAnimationFrame()
|
||||
expect(editor.getLastSelection().isReversed()).toBe false
|
||||
|
||||
it "autoscrolls when the cursor exceeds the top or bottom of the editor", ->
|
||||
it "autoscrolls when the cursor approaches the top or bottom of the editor", ->
|
||||
wrapperNode.style.height = 6 * lineHeightInPixels + 'px'
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
@@ -2100,11 +2128,12 @@ describe "TextEditorComponent", ->
|
||||
expect(editor.getScrollTop()).toBeGreaterThan 0
|
||||
maxScrollTop = editor.getScrollTop()
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(5)))
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(10)))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getScrollTop()).toBe maxScrollTop
|
||||
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(2)))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(7)))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getScrollTop()).toBeLessThan maxScrollTop
|
||||
|
||||
@@ -2355,11 +2384,11 @@ describe "TextEditorComponent", ->
|
||||
inputNode.focus()
|
||||
nextAnimationFrame()
|
||||
expect(componentNode.classList.contains('is-focused')).toBe true
|
||||
expect(wrapperView.hasClass('is-focused')).toBe true
|
||||
expect(wrapperNode.classList.contains('is-focused')).toBe true
|
||||
inputNode.blur()
|
||||
nextAnimationFrame()
|
||||
expect(componentNode.classList.contains('is-focused')).toBe false
|
||||
expect(wrapperView.hasClass('is-focused')).toBe false
|
||||
expect(wrapperNode.classList.contains('is-focused')).toBe false
|
||||
|
||||
describe "selection handling", ->
|
||||
cursor = null
|
||||
@@ -2396,7 +2425,7 @@ describe "TextEditorComponent", ->
|
||||
component.measureDimensions()
|
||||
nextAnimationFrame()
|
||||
|
||||
tilesNodes = componentNode.querySelector(".lines").querySelectorAll(".tile")
|
||||
tilesNodes = component.tileNodesForLines()
|
||||
|
||||
top = 0
|
||||
for tileNode in tilesNodes
|
||||
@@ -2867,17 +2896,18 @@ describe "TextEditorComponent", ->
|
||||
describe "hiding and showing the editor", ->
|
||||
describe "when the editor is hidden when it is mounted", ->
|
||||
it "defers measurement and rendering until the editor becomes visible", ->
|
||||
wrapperView.remove()
|
||||
wrapperNode.remove()
|
||||
|
||||
hiddenParent = document.createElement('div')
|
||||
hiddenParent.style.display = 'none'
|
||||
contentNode.appendChild(hiddenParent)
|
||||
|
||||
wrapperView = new TextEditorView(editor, {tileSize})
|
||||
wrapperNode = wrapperView.element
|
||||
wrapperView.appendTo(hiddenParent)
|
||||
wrapperNode = new TextEditorElement()
|
||||
wrapperNode.tileSize = tileSize
|
||||
wrapperNode.initialize(editor)
|
||||
hiddenParent.appendChild(wrapperNode)
|
||||
|
||||
{component} = wrapperView
|
||||
{component} = wrapperNode
|
||||
componentNode = component.getDomNode()
|
||||
expect(componentNode.querySelectorAll('.line').length).toBe 0
|
||||
|
||||
@@ -2888,18 +2918,25 @@ describe "TextEditorComponent", ->
|
||||
|
||||
describe "when the lineHeight changes while the editor is hidden", ->
|
||||
it "does not attempt to measure the lineHeightInPixels until the editor becomes visible again", ->
|
||||
wrapperView.hide()
|
||||
initialLineHeightInPixels = null
|
||||
wrapperNode.style.display = 'none'
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
initialLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
|
||||
component.setLineHeight(2)
|
||||
expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels
|
||||
|
||||
wrapperView.show()
|
||||
wrapperNode.style.display = ''
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
expect(editor.getLineHeightInPixels()).not.toBe initialLineHeightInPixels
|
||||
|
||||
describe "when the fontSize changes while the editor is hidden", ->
|
||||
it "does not attempt to measure the lineHeightInPixels or defaultCharWidth until the editor becomes visible again", ->
|
||||
wrapperView.hide()
|
||||
wrapperNode.style.display = 'none'
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
initialLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
initialCharWidth = editor.getDefaultCharWidth()
|
||||
|
||||
@@ -2907,17 +2944,22 @@ describe "TextEditorComponent", ->
|
||||
expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels
|
||||
expect(editor.getDefaultCharWidth()).toBe initialCharWidth
|
||||
|
||||
wrapperView.show()
|
||||
wrapperNode.style.display = ''
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
expect(editor.getLineHeightInPixels()).not.toBe initialLineHeightInPixels
|
||||
expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth
|
||||
|
||||
it "does not re-measure character widths until the editor is shown again", ->
|
||||
wrapperView.hide()
|
||||
wrapperNode.style.display = 'none'
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
component.setFontSize(22)
|
||||
editor.getBuffer().insert([0, 0], 'a') # regression test against atom/atom#3318
|
||||
|
||||
wrapperView.show()
|
||||
wrapperNode.style.display = ''
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
editor.setCursorBufferPosition([0, Infinity])
|
||||
nextAnimationFrame()
|
||||
|
||||
@@ -2927,22 +2969,29 @@ describe "TextEditorComponent", ->
|
||||
|
||||
describe "when the fontFamily changes while the editor is hidden", ->
|
||||
it "does not attempt to measure the defaultCharWidth until the editor becomes visible again", ->
|
||||
wrapperView.hide()
|
||||
wrapperNode.style.display = 'none'
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
initialLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
initialCharWidth = editor.getDefaultCharWidth()
|
||||
|
||||
component.setFontFamily('serif')
|
||||
expect(editor.getDefaultCharWidth()).toBe initialCharWidth
|
||||
|
||||
wrapperView.show()
|
||||
wrapperNode.style.display = ''
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth
|
||||
|
||||
it "does not re-measure character widths until the editor is shown again", ->
|
||||
wrapperView.hide()
|
||||
wrapperNode.style.display = 'none'
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
component.setFontFamily('serif')
|
||||
|
||||
wrapperView.show()
|
||||
wrapperNode.style.display = ''
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
editor.setCursorBufferPosition([0, Infinity])
|
||||
nextAnimationFrame()
|
||||
|
||||
@@ -2957,14 +3006,18 @@ describe "TextEditorComponent", ->
|
||||
it "does not re-measure character widths until the editor is shown again", ->
|
||||
atom.config.set('editor.fontFamily', 'sans-serif')
|
||||
|
||||
wrapperView.hide()
|
||||
wrapperNode.style.display = 'none'
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
atom.themes.applyStylesheet 'test', """
|
||||
.function.js {
|
||||
font-weight: bold;
|
||||
}
|
||||
"""
|
||||
|
||||
wrapperView.show()
|
||||
wrapperNode.style.display = ''
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
editor.setCursorBufferPosition([0, Infinity])
|
||||
nextAnimationFrame()
|
||||
|
||||
@@ -2975,11 +3028,17 @@ describe "TextEditorComponent", ->
|
||||
describe "when lines are changed while the editor is hidden", ->
|
||||
it "does not measure new characters until the editor is shown again", ->
|
||||
editor.setText('')
|
||||
wrapperView.hide()
|
||||
|
||||
wrapperNode.style.display = 'none'
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
editor.setText('var z = 1')
|
||||
editor.setCursorBufferPosition([0, Infinity])
|
||||
nextAnimationFrame()
|
||||
wrapperView.show()
|
||||
|
||||
wrapperNode.style.display = 'none'
|
||||
component.checkForVisibilityChange()
|
||||
|
||||
expect(componentNode.querySelector('.cursor').style['-webkit-transform']).toBe "translate(#{9 * charWidth}px, 0px)"
|
||||
|
||||
describe "soft wrapping", ->
|
||||
@@ -3109,26 +3168,6 @@ describe "TextEditorComponent", ->
|
||||
nextAnimationFrame()
|
||||
expect(componentNode.querySelector('.placeholder-text')).toBeNull()
|
||||
|
||||
describe "legacy editor compatibility", ->
|
||||
it "triggers the screen-lines-changed event before the editor:display-updated event", ->
|
||||
editor.setSoftWrapped(true)
|
||||
|
||||
callingOrder = []
|
||||
editor.onDidChange -> callingOrder.push 'screen-lines-changed'
|
||||
wrapperView.on 'editor:display-updated', -> callingOrder.push 'editor:display-updated'
|
||||
editor.insertText("HELLO! HELLO!\n HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! ")
|
||||
nextAnimationFrame()
|
||||
|
||||
expect(callingOrder).toEqual ['screen-lines-changed', 'editor:display-updated', 'editor:display-updated']
|
||||
|
||||
it "works with the ::setEditorHeightInLines and ::setEditorWidthInChars helpers", ->
|
||||
setEditorHeightInLines(wrapperView, 7)
|
||||
nextAnimationFrame()
|
||||
expect(componentNode.offsetHeight).toBe lineHeightInPixels * 7
|
||||
|
||||
setEditorWidthInChars(wrapperView, 10)
|
||||
expect(componentNode.querySelector('.scroll-view').offsetWidth).toBe charWidth * 10
|
||||
|
||||
describe "grammar data attributes", ->
|
||||
it "adds and updates the grammar data attribute based on the current grammar", ->
|
||||
expect(wrapperNode.dataset.grammar).toBe 'source js'
|
||||
@@ -3143,10 +3182,10 @@ describe "TextEditorComponent", ->
|
||||
|
||||
describe "detaching and reattaching the editor (regression)", ->
|
||||
it "does not throw an exception", ->
|
||||
wrapperView.detach()
|
||||
wrapperView.attachToDom()
|
||||
wrapperNode.remove()
|
||||
jasmine.attachToDOM(wrapperNode)
|
||||
|
||||
wrapperView.trigger('core:move-right')
|
||||
atom.commands.dispatch(wrapperNode, 'core:move-right')
|
||||
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 1]
|
||||
|
||||
|
||||
@@ -2035,15 +2035,10 @@ describe "TextEditorPresenter", ->
|
||||
lineNumberStateForScreenRow = (presenter, screenRow) ->
|
||||
editor = presenter.model
|
||||
tileRow = presenter.tileForRow(screenRow)
|
||||
bufferRow = editor.bufferRowForScreenRow(screenRow)
|
||||
wrapCount = screenRow - editor.screenRowForBufferRow(bufferRow)
|
||||
if wrapCount > 0
|
||||
key = bufferRow + '-' + wrapCount
|
||||
else
|
||||
key = bufferRow
|
||||
line = editor.tokenizedLineForScreenRow(screenRow)
|
||||
|
||||
gutterState = getLineNumberGutterState(presenter)
|
||||
gutterState.content.tiles[tileRow]?.lineNumbers[key]
|
||||
gutterState.content.tiles[tileRow]?.lineNumbers[line?.id]
|
||||
|
||||
tiledContentContract (presenter) -> getLineNumberGutterState(presenter).content
|
||||
|
||||
|
||||
@@ -1284,7 +1284,7 @@ describe "TextEditor", ->
|
||||
expect(selection2.isReversed()).toBeFalsy()
|
||||
|
||||
describe ".selectLinesContainingCursors()", ->
|
||||
it "selects the entire line (including newlines) at given row", ->
|
||||
it "selects to the entire line (including newlines) at given row", ->
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.selectLinesContainingCursors()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[1, 0], [2, 0]]
|
||||
@@ -1299,6 +1299,13 @@ describe "TextEditor", ->
|
||||
editor.selectLinesContainingCursors()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [2, 0]]
|
||||
|
||||
describe "when the selection spans multiple row", ->
|
||||
it "selects from the beginning of the first line to the last line", ->
|
||||
selection = editor.getLastSelection()
|
||||
selection.setBufferRange [[1, 10], [3, 20]]
|
||||
editor.selectLinesContainingCursors()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[1, 0], [4, 0]]
|
||||
|
||||
it "autoscrolls to the selection", ->
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
@@ -2892,6 +2899,32 @@ describe "TextEditor", ->
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(atom.clipboard.read()).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe ".cutToEndOfBufferLine()", ->
|
||||
beforeEach ->
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
|
||||
describe "when nothing is selected", ->
|
||||
it "cuts up to the end of the buffer line", ->
|
||||
editor.setCursorBufferPosition([2, 20])
|
||||
editor.addCursorAtBufferPosition([3, 20])
|
||||
|
||||
editor.cutToEndOfBufferLine()
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(atom.clipboard.read()).toBe ' <= 1) return items;\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "only cuts the selected text, not to the end of the buffer line", ->
|
||||
editor.setSelectedBufferRanges([[[2, 20], [2, 30]], [[3, 20], [3, 20]]])
|
||||
|
||||
editor.cutToEndOfBufferLine()
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.lengthurn items;'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(atom.clipboard.read()).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe ".copySelectedText()", ->
|
||||
it "copies selected text onto the clipboard", ->
|
||||
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]], [[2, 8], [2, 13]]])
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
path = require 'path'
|
||||
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
|
||||
@@ -26,19 +24,20 @@ describe "ThemeManager", ->
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it 'getLoadedThemes get all the loaded themes', ->
|
||||
themes = themeManager.getLoadedThemes()
|
||||
expect(themes.length).toBeGreaterThan(2)
|
||||
describe 'getLoadedThemes', ->
|
||||
it 'gets all the loaded themes', ->
|
||||
themes = themeManager.getLoadedThemes()
|
||||
expect(themes.length).toBeGreaterThan(2)
|
||||
|
||||
it 'getActiveThemes get all the active themes', ->
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
describe "getActiveThemes", ->
|
||||
it 'gets all the active themes', ->
|
||||
waitsForPromise -> themeManager.activateThemes()
|
||||
|
||||
runs ->
|
||||
names = atom.config.get('core.themes')
|
||||
expect(names.length).toBeGreaterThan(0)
|
||||
themes = themeManager.getActiveThemes()
|
||||
expect(themes).toHaveLength(names.length)
|
||||
runs ->
|
||||
names = atom.config.get('core.themes')
|
||||
expect(names.length).toBeGreaterThan(0)
|
||||
themes = themeManager.getActiveThemes()
|
||||
expect(themes).toHaveLength(names.length)
|
||||
|
||||
describe "when the core.themes config value contains invalid entry", ->
|
||||
it "ignores theme", ->
|
||||
@@ -88,7 +87,7 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
didChangeActiveThemesHandler.reset()
|
||||
expect($('style.theme')).toHaveLength 0
|
||||
expect(document.querySelectorAll('style.theme')).toHaveLength 0
|
||||
atom.config.set('core.themes', ['atom-dark-ui'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -96,8 +95,8 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
didChangeActiveThemesHandler.reset()
|
||||
expect($('style[priority=1]')).toHaveLength 2
|
||||
expect($('style[priority=1]:eq(0)').attr('source-path')).toMatch /atom-dark-ui/
|
||||
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength 2
|
||||
expect(document.querySelector('style[priority="1"]').getAttribute('source-path')).toMatch /atom-dark-ui/
|
||||
atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-ui'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -105,9 +104,9 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
didChangeActiveThemesHandler.reset()
|
||||
expect($('style[priority=1]')).toHaveLength 2
|
||||
expect($('style[priority=1]:eq(0)').attr('source-path')).toMatch /atom-dark-ui/
|
||||
expect($('style[priority=1]:eq(1)').attr('source-path')).toMatch /atom-light-ui/
|
||||
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength 2
|
||||
expect(document.querySelectorAll('style[priority="1"]')[0].getAttribute('source-path')).toMatch /atom-dark-ui/
|
||||
expect(document.querySelectorAll('style[priority="1"]')[1].getAttribute('source-path')).toMatch /atom-light-ui/
|
||||
atom.config.set('core.themes', [])
|
||||
|
||||
waitsFor ->
|
||||
@@ -115,7 +114,7 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
didChangeActiveThemesHandler.reset()
|
||||
expect($('style[priority=1]')).toHaveLength 2
|
||||
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength 2
|
||||
# atom-dark-ui has an directory path, the syntax one doesn't
|
||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
|
||||
|
||||
@@ -123,7 +122,7 @@ describe "ThemeManager", ->
|
||||
didChangeActiveThemesHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
expect($('style[priority=1]')).toHaveLength 2
|
||||
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength 2
|
||||
importPaths = themeManager.getImportPaths()
|
||||
expect(importPaths.length).toBe 1
|
||||
expect(importPaths[0]).toContain 'atom-dark-ui'
|
||||
@@ -172,37 +171,38 @@ describe "ThemeManager", ->
|
||||
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
|
||||
cssPath = atom.project.getDirectories()[0]?.resolve('css.css')
|
||||
lengthBefore = $('head style').length
|
||||
lengthBefore = document.querySelectorAll('head style').length
|
||||
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
|
||||
expect(styleElementAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
element = $('head style[source-path*="css.css"]')
|
||||
expect(element.attr('source-path')).toBe themeManager.stringToId(cssPath)
|
||||
expect(element.text()).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
expect(element[0].sheet).toBe stylesheetAddedHandler.argsForCall[0][0]
|
||||
element = document.querySelector('head style[source-path*="css.css"]')
|
||||
expect(element.getAttribute('source-path')).toBe themeManager.stringToId(cssPath)
|
||||
expect(element.textContent).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
expect(element.sheet).toBe stylesheetAddedHandler.argsForCall[0][0]
|
||||
|
||||
# doesn't append twice
|
||||
styleElementAddedHandler.reset()
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
expect(styleElementAddedHandler).not.toHaveBeenCalled()
|
||||
|
||||
$('head style[id*="css.css"]').remove()
|
||||
for styleElement in document.querySelectorAll('head style[id*="css.css"]')
|
||||
styleElement.remove()
|
||||
|
||||
it "synchronously loads and parses less files at the given path and installs a style tag for it in the head", ->
|
||||
lessPath = atom.project.getDirectories()[0]?.resolve('sample.less')
|
||||
lengthBefore = $('head style').length
|
||||
lengthBefore = document.querySelectorAll('head style').length
|
||||
themeManager.requireStylesheet(lessPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
|
||||
element = $('head style[source-path*="sample.less"]')
|
||||
expect(element.attr('source-path')).toBe themeManager.stringToId(lessPath)
|
||||
expect(element.text()).toBe """
|
||||
element = document.querySelector('head style[source-path*="sample.less"]')
|
||||
expect(element.getAttribute('source-path')).toBe themeManager.stringToId(lessPath)
|
||||
expect(element.textContent).toBe """
|
||||
#header {
|
||||
color: #4d926f;
|
||||
}
|
||||
@@ -214,24 +214,25 @@ describe "ThemeManager", ->
|
||||
|
||||
# doesn't append twice
|
||||
themeManager.requireStylesheet(lessPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
$('head style[id*="sample.less"]').remove()
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
for styleElement in document.querySelectorAll('head style[id*="sample.less"]')
|
||||
styleElement.remove()
|
||||
|
||||
it "supports requiring css and less stylesheets without an explicit extension", ->
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'css')
|
||||
expect($('head style[source-path*="css.css"]').attr('source-path')).toBe themeManager.stringToId(atom.project.getDirectories()[0]?.resolve('css.css'))
|
||||
expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toBe themeManager.stringToId(atom.project.getDirectories()[0]?.resolve('css.css'))
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'sample')
|
||||
expect($('head style[source-path*="sample.less"]').attr('source-path')).toBe themeManager.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less'))
|
||||
expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toBe themeManager.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less'))
|
||||
|
||||
$('head style[id*="css.css"]').remove()
|
||||
$('head style[id*="sample.less"]').remove()
|
||||
document.querySelector('head style[source-path*="css.css"]').remove()
|
||||
document.querySelector('head style[source-path*="sample.less"]').remove()
|
||||
|
||||
it "returns a disposable allowing styles applied by the given path to be removed", ->
|
||||
cssPath = require.resolve('./fixtures/css.css')
|
||||
|
||||
expect($(document.body).css('font-weight')).not.toBe("bold")
|
||||
expect(getComputedStyle(document.body).fontWeight).not.toBe("bold")
|
||||
disposable = themeManager.requireStylesheet(cssPath)
|
||||
expect($(document.body).css('font-weight')).toBe("bold")
|
||||
expect(getComputedStyle(document.body).fontWeight).toBe("bold")
|
||||
|
||||
atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler")
|
||||
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
@@ -239,7 +240,7 @@ describe "ThemeManager", ->
|
||||
|
||||
disposable.dispose()
|
||||
|
||||
expect($(document.body).css('font-weight')).not.toBe("bold")
|
||||
expect(getComputedStyle(document.body).fontWeight).not.toBe("bold")
|
||||
|
||||
expect(styleElementRemovedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
@@ -271,9 +272,9 @@ describe "ThemeManager", ->
|
||||
expect(getComputedStyle(workspaceElement)["background-color"]).toBe "rgb(0, 0, 255)"
|
||||
|
||||
# from within the theme itself
|
||||
expect($("atom-text-editor").css("padding-top")).toBe "150px"
|
||||
expect($("atom-text-editor").css("padding-right")).toBe "150px"
|
||||
expect($("atom-text-editor").css("padding-bottom")).toBe "150px"
|
||||
expect(getComputedStyle(document.querySelector("atom-text-editor")).paddingTop).toBe "150px"
|
||||
expect(getComputedStyle(document.querySelector("atom-text-editor")).paddingRight).toBe "150px"
|
||||
expect(getComputedStyle(document.querySelector("atom-text-editor")).paddingBottom).toBe "150px"
|
||||
|
||||
describe "when there is a theme with incomplete variables", ->
|
||||
it "loads the correct values from the fallback ui-variables", ->
|
||||
@@ -288,7 +289,7 @@ describe "ThemeManager", ->
|
||||
expect(getComputedStyle(workspaceElement)["background-color"]).toBe "rgb(0, 0, 255)"
|
||||
|
||||
# from within the theme itself
|
||||
expect($("atom-text-editor").css("background-color")).toBe "rgb(0, 152, 255)"
|
||||
expect(getComputedStyle(document.querySelector("atom-text-editor")).backgroundColor).toBe "rgb(0, 152, 255)"
|
||||
|
||||
describe "user stylesheet", ->
|
||||
userStylesheetPath = null
|
||||
@@ -320,14 +321,14 @@ describe "ThemeManager", ->
|
||||
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
|
||||
|
||||
expect($(document.body).css('border-style')).toBe 'dotted'
|
||||
expect(getComputedStyle(document.body).borderStyle).toBe 'dotted'
|
||||
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
|
||||
|
||||
waitsFor ->
|
||||
themeManager.loadUserStylesheet.callCount is 1
|
||||
|
||||
runs ->
|
||||
expect($(document.body).css('border-style')).toBe 'dashed'
|
||||
expect(getComputedStyle(document.body).borderStyle).toBe 'dashed'
|
||||
|
||||
expect(styleElementRemovedHandler).toHaveBeenCalled()
|
||||
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dotted'
|
||||
@@ -354,7 +355,7 @@ describe "ThemeManager", ->
|
||||
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dashed'
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
|
||||
expect($(document.body).css('border-style')).toBe 'none'
|
||||
expect(getComputedStyle(document.body).borderStyle).toBe 'none'
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when there is an error reading the stylesheet", ->
|
||||
|
||||