mirror of
https://github.com/atom/atom.git
synced 2026-02-03 03:05:05 -05:00
Merge branch 'master' into colllin-patch-1
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,6 +1,18 @@
|
||||
See https://atom.io/releases
|
||||
|
||||
## 1.4.0
|
||||
|
||||
* Switching encoding is now fast also with large files.
|
||||
* Fixed an issue where disabling and re-enabling a package caused custom keymaps to be overridden.
|
||||
* Fixed restoring untitled editors on restart. The new behavior never prompts to save new/changed files when closing a window or quitting Atom.
|
||||
|
||||
## 1.3.0
|
||||
|
||||
* The tree-view now sorts directory entries more naturally, in a locale-sensitive way.
|
||||
* Lines can now be moved up and down with multiple cursors.
|
||||
* Improved the performance of marker-dependent code paths such as spell-check and find and replace.
|
||||
* Fixed copying and pasting in native input fields.
|
||||
* By default, windows with no pane items are now closed via the `core:close` command. The previous behavior can be restored via the `Close Empty Windows` option in settings.
|
||||
* Fixed an issue where characters were inserted when toggling the settings view on some keyboard layouts.
|
||||
* Modules can now temporarily override `Error.prepareStackTrace`. There is also an `Error.prototype.getRawStack()` method if you just need access to the raw v8 trace structure.
|
||||
* Fixed a problem that caused blurry fonts on monitors that have a slightly higher resolution than 96 DPI.
|
||||
|
||||
24
CODE_OF_CONDUCT.md
Normal file
24
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery
|
||||
- Personal attacks
|
||||
- Trolling or insulting/derogatory comments
|
||||
- Public or private harassment
|
||||
- Publishing other's private information, such as physical or electronic addresses, without explicit permission
|
||||
- Other unethical or unprofessional conduct
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant, version 1.3.0, available from http://contributor-covenant.org/version/1/3/0/
|
||||
@@ -13,6 +13,7 @@ These are just guidelines, not rules, use your best judgment and feel free to pr
|
||||
|
||||
[How Can I Contribute?](#how-can-i-contribute)
|
||||
* [Reporting Bugs](#reporting-bugs)
|
||||
* [Suggesting Enhancements](#suggesting-enhancements)
|
||||
* [Your First Code Contribution](#your-first-code-contribution)
|
||||
* [Pull Requests](#pull-requests)
|
||||
|
||||
@@ -29,7 +30,7 @@ These are just guidelines, not rules, use your best judgment and feel free to pr
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0).
|
||||
This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md).
|
||||
By participating, you are expected to uphold this code.
|
||||
Please report unacceptable behavior to [atom@github.com](mailto:atom@github.com).
|
||||
|
||||
@@ -157,6 +158,60 @@ Include details about your configuration and environment:
|
||||
* Problem can be reliably reproduced, doesn't happen randomly: [Yes/No]
|
||||
* Problem happens with all files and projects, not only some files or projects: [Yes/No]
|
||||
|
||||
### Suggesting Enhancements
|
||||
|
||||
This section guides you through submitting an enhancement suggestion for Atom, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion :pencil: and find related suggestions :mag_right:.
|
||||
|
||||
Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). If you'd like, you can use [this template](#template-for-submitting-enhancement-suggestions) to structure the information.
|
||||
|
||||
#### Before Submitting An Enhancement Suggestion
|
||||
|
||||
* **Check the [debugging guide](https://atom.io/docs/latest/hacking-atom-debugging)** for tips — you might discover that the enhancement is already available. Most importantly, check if you're using [the latest version of Atom](https://atom.io/docs/latest/hacking-atom-debugging#update-to-the-latest-version) and if you can get the desired behavior by changing [Atom's or packages' config settings](https://atom.io/docs/latest/hacking-atom-debugging#check-atom-and-package-settings).
|
||||
* **Check if there's already [a package](https://atom.io/packages) which provides that enhancement.**
|
||||
* **Determine [which repository the enhancement should be suggested in](#atom-and-packages).**
|
||||
* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+user%3Aatom)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
|
||||
|
||||
#### How Do I Submit A (Good) Enhancement Suggestion?
|
||||
|
||||
Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined [which repository](#atom-and-packages) your enhancement suggestions is related to, create an issue on that repository and provide the following information:
|
||||
|
||||
* **Use a clear and descriptive title** for the issue to identify the suggestion.
|
||||
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
|
||||
* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
|
||||
* **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
|
||||
* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of Atom which the suggestion is related to. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on OSX and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
|
||||
* **Explain why this enhancement would be useful** to most Atom users and isn't something that can or should be implemented as a [community package](#atom-and-packages).
|
||||
* **List some other text editors or applications where this enhancement exists.**
|
||||
* **Specify which version of Atom you're using.** You can get the exact version by running `atom -v` in your terminal, or by starting Atom and running the `Application: About` command from the [Command Palette](https://github.com/atom/command-palette).
|
||||
* **Specify the name and version of the OS you're using.**
|
||||
|
||||
#### Template For Submitting Enhancement Suggestions
|
||||
|
||||
[Short description of suggestion]
|
||||
|
||||
**Steps which explain the enhancement**
|
||||
|
||||
1. [First Step]
|
||||
2. [Second Step]
|
||||
3. [Other Steps...]
|
||||
|
||||
**Current and suggested behavior**
|
||||
|
||||
[Describe current and suggested behavior here]
|
||||
|
||||
**Why would the enhancement be useful to most users**
|
||||
|
||||
[Explain why the enhancement would be useful to most users]
|
||||
|
||||
[List some other text editors or applications where this enhancement exists]
|
||||
|
||||
**Screenshots and GIFs**
|
||||
|
||||

|
||||
|
||||
**Atom Version:** [Enter Atom version here]
|
||||
**OS and Version:** [Enter OS name and version here]
|
||||
|
||||
### Your First Code Contribution
|
||||
|
||||
Unsure where to begin contributing to Atom? You can start by looking through these `beginner` and `help-wanted` issues:
|
||||
@@ -200,6 +255,7 @@ Both issue lists are sorted by total number of comments. While not perfect, numb
|
||||
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
|
||||
* Limit the first line to 72 characters or less
|
||||
* Reference issues and pull requests liberally
|
||||
* When only changing documentation, include `[ci skip]` in the commit description
|
||||
* Consider starting the commit message with an applicable emoji:
|
||||
* :art: `:art:` when improving the format/structure of the code
|
||||
* :racehorse: `:racehorse:` when improving performance
|
||||
|
||||
@@ -11,7 +11,7 @@ Visit [atom.io](https://atom.io) to learn more or visit the [Atom forum](https:/
|
||||
Follow [@AtomEditor](https://twitter.com/atomeditor) on Twitter for important
|
||||
announcements.
|
||||
|
||||
This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0).
|
||||
This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior to atom@github.com.
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "1.4.1"
|
||||
"atom-package-manager": "1.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
"dependencies": {
|
||||
"asar": "^0.8.0",
|
||||
"async": "~0.2.9",
|
||||
"aws-sdk": "^2.2.18",
|
||||
"donna": "^1.0.13",
|
||||
"formidable": "~1.0.14",
|
||||
"fs-plus": "2.x",
|
||||
"github-releases": "~0.3.0",
|
||||
"github-releases": "~0.3.1",
|
||||
"glob": "^5.0.14",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-babel": "^5.0.1",
|
||||
|
||||
@@ -20,9 +20,6 @@ module.exports = (grunt) ->
|
||||
copyFolder = path.resolve 'script', 'copy-folder.cmd'
|
||||
if runas('cmd', ['/c', copyFolder, shellAppDir, installDir], admin: true) isnt 0
|
||||
grunt.log.error("Failed to copy #{shellAppDir} to #{installDir}")
|
||||
|
||||
createShortcut = path.resolve 'script', 'create-shortcut.cmd'
|
||||
runas('cmd', ['/c', createShortcut, path.join(installDir, 'atom.exe'), appName])
|
||||
else if process.platform is 'darwin'
|
||||
rm installDir
|
||||
mkdir path.dirname(installDir)
|
||||
|
||||
@@ -6,6 +6,7 @@ async = require 'async'
|
||||
fs = require 'fs-plus'
|
||||
GitHub = require 'github-releases'
|
||||
request = require 'request'
|
||||
AWS = require 'aws-sdk'
|
||||
|
||||
grunt = null
|
||||
|
||||
@@ -210,7 +211,7 @@ deleteExistingAssets = (release, assetNames, callback) ->
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
uploadAssets = (release, buildDir, assets, callback) ->
|
||||
upload = (release, assetName, assetPath, callback) ->
|
||||
uploadToReleases = (release, assetName, assetPath, callback) ->
|
||||
options =
|
||||
uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}")
|
||||
method: 'POST'
|
||||
@@ -221,15 +222,43 @@ uploadAssets = (release, buildDir, assets, callback) ->
|
||||
|
||||
assetRequest = request options, (error, response, body='') ->
|
||||
if error? or response.statusCode >= 400
|
||||
logError("Upload release asset #{assetName} failed", error, body)
|
||||
logError("Upload release asset #{assetName} to Releases failed", error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(null, release)
|
||||
|
||||
fs.createReadStream(assetPath).pipe(assetRequest)
|
||||
|
||||
uploadToS3 = (release, assetName, assetPath, callback) ->
|
||||
s3Key = process.env.BUILD_ATOM_RELEASES_S3_KEY
|
||||
s3Secret = process.env.BUILD_ATOM_RELEASES_S3_SECRET
|
||||
s3Bucket = process.env.BUILD_ATOM_RELEASES_S3_BUCKET
|
||||
|
||||
unless s3Key and s3Secret and s3Bucket
|
||||
callback(new Error('BUILD_ATOM_RELEASES_S3_KEY, BUILD_ATOM_RELEASES_S3_SECRET, and BUILD_ATOM_RELEASES_S3_BUCKET environment variables must be set.'))
|
||||
return
|
||||
|
||||
s3Info =
|
||||
accessKeyId: s3Key
|
||||
secretAccessKey: s3Secret
|
||||
s3 = new AWS.S3 s3Info
|
||||
|
||||
key = "releases/#{release.tag_name}/#{assetName}"
|
||||
uploadParams =
|
||||
Bucket: s3Bucket
|
||||
ACL: 'public-read'
|
||||
Key: key
|
||||
Body: fs.createReadStream(assetPath)
|
||||
s3.upload uploadParams, (error, data) ->
|
||||
if error?
|
||||
logError("Upload release asset #{assetName} to S3 failed", error)
|
||||
callback(error)
|
||||
else
|
||||
callback(null, release)
|
||||
|
||||
tasks = []
|
||||
for {assetName} in assets
|
||||
assetPath = path.join(buildDir, assetName)
|
||||
tasks.push(upload.bind(this, release, assetName, assetPath))
|
||||
tasks.push(uploadToReleases.bind(this, release, assetName, assetPath))
|
||||
tasks.push(uploadToS3.bind(this, release, assetName, assetPath))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
@@ -29,6 +29,7 @@ module.exports = (grunt) ->
|
||||
return
|
||||
|
||||
appDir = grunt.config.get('atom.appDir')
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
|
||||
# Replace version field of package.json.
|
||||
packageJsonPath = path.join(appDir, 'package.json')
|
||||
@@ -39,7 +40,7 @@ module.exports = (grunt) ->
|
||||
|
||||
if process.platform is 'darwin'
|
||||
cmd = 'script/set-version'
|
||||
args = [grunt.config.get('atom.buildDir'), version]
|
||||
args = [shellAppDir, version]
|
||||
spawn {cmd, args}, (error, result, code) -> done(error)
|
||||
else if process.platform is 'win32'
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
|
||||
70
package.json
70
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "1.4.0-dev",
|
||||
"version": "1.5.0-dev",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -12,10 +12,10 @@
|
||||
"url": "https://github.com/atom/atom/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"electronVersion": "0.34.3",
|
||||
"electronVersion": "0.34.5",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^6.1.1",
|
||||
"atom-keymap": "^6.2.0",
|
||||
"babel-core": "^5.8.21",
|
||||
"bootstrap": "^3.3.4",
|
||||
"cached-run-in-this-context": "0.4.0",
|
||||
@@ -52,7 +52,7 @@
|
||||
"service-hub": "^0.7.0",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "^8.0.4",
|
||||
"text-buffer": "8.1.1",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"yargs": "^3.23.0"
|
||||
@@ -64,10 +64,10 @@
|
||||
"atom-light-ui": "0.43.0",
|
||||
"base16-tomorrow-dark-theme": "1.0.0",
|
||||
"base16-tomorrow-light-theme": "1.0.0",
|
||||
"one-dark-ui": "1.1.5",
|
||||
"one-dark-ui": "1.1.9",
|
||||
"one-dark-syntax": "1.1.1",
|
||||
"one-light-syntax": "1.1.1",
|
||||
"one-light-ui": "1.1.5",
|
||||
"one-light-ui": "1.1.9",
|
||||
"solarized-dark-syntax": "0.39.0",
|
||||
"solarized-light-syntax": "0.23.0",
|
||||
"about": "1.1.0",
|
||||
@@ -75,8 +75,8 @@
|
||||
"autocomplete-atom-api": "0.9.2",
|
||||
"autocomplete-css": "0.11.0",
|
||||
"autocomplete-html": "0.7.2",
|
||||
"autocomplete-plus": "2.23.0",
|
||||
"autocomplete-snippets": "1.8.0",
|
||||
"autocomplete-plus": "2.24.0",
|
||||
"autocomplete-snippets": "1.9.0",
|
||||
"autoflow": "0.26.0",
|
||||
"autosave": "0.23.0",
|
||||
"background-tips": "0.26.0",
|
||||
@@ -87,7 +87,7 @@
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.21.0",
|
||||
"exception-reporting": "0.37.0",
|
||||
"find-and-replace": "0.191.0",
|
||||
"find-and-replace": "0.194.0",
|
||||
"fuzzy-finder": "0.93.0",
|
||||
"git-diff": "0.57.0",
|
||||
"go-to-line": "0.30.0",
|
||||
@@ -97,13 +97,13 @@
|
||||
"keybinding-resolver": "0.33.0",
|
||||
"line-ending-selector": "0.3.0",
|
||||
"link": "0.31.0",
|
||||
"markdown-preview": "0.156.2",
|
||||
"metrics": "0.53.0",
|
||||
"markdown-preview": "0.157.0",
|
||||
"metrics": "0.53.1",
|
||||
"notifications": "0.62.1",
|
||||
"open-on-github": "0.40.0",
|
||||
"package-generator": "0.41.0",
|
||||
"release-notes": "0.53.0",
|
||||
"settings-view": "0.232.0",
|
||||
"settings-view": "0.232.1",
|
||||
"snippets": "1.0.1",
|
||||
"spell-check": "0.63.0",
|
||||
"status-bar": "0.80.0",
|
||||
@@ -116,38 +116,38 @@
|
||||
"welcome": "0.33.0",
|
||||
"whitespace": "0.32.1",
|
||||
"wrap-guide": "0.38.1",
|
||||
"language-c": "0.49.0",
|
||||
"language-clojure": "0.18.0",
|
||||
"language-coffee-script": "0.45.0",
|
||||
"language-c": "0.51.1",
|
||||
"language-clojure": "0.19.0",
|
||||
"language-coffee-script": "0.46.0",
|
||||
"language-csharp": "0.11.0",
|
||||
"language-css": "0.35.0",
|
||||
"language-gfm": "0.81.0",
|
||||
"language-git": "0.10.0",
|
||||
"language-go": "0.40.0",
|
||||
"language-html": "0.42.0",
|
||||
"language-hyperlink": "0.15.0",
|
||||
"language-java": "0.16.1",
|
||||
"language-javascript": "0.102.0",
|
||||
"language-json": "0.17.1",
|
||||
"language-less": "0.28.3",
|
||||
"language-make": "0.20.0",
|
||||
"language-css": "0.36.0",
|
||||
"language-gfm": "0.82.0",
|
||||
"language-git": "0.11.0",
|
||||
"language-go": "0.41.0",
|
||||
"language-html": "0.43.1",
|
||||
"language-hyperlink": "0.16.0",
|
||||
"language-java": "0.17.0",
|
||||
"language-javascript": "0.104.0",
|
||||
"language-json": "0.17.2",
|
||||
"language-less": "0.29.0",
|
||||
"language-make": "0.21.0",
|
||||
"language-mustache": "0.13.0",
|
||||
"language-objective-c": "0.15.0",
|
||||
"language-perl": "0.31.0",
|
||||
"language-objective-c": "0.15.1",
|
||||
"language-perl": "0.32.0",
|
||||
"language-php": "0.34.0",
|
||||
"language-property-list": "0.8.0",
|
||||
"language-python": "0.42.1",
|
||||
"language-ruby": "0.64.0",
|
||||
"language-ruby": "0.65.0",
|
||||
"language-ruby-on-rails": "0.24.0",
|
||||
"language-sass": "0.44.0",
|
||||
"language-shellscript": "0.20.0",
|
||||
"language-sass": "0.45.0",
|
||||
"language-shellscript": "0.21.0",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.19.0",
|
||||
"language-sql": "0.20.0",
|
||||
"language-text": "0.7.0",
|
||||
"language-todo": "0.27.0",
|
||||
"language-toml": "0.16.0",
|
||||
"language-xml": "0.34.1",
|
||||
"language-yaml": "0.24.0"
|
||||
"language-toml": "0.18.0",
|
||||
"language-xml": "0.34.2",
|
||||
"language-yaml": "0.25.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -40,6 +40,10 @@ function setEnvironmentVariables() {
|
||||
process.env.CC = 'clang';
|
||||
process.env.CXX = 'clang++';
|
||||
process.env.npm_config_clang = '1';
|
||||
} else if (process.platform === 'win32') {
|
||||
process.env.BUILD_ATOM_RELEASES_S3_KEY = process.env.BUILD_ATOM_WIN_RELEASES_S3_KEY
|
||||
process.env.BUILD_ATOM_RELEASES_S3_SECRET = process.env.BUILD_ATOM_WIN_RELEASES_S3_SECRET
|
||||
process.env.BUILD_ATOM_RELEASES_S3_BUCKET = process.env.BUILD_ATOM_WIN_RELEASES_S3_BUCKET
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
set -e
|
||||
|
||||
export ATOM_ACCESS_TOKEN=$BUILD_ATOM_LINUX_ACCESS_TOKEN
|
||||
export BUILD_ATOM_RELEASES_S3_KEY=$BUILD_ATOM_LINUX_RELEASES_S3_KEY
|
||||
export BUILD_ATOM_RELEASES_S3_SECRET=$BUILD_ATOM_LINUX_RELEASES_S3_SECRET
|
||||
export BUILD_ATOM_RELEASES_S3_BUCKET=$BUILD_ATOM_LINUX_RELEASES_S3_BUCKET
|
||||
|
||||
if [ -d /usr/local/share/nodenv ]; then
|
||||
export NODENV_ROOT=/usr/local/share/nodenv
|
||||
|
||||
@@ -8,5 +8,8 @@ docker run \
|
||||
--env JANKY_SHA1="$JANKY_SHA1" \
|
||||
--env JANKY_BRANCH="$JANKY_BRANCH" \
|
||||
--env ATOM_ACCESS_TOKEN="$BUILD_ATOM_RPM_ACCESS_TOKEN" \
|
||||
--env BUILD_ATOM_RELEASES_S3_KEY="$BUILD_ATOM_RPM_RELEASES_S3_KEY" \
|
||||
--env BUILD_ATOM_RELEASES_S3_SECRET="$BUILD_ATOM_RPM_RELEASES_S3_SECRET" \
|
||||
--env BUILD_ATOM_RELEASES_S3_BUCKET="$BUILD_ATOM_RPM_RELEASES_S3_BUCKET" \
|
||||
atom-rpm /atom/script/rpmbuild
|
||||
docker rmi atom-rpm
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
@echo off
|
||||
|
||||
set USAGE=Usage: %0 source name-on-desktop
|
||||
|
||||
if [%1] == [] (
|
||||
echo %USAGE%
|
||||
exit 1
|
||||
)
|
||||
if [%2] == [] (
|
||||
echo %USAGE%
|
||||
exit 2
|
||||
)
|
||||
|
||||
set SCRIPT="%TEMP%\%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%.vbs"
|
||||
|
||||
echo Set oWS = WScript.CreateObject("WScript.Shell") >> %SCRIPT%
|
||||
echo sLinkFile = "%USERPROFILE%\Desktop\%2.lnk" >> %SCRIPT%
|
||||
echo Set oLink = oWS.CreateShortcut(sLinkFile) >> %SCRIPT%
|
||||
echo oLink.TargetPath = %1 >> %SCRIPT%
|
||||
echo oLink.Save >> %SCRIPT%
|
||||
|
||||
cscript /nologo %SCRIPT%
|
||||
del %SCRIPT%
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
set -e
|
||||
|
||||
BUILT_PRODUCTS_DIR=$1
|
||||
SHELL_APP_DIR=$1
|
||||
VERSION=$2
|
||||
PLIST_PATH="$BUILT_PRODUCTS_DIR/Atom.app/Contents/Info.plist"
|
||||
HELPER_PLIST_PATH="$BUILT_PRODUCTS_DIR/Atom.app/Contents/Frameworks/Atom Helper.app/Contents/Info.plist"
|
||||
|
||||
PLIST_PATH="$SHELL_APP_DIR/Contents/Info.plist"
|
||||
HELPER_PLIST_PATH="$SHELL_APP_DIR/Contents/Frameworks/Atom Helper.app/Contents/Info.plist"
|
||||
|
||||
# Update version
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $VERSION" "$PLIST_PATH"
|
||||
|
||||
@@ -45,9 +45,11 @@ describe "AtomEnvironment", ->
|
||||
expect(atom.config.get('editor.showInvisibles')).toBe false
|
||||
|
||||
describe "window onerror handler", ->
|
||||
devToolsPromise = null
|
||||
beforeEach ->
|
||||
spyOn atom, 'openDevTools'
|
||||
spyOn atom, 'executeJavaScriptInDevTools'
|
||||
devToolsPromise = Promise.resolve()
|
||||
spyOn(atom, 'openDevTools').andReturn(devToolsPromise)
|
||||
spyOn(atom, 'executeJavaScriptInDevTools')
|
||||
|
||||
it "will open the dev tools when an error is triggered", ->
|
||||
try
|
||||
@@ -55,8 +57,10 @@ describe "AtomEnvironment", ->
|
||||
catch e
|
||||
window.onerror.call(window, e.toString(), 'abc', 2, 3, e)
|
||||
|
||||
expect(atom.openDevTools).toHaveBeenCalled()
|
||||
expect(atom.executeJavaScriptInDevTools).toHaveBeenCalled()
|
||||
waitsForPromise -> devToolsPromise
|
||||
runs ->
|
||||
expect(atom.openDevTools).toHaveBeenCalled()
|
||||
expect(atom.executeJavaScriptInDevTools).toHaveBeenCalled()
|
||||
|
||||
describe "::onWillThrowError", ->
|
||||
willThrowSpy = null
|
||||
|
||||
@@ -69,3 +69,39 @@ describe 'CompileCache', ->
|
||||
|
||||
CompileCache.addPathToCache(path.join(fixtures, 'cson.cson'), atomHome)
|
||||
expect(CSONParser.parse.callCount).toBe 1
|
||||
|
||||
describe 'overriding Error.prepareStackTrace', ->
|
||||
it 'removes the override on the next tick, and always assigns the raw stack', ->
|
||||
Error.prepareStackTrace = -> 'a-stack-trace'
|
||||
|
||||
error = new Error("Oops")
|
||||
expect(error.stack).toBe 'a-stack-trace'
|
||||
expect(Array.isArray(error.getRawStack())).toBe true
|
||||
|
||||
waits(1)
|
||||
runs ->
|
||||
error = new Error("Oops again")
|
||||
expect(error.stack).toContain('compile-cache-spec.coffee')
|
||||
expect(Array.isArray(error.getRawStack())).toBe true
|
||||
|
||||
it 'does not infinitely loop when the original prepareStackTrace value is reassigned', ->
|
||||
originalPrepareStackTrace = Error.prepareStackTrace
|
||||
|
||||
Error.prepareStackTrace = -> 'a-stack-trace'
|
||||
Error.prepareStackTrace = originalPrepareStackTrace
|
||||
|
||||
error = new Error('Oops')
|
||||
expect(error.stack).toContain('compile-cache-spec.coffee')
|
||||
expect(Array.isArray(error.getRawStack())).toBe true
|
||||
|
||||
it 'does not infinitely loop when the assigned prepareStackTrace calls the original prepareStackTrace', ->
|
||||
originalPrepareStackTrace = Error.prepareStackTrace
|
||||
|
||||
Error.prepareStackTrace = (error, stack) ->
|
||||
error.foo = 'bar'
|
||||
originalPrepareStackTrace(error, stack)
|
||||
|
||||
error = new Error('Oops')
|
||||
expect(error.stack).toContain('compile-cache-spec.coffee')
|
||||
expect(error.foo).toBe('bar')
|
||||
expect(Array.isArray(error.getRawStack())).toBe true
|
||||
|
||||
@@ -679,6 +679,26 @@ describe "Config", ->
|
||||
writtenConfig = CSON.writeFileSync.argsForCall[0][1]
|
||||
expect(writtenConfig).toEqual '*': atom.config.settings
|
||||
|
||||
it 'writes properties in alphabetical order', ->
|
||||
atom.config.set('foo', 1)
|
||||
atom.config.set('bar', 2)
|
||||
atom.config.set('baz.foo', 3)
|
||||
atom.config.set('baz.bar', 4)
|
||||
|
||||
CSON.writeFileSync.reset()
|
||||
atom.config.save()
|
||||
|
||||
expect(CSON.writeFileSync.argsForCall[0][0]).toBe atom.config.configFilePath
|
||||
writtenConfig = CSON.writeFileSync.argsForCall[0][1]
|
||||
expect(writtenConfig).toEqual '*': atom.config.settings
|
||||
|
||||
expectedKeys = ['bar', 'baz', 'foo']
|
||||
foundKeys = (key for key of writtenConfig['*'] when key in expectedKeys)
|
||||
expect(foundKeys).toEqual expectedKeys
|
||||
expectedKeys = ['bar', 'foo']
|
||||
foundKeys = (key for key of writtenConfig['*']['baz'] when key in expectedKeys)
|
||||
expect(foundKeys).toEqual expectedKeys
|
||||
|
||||
describe "when ~/.atom/config.json doesn't exist", ->
|
||||
it "writes any non-default properties to ~/.atom/config.cson", ->
|
||||
atom.config.set("a.b.c", 1)
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
|
||||
module.exports =
|
||||
class FakeLinesYardstick
|
||||
constructor: (@model, @presenter) ->
|
||||
constructor: (@model) ->
|
||||
@characterWidthsByScope = {}
|
||||
|
||||
prepareScreenRowsForMeasurement: ->
|
||||
@presenter.getPreMeasurementState()
|
||||
|
||||
getScopedCharacterWidth: (scopeNames, char) ->
|
||||
@getScopedCharacterWidths(scopeNames)[char]
|
||||
|
||||
@@ -56,18 +53,3 @@ class FakeLinesYardstick
|
||||
column += charLength
|
||||
|
||||
{top, left}
|
||||
|
||||
pixelRectForScreenRange: (screenRange) ->
|
||||
lineHeight = @model.getLineHeightInPixels()
|
||||
|
||||
if screenRange.end.row > screenRange.start.row
|
||||
top = @pixelPositionForScreenPosition(screenRange.start).top
|
||||
left = 0
|
||||
height = (screenRange.end.row - screenRange.start.row + 1) * lineHeight
|
||||
width = @presenter.getScrollWidth()
|
||||
else
|
||||
{top, left} = @pixelPositionForScreenPosition(screenRange.start, false)
|
||||
height = lineHeight
|
||||
width = @pixelPositionForScreenPosition(screenRange.end, false).left - left
|
||||
|
||||
{top, left, width, height}
|
||||
|
||||
6
spec/fixtures/packages/package-with-deserializers/deserializer-1.js
vendored
Normal file
6
spec/fixtures/packages/package-with-deserializers/deserializer-1.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = function (state) {
|
||||
return {
|
||||
wasDeserializedBy: 'Deserializer1',
|
||||
state: state
|
||||
}
|
||||
}
|
||||
6
spec/fixtures/packages/package-with-deserializers/deserializer-2.js
vendored
Normal file
6
spec/fixtures/packages/package-with-deserializers/deserializer-2.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = function (state) {
|
||||
return {
|
||||
wasDeserializedBy: 'Deserializer2',
|
||||
state: state
|
||||
}
|
||||
}
|
||||
3
spec/fixtures/packages/package-with-deserializers/index.js
vendored
Normal file
3
spec/fixtures/packages/package-with-deserializers/index.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
activate: function() {}
|
||||
}
|
||||
9
spec/fixtures/packages/package-with-deserializers/package.json
vendored
Normal file
9
spec/fixtures/packages/package-with-deserializers/package.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "package-with-deserializers",
|
||||
"version": "1.0.0",
|
||||
"main": "./index",
|
||||
"deserializers": {
|
||||
"Deserializer1": "./deserializer-1.js",
|
||||
"Deserializer2": "./deserializer-2.js"
|
||||
}
|
||||
}
|
||||
5
spec/fixtures/packages/package-with-eval-time-api-calls/index.js
vendored
Normal file
5
spec/fixtures/packages/package-with-eval-time-api-calls/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
atom.deserializers.add('MyDeserializer', function (state) {
|
||||
return {state: state, a: 'b'}
|
||||
})
|
||||
|
||||
exports.activate = function () {}
|
||||
5
spec/fixtures/packages/package-with-eval-time-api-calls/package.json
vendored
Normal file
5
spec/fixtures/packages/package-with-eval-time-api-calls/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "package-with-eval-time-api-calls",
|
||||
"version": "1.2.3",
|
||||
"main": "./index"
|
||||
}
|
||||
13
spec/fixtures/packages/package-with-json-config-schema/package.json
vendored
Normal file
13
spec/fixtures/packages/package-with-json-config-schema/package.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "package-with-json-config-schema",
|
||||
"configSchema": {
|
||||
"a": {
|
||||
"type": "number",
|
||||
"default": 5
|
||||
},
|
||||
"b": {
|
||||
"type": "string",
|
||||
"default": "five"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
'main': 'main-module.coffee'
|
||||
'version': '2.3.4'
|
||||
|
||||
3
spec/fixtures/packages/package-with-view-providers/deserializer.js
vendored
Normal file
3
spec/fixtures/packages/package-with-view-providers/deserializer.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = function (state) {
|
||||
return {state: state}
|
||||
}
|
||||
3
spec/fixtures/packages/package-with-view-providers/index.js
vendored
Normal file
3
spec/fixtures/packages/package-with-view-providers/index.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
activate: function() {}
|
||||
}
|
||||
12
spec/fixtures/packages/package-with-view-providers/package.json
vendored
Normal file
12
spec/fixtures/packages/package-with-view-providers/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "package-with-view-providers",
|
||||
"main": "./index",
|
||||
"version": "1.0.0",
|
||||
"deserializers": {
|
||||
"DeserializerFromPackageWithViewProviders": "./deserializer"
|
||||
},
|
||||
"viewProviders": [
|
||||
"./view-provider-1",
|
||||
"./view-provider-2"
|
||||
]
|
||||
}
|
||||
9
spec/fixtures/packages/package-with-view-providers/view-provider-1.js
vendored
Normal file
9
spec/fixtures/packages/package-with-view-providers/view-provider-1.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = function (model) {
|
||||
if (model.worksWithViewProvider1) {
|
||||
let element = document.createElement('div')
|
||||
element.dataset['createdBy'] = 'view-provider-1'
|
||||
return element
|
||||
}
|
||||
}
|
||||
9
spec/fixtures/packages/package-with-view-providers/view-provider-2.js
vendored
Normal file
9
spec/fixtures/packages/package-with-view-providers/view-provider-2.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = function (model) {
|
||||
if (model.worksWithViewProvider2) {
|
||||
let element = document.createElement('div')
|
||||
element.dataset['createdBy'] = 'view-provider-2'
|
||||
return element
|
||||
}
|
||||
}
|
||||
0
spec/fixtures/testdir/sample-theme-2/src/js/plugin/main.js
vendored
Normal file
0
spec/fixtures/testdir/sample-theme-2/src/js/plugin/main.js
vendored
Normal file
@@ -2,7 +2,7 @@ LinesYardstick = require "../src/lines-yardstick"
|
||||
{toArray} = require 'underscore-plus'
|
||||
|
||||
describe "LinesYardstick", ->
|
||||
[editor, mockPresenter, mockLineNodesProvider, createdLineNodes, linesYardstick, buildLineNode] = []
|
||||
[editor, mockLineNodesProvider, createdLineNodes, linesYardstick, buildLineNode] = []
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
@@ -31,22 +31,10 @@ describe "LinesYardstick", ->
|
||||
createdLineNodes.push(lineNode)
|
||||
lineNode
|
||||
|
||||
mockPresenter =
|
||||
setScreenRowsToMeasure: (screenRows) -> screenRowsToMeasure = screenRows
|
||||
clearScreenRowsToMeasure: -> setScreenRowsToMeasure = []
|
||||
getPreMeasurementState: ->
|
||||
state = {}
|
||||
for screenRow in screenRowsToMeasure
|
||||
tokenizedLine = editor.tokenizedLineForScreenRow(screenRow)
|
||||
state[tokenizedLine.id] = screenRow
|
||||
state
|
||||
|
||||
mockLineNodesProvider =
|
||||
updateSync: (state) -> availableScreenRows = state
|
||||
lineNodeForLineIdAndScreenRow: (lineId, screenRow) ->
|
||||
return if availableScreenRows[lineId] isnt screenRow
|
||||
|
||||
buildLineNode(screenRow)
|
||||
|
||||
textNodesForLineIdAndScreenRow: (lineId, screenRow) ->
|
||||
lineNode = @lineNodeForLineIdAndScreenRow(lineId, screenRow)
|
||||
iterator = document.createNodeIterator(lineNode, NodeFilter.SHOW_TEXT)
|
||||
@@ -56,7 +44,7 @@ describe "LinesYardstick", ->
|
||||
textNodes
|
||||
|
||||
editor.setLineHeightInPixels(14)
|
||||
linesYardstick = new LinesYardstick(editor, mockPresenter, mockLineNodesProvider, atom.grammars)
|
||||
linesYardstick = new LinesYardstick(editor, mockLineNodesProvider, atom.grammars)
|
||||
|
||||
afterEach ->
|
||||
lineNode.remove() for lineNode in createdLineNodes
|
||||
@@ -153,18 +141,6 @@ describe "LinesYardstick", ->
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 36]).left).toBe 237.5
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 37]).left).toBe 244.09375
|
||||
|
||||
it "doesn't measure invisible lines if it is explicitly told so", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 0], true, true)).toEqual({left: 0, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 1], true, true)).toEqual({left: 0, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 5], true, true)).toEqual({left: 0, top: 0})
|
||||
|
||||
describe "::screenPositionForPixelPosition(pixelPosition)", ->
|
||||
it "converts pixel positions to screen positions", ->
|
||||
atom.styles.addStyleSheet """
|
||||
@@ -197,15 +173,3 @@ describe "LinesYardstick", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: Infinity, left: Infinity)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: (editor.getLastScreenRow() + 1) * 14, left: 0)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: editor.getLastScreenRow() * 14, left: 0)).toEqual [12, 0]
|
||||
|
||||
it "doesn't measure invisible lines if it is explicitly told so", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 13}, true)).toEqual([0, 0])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 14, left: 20}, true)).toEqual([1, 0])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 28, left: 100}, true)).toEqual([2, 0])
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
path = require 'path'
|
||||
Package = require '../src/package'
|
||||
temp = require 'temp'
|
||||
fs = require 'fs-plus'
|
||||
{Disposable} = require 'atom'
|
||||
{buildKeydownEvent} = require '../src/keymap-extensions'
|
||||
{mockLocalStorage} = require './spec-helper'
|
||||
|
||||
describe "PackageManager", ->
|
||||
workspaceElement = null
|
||||
@@ -79,6 +83,121 @@ describe "PackageManager", ->
|
||||
|
||||
expect(loadedPackage.name).toBe "package-with-main"
|
||||
|
||||
it "registers any deserializers specified in the package's package.json", ->
|
||||
pack = atom.packages.loadPackage("package-with-deserializers")
|
||||
|
||||
state1 = {deserializer: 'Deserializer1', a: 'b'}
|
||||
expect(atom.deserializers.deserialize(state1)).toEqual {
|
||||
wasDeserializedBy: 'Deserializer1'
|
||||
state: state1
|
||||
}
|
||||
|
||||
state2 = {deserializer: 'Deserializer2', c: 'd'}
|
||||
expect(atom.deserializers.deserialize(state2)).toEqual {
|
||||
wasDeserializedBy: 'Deserializer2'
|
||||
state: state2
|
||||
}
|
||||
|
||||
expect(pack.mainModule).toBeNull()
|
||||
|
||||
describe "when there are view providers specified in the package's package.json", ->
|
||||
model1 = {worksWithViewProvider1: true}
|
||||
model2 = {worksWithViewProvider2: true}
|
||||
|
||||
afterEach ->
|
||||
atom.packages.deactivatePackage('package-with-view-providers')
|
||||
atom.packages.unloadPackage('package-with-view-providers')
|
||||
|
||||
it "does not load the view providers immediately", ->
|
||||
pack = atom.packages.loadPackage("package-with-view-providers")
|
||||
expect(pack.mainModule).toBeNull()
|
||||
|
||||
expect(-> atom.views.getView(model1)).toThrow()
|
||||
expect(-> atom.views.getView(model2)).toThrow()
|
||||
|
||||
it "registers the view providers when the package is activated", ->
|
||||
pack = atom.packages.loadPackage("package-with-view-providers")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-view-providers").then ->
|
||||
element1 = atom.views.getView(model1)
|
||||
expect(element1 instanceof HTMLDivElement).toBe true
|
||||
expect(element1.dataset.createdBy).toBe 'view-provider-1'
|
||||
|
||||
element2 = atom.views.getView(model2)
|
||||
expect(element2 instanceof HTMLDivElement).toBe true
|
||||
expect(element2.dataset.createdBy).toBe 'view-provider-2'
|
||||
|
||||
it "registers the view providers when any of the package's deserializers are used", ->
|
||||
pack = atom.packages.loadPackage("package-with-view-providers")
|
||||
|
||||
spyOn(atom.views, 'addViewProvider').andCallThrough()
|
||||
atom.deserializers.deserialize({
|
||||
deserializer: 'DeserializerFromPackageWithViewProviders',
|
||||
a: 'b'
|
||||
})
|
||||
expect(atom.views.addViewProvider.callCount).toBe 2
|
||||
|
||||
atom.deserializers.deserialize({
|
||||
deserializer: 'DeserializerFromPackageWithViewProviders',
|
||||
a: 'b'
|
||||
})
|
||||
expect(atom.views.addViewProvider.callCount).toBe 2
|
||||
|
||||
element1 = atom.views.getView(model1)
|
||||
expect(element1 instanceof HTMLDivElement).toBe true
|
||||
expect(element1.dataset.createdBy).toBe 'view-provider-1'
|
||||
|
||||
element2 = atom.views.getView(model2)
|
||||
expect(element2 instanceof HTMLDivElement).toBe true
|
||||
expect(element2.dataset.createdBy).toBe 'view-provider-2'
|
||||
|
||||
it "registers the config schema in the package's metadata, if present", ->
|
||||
pack = atom.packages.loadPackage("package-with-json-config-schema")
|
||||
expect(atom.config.getSchema('package-with-json-config-schema')).toEqual {
|
||||
type: 'object'
|
||||
properties: {
|
||||
a: {type: 'number', default: 5}
|
||||
b: {type: 'string', default: 'five'}
|
||||
}
|
||||
}
|
||||
|
||||
expect(pack.mainModule).toBeNull()
|
||||
|
||||
atom.packages.unloadPackage('package-with-json-config-schema')
|
||||
atom.config.clear()
|
||||
|
||||
pack = atom.packages.loadPackage("package-with-json-config-schema")
|
||||
expect(atom.config.getSchema('package-with-json-config-schema')).toEqual {
|
||||
type: 'object'
|
||||
properties: {
|
||||
a: {type: 'number', default: 5}
|
||||
b: {type: 'string', default: 'five'}
|
||||
}
|
||||
}
|
||||
|
||||
describe "when a package does not have deserializers, view providers or a config schema in its package.json", ->
|
||||
beforeEach ->
|
||||
mockLocalStorage()
|
||||
|
||||
it "defers loading the package's main module if the package previously used no Atom APIs when its main module was required", ->
|
||||
pack1 = atom.packages.loadPackage('package-with-main')
|
||||
expect(pack1.mainModule).toBeDefined()
|
||||
|
||||
atom.packages.unloadPackage('package-with-main')
|
||||
|
||||
pack2 = atom.packages.loadPackage('package-with-main')
|
||||
expect(pack2.mainModule).toBeNull()
|
||||
|
||||
it "does not defer loading the package's main module if the package previously used Atom APIs when its main module was required", ->
|
||||
pack1 = atom.packages.loadPackage('package-with-eval-time-api-calls')
|
||||
expect(pack1.mainModule).toBeDefined()
|
||||
|
||||
atom.packages.unloadPackage('package-with-eval-time-api-calls')
|
||||
|
||||
pack2 = atom.packages.loadPackage('package-with-eval-time-api-calls')
|
||||
expect(pack2.mainModule).not.toBeNull()
|
||||
|
||||
describe "::unloadPackage(name)", ->
|
||||
describe "when the package is active", ->
|
||||
it "throws an error", ->
|
||||
@@ -456,6 +575,54 @@ describe "PackageManager", ->
|
||||
atom.config.set("core.packagesWithKeymapsDisabled", [])
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe 'keymap-1'
|
||||
|
||||
describe "when the package is de-activated and re-activated", ->
|
||||
[element, events, userKeymapPath] = []
|
||||
|
||||
beforeEach ->
|
||||
userKeymapPath = path.join(temp.path(), "user-keymaps.cson")
|
||||
spyOn(atom.keymaps, "getUserKeymapPath").andReturn(userKeymapPath)
|
||||
|
||||
element = createTestElement('test-1')
|
||||
jasmine.attachToDOM(element)
|
||||
|
||||
events = []
|
||||
element.addEventListener 'user-command', (e) -> events.push(e)
|
||||
element.addEventListener 'test-1', (e) -> events.push(e)
|
||||
|
||||
afterEach ->
|
||||
element.remove()
|
||||
|
||||
# Avoid leaking user keymap subscription
|
||||
atom.keymaps.watchSubscriptions[userKeymapPath].dispose()
|
||||
delete atom.keymaps.watchSubscriptions[userKeymapPath]
|
||||
|
||||
it "doesn't override user-defined keymaps", ->
|
||||
fs.writeFileSync userKeymapPath, """
|
||||
".test-1":
|
||||
"ctrl-z": "user-command"
|
||||
"""
|
||||
atom.keymaps.loadUserKeymap()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-keymaps")
|
||||
|
||||
runs ->
|
||||
atom.keymaps.handleKeyboardEvent(buildKeydownEvent("z", ctrl: true, target: element))
|
||||
|
||||
expect(events.length).toBe(1)
|
||||
expect(events[0].type).toBe("user-command")
|
||||
|
||||
atom.packages.deactivatePackage("package-with-keymaps")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-keymaps")
|
||||
|
||||
runs ->
|
||||
atom.keymaps.handleKeyboardEvent(buildKeydownEvent("z", ctrl: true, target: element))
|
||||
|
||||
expect(events.length).toBe(2)
|
||||
expect(events[1].type).toBe("user-command")
|
||||
|
||||
describe "menu loading", ->
|
||||
beforeEach ->
|
||||
atom.contextMenu.definitions = []
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
path = require 'path'
|
||||
Package = require '../src/package'
|
||||
ThemePackage = require '../src/theme-package'
|
||||
{mockLocalStorage} = require './spec-helper'
|
||||
|
||||
describe "Package", ->
|
||||
build = (constructor, path) ->
|
||||
@@ -10,6 +11,7 @@ describe "Package", ->
|
||||
keymapManager: atom.keymaps, commandRegistry: atom.command,
|
||||
grammarRegistry: atom.grammars, themeManager: atom.themes,
|
||||
menuManager: atom.menu, contextMenuManager: atom.contextMenu,
|
||||
deserializerManager: atom.deserializers, viewRegistry: atom.views,
|
||||
devMode: false
|
||||
)
|
||||
|
||||
@@ -19,10 +21,7 @@ describe "Package", ->
|
||||
|
||||
describe "when the package contains incompatible native modules", ->
|
||||
beforeEach ->
|
||||
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
|
||||
mockLocalStorage()
|
||||
|
||||
it "does not activate it", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
|
||||
@@ -54,10 +53,7 @@ describe "Package", ->
|
||||
|
||||
describe "::rebuild()", ->
|
||||
beforeEach ->
|
||||
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
|
||||
mockLocalStorage()
|
||||
|
||||
it "returns a promise resolving to the results of `apm rebuild`", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-index')
|
||||
|
||||
@@ -265,3 +265,9 @@ window.advanceClock = (delta=1) ->
|
||||
true
|
||||
|
||||
callback() for callback in callbacks
|
||||
|
||||
exports.mockLocalStorage = ->
|
||||
items = {}
|
||||
spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item.toString(); undefined
|
||||
spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null
|
||||
spyOn(global.localStorage, 'removeItem').andCallFake (key) -> delete items[key]; undefined
|
||||
|
||||
@@ -484,7 +484,7 @@ describe('TextEditorComponent', function () {
|
||||
it('displays newlines as their own token outside of the other tokens\' scopeDescriptor', async function () {
|
||||
editor.setText('let\n')
|
||||
await nextViewUpdatePromise()
|
||||
expect(component.lineNodeForScreenRow(0).innerHTML).toBe('<span class="source js"><span class="storage modifier js">let</span></span><span class="invisible-character">' + invisibles.eol + '</span>')
|
||||
expect(component.lineNodeForScreenRow(0).innerHTML).toBe('<span class="source js"><span class="storage type var js">let</span></span><span class="invisible-character">' + invisibles.eol + '</span>')
|
||||
})
|
||||
|
||||
it('displays trailing carriage returns using a visible, non-empty value', async function () {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,17 +22,6 @@ describe "TextEditor", ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
describe "when the editor is deserialized", ->
|
||||
it "returns undefined when the path cannot be read", ->
|
||||
pathToOpen = path.join(temp.mkdirSync(), 'file.txt')
|
||||
editor1 = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(pathToOpen).then (o) -> editor1 = o
|
||||
|
||||
runs ->
|
||||
fs.mkdirSync(pathToOpen)
|
||||
expect(TextEditor.deserialize(editor1.serialize(), atom)).toBeUndefined()
|
||||
|
||||
it "restores selections and folds based on markers in the buffer", ->
|
||||
editor.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
editor.addSelectionForBufferRange([[5, 6], [7, 5]], reversed: true)
|
||||
@@ -168,7 +157,7 @@ describe "TextEditor", ->
|
||||
buffer.setPath(undefined)
|
||||
expect(editor.getLongTitle()).toBe 'untitled'
|
||||
|
||||
it "returns <parent-directory>/<filename> when opened files has identical file names", ->
|
||||
it "returns '<filename> — <parent-directory>' when opened files have identical file names", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
waitsForPromise ->
|
||||
@@ -177,10 +166,10 @@ describe "TextEditor", ->
|
||||
atom.workspace.open(path.join('sample-theme-2', 'readme')).then (o) ->
|
||||
editor2 = o
|
||||
runs ->
|
||||
expect(editor1.getLongTitle()).toBe 'sample-theme-1/readme'
|
||||
expect(editor2.getLongTitle()).toBe 'sample-theme-2/readme'
|
||||
expect(editor1.getLongTitle()).toBe "readme \u2014 sample-theme-1"
|
||||
expect(editor2.getLongTitle()).toBe "readme \u2014 sample-theme-2"
|
||||
|
||||
it "or returns <parent-directory>/.../<filename> when opened files has identical file names", ->
|
||||
it "returns '<filename> — <parent-directories>' when opened files have identical file and dir names", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
waitsForPromise ->
|
||||
@@ -189,9 +178,20 @@ describe "TextEditor", ->
|
||||
atom.workspace.open(path.join('sample-theme-2', 'src', 'js', 'main.js')).then (o) ->
|
||||
editor2 = o
|
||||
runs ->
|
||||
expect(editor1.getLongTitle()).toBe 'sample-theme-1/.../main.js'
|
||||
expect(editor2.getLongTitle()).toBe 'sample-theme-2/.../main.js'
|
||||
expect(editor1.getLongTitle()).toBe "main.js \u2014 sample-theme-1/src/js"
|
||||
expect(editor2.getLongTitle()).toBe "main.js \u2014 sample-theme-2/src/js"
|
||||
|
||||
it "returns '<filename> — <parent-directories>' when opened files have identical file and same parent dir name", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(path.join('sample-theme-2', 'src', 'js', 'main.js')).then (o) ->
|
||||
editor1 = o
|
||||
atom.workspace.open(path.join('sample-theme-2', 'src', 'js', 'plugin', 'main.js')).then (o) ->
|
||||
editor2 = o
|
||||
runs ->
|
||||
expect(editor1.getLongTitle()).toBe "main.js \u2014 js"
|
||||
expect(editor2.getLongTitle()).toBe "main.js \u2014 js/plugin"
|
||||
|
||||
it "notifies ::onDidChangeTitle observers when the underlying buffer path changes", ->
|
||||
observed = []
|
||||
@@ -751,11 +751,24 @@ describe "TextEditor", ->
|
||||
editor.moveToBeginningOfWord()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
|
||||
|
||||
it "treats lines with only whitespace as a word (CRLF line ending)", ->
|
||||
editor.buffer.setText(buffer.getText().replace(/\n/g, "\r\n"))
|
||||
editor.setCursorBufferPosition([11, 0])
|
||||
editor.moveToBeginningOfWord()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
|
||||
|
||||
it "works when the current line is blank", ->
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
editor.moveToBeginningOfWord()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [9, 2]
|
||||
|
||||
it "works when the current line is blank (CRLF line ending)", ->
|
||||
editor.buffer.setText(buffer.getText().replace(/\n/g, "\r\n"))
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
editor.moveToBeginningOfWord()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [9, 2]
|
||||
editor.buffer.setText(buffer.getText().replace(/\r\n/g, "\n"))
|
||||
|
||||
describe ".moveToPreviousWordBoundary()", ->
|
||||
it "moves the cursor to the previous word boundary", ->
|
||||
editor.setCursorBufferPosition [0, 8]
|
||||
@@ -810,11 +823,23 @@ describe "TextEditor", ->
|
||||
editor.moveToEndOfWord()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
|
||||
|
||||
it "treats lines with only whitespace as a word (CRLF line ending)", ->
|
||||
editor.buffer.setText(buffer.getText().replace(/\n/g, "\r\n"))
|
||||
editor.setCursorBufferPosition([9, 4])
|
||||
editor.moveToEndOfWord()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
|
||||
|
||||
it "works when the current line is blank", ->
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
editor.moveToEndOfWord()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [11, 8]
|
||||
|
||||
it "works when the current line is blank (CRLF line ending)", ->
|
||||
editor.buffer.setText(buffer.getText().replace(/\n/g, "\r\n"))
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
editor.moveToEndOfWord()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [11, 8]
|
||||
|
||||
describe ".moveToBeginningOfNextWord()", ->
|
||||
it "moves the cursor before the first character of the next word", ->
|
||||
editor.setCursorBufferPosition [0, 6]
|
||||
@@ -1044,8 +1069,36 @@ describe "TextEditor", ->
|
||||
editor.moveToBeginningOfNextParagraph()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||
|
||||
it "moves the cursor before the first line of the next paragraph (CRLF line endings)", ->
|
||||
editor.setText(editor.getText().replace(/\n/g, '\r\n'))
|
||||
|
||||
editor.setCursorBufferPosition [0, 6]
|
||||
editor.foldBufferRow(4)
|
||||
|
||||
editor.moveToBeginningOfNextParagraph()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
|
||||
|
||||
editor.setText("")
|
||||
editor.setCursorBufferPosition [0, 0]
|
||||
editor.moveToBeginningOfNextParagraph()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||
|
||||
describe ".moveToBeginningOfPreviousParagraph()", ->
|
||||
it "moves the cursor before the first line of the pevious paragraph", ->
|
||||
it "moves the cursor before the first line of the previous paragraph", ->
|
||||
editor.setCursorBufferPosition [10, 0]
|
||||
editor.foldBufferRow(4)
|
||||
|
||||
editor.moveToBeginningOfPreviousParagraph()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||
|
||||
editor.setText("")
|
||||
editor.setCursorBufferPosition [0, 0]
|
||||
editor.moveToBeginningOfPreviousParagraph()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||
|
||||
it "moves the cursor before the first line of the previous paragraph (CRLF line endings)", ->
|
||||
editor.setText(editor.getText().replace(/\n/g, '\r\n'))
|
||||
|
||||
editor.setCursorBufferPosition [10, 0]
|
||||
editor.foldBufferRow(4)
|
||||
|
||||
@@ -5326,7 +5379,7 @@ describe "TextEditor", ->
|
||||
|
||||
tokens = atom.grammars.decodeTokens(line, tags)
|
||||
expect(tokens[0].value).toBe "var"
|
||||
expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"]
|
||||
expect(tokens[0].scopes).toEqual ["source.js", "storage.type.var.js"]
|
||||
|
||||
expect(tokens[6].value).toBe "http://github.com"
|
||||
expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
@@ -24,6 +24,34 @@ describe "TokenizedBuffer", ->
|
||||
advanceClock() while tokenizedBuffer.firstInvalidRow()?
|
||||
changeHandler?.reset()
|
||||
|
||||
describe "serialization", ->
|
||||
describe "when the underlying buffer has a path", ->
|
||||
it "deserializes it searching among the buffers in the current project", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBufferA = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
tokenizedBufferB = TokenizedBuffer.deserialize(
|
||||
JSON.parse(JSON.stringify(tokenizedBufferA.serialize())),
|
||||
atom
|
||||
)
|
||||
|
||||
expect(tokenizedBufferB.buffer).toBe(tokenizedBufferA.buffer)
|
||||
|
||||
describe "when the underlying buffer has no path", ->
|
||||
it "deserializes it searching among the buffers in the current project", ->
|
||||
buffer = atom.project.bufferForPathSync(null)
|
||||
|
||||
tokenizedBufferA = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
tokenizedBufferB = TokenizedBuffer.deserialize(
|
||||
JSON.parse(JSON.stringify(tokenizedBufferA.serialize())),
|
||||
atom
|
||||
)
|
||||
|
||||
expect(tokenizedBufferB.buffer).toBe(tokenizedBufferA.buffer)
|
||||
|
||||
describe "when the buffer is destroyed", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
@@ -198,7 +226,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.setTextInRange([[1, 0], [3, 0]], "foo()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
||||
|
||||
# previous line 3 should be combined with input to form line 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
@@ -242,7 +270,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.setTextInRange([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
||||
|
||||
# 3 new lines inserted
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
||||
@@ -582,7 +610,7 @@ describe "TokenizedBuffer", ->
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([1, 0]).scopes).toEqual ["source.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1, 1]).scopes).toEqual ["source.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1, 2]).scopes).toEqual ["source.js", "storage.modifier.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1, 2]).scopes).toEqual ["source.js", "storage.type.var.js"]
|
||||
|
||||
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
|
||||
beforeEach ->
|
||||
@@ -599,8 +627,8 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the selector matches a single token at the position", ->
|
||||
it "returns the range covered by the token", ->
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.storage.modifier.js', [0, 1])).toEqual [[0, 0], [0, 3]]
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.storage.modifier.js', [0, 3])).toEqual [[0, 0], [0, 3]]
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.storage.type.var.js', [0, 1])).toEqual [[0, 0], [0, 3]]
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.storage.type.var.js', [0, 3])).toEqual [[0, 0], [0, 3]]
|
||||
|
||||
describe "when the selector matches a run of multiple tokens at the position", ->
|
||||
it "returns the range covered by all contigous tokens (within a single line)", ->
|
||||
|
||||
@@ -47,6 +47,21 @@ describe "ViewRegistry", ->
|
||||
expect(view2 instanceof TestView).toBe true
|
||||
expect(view2.model).toBe subclassModel
|
||||
|
||||
describe "when a view provider is registered generically, and works with the object", ->
|
||||
it "constructs a view element and assigns the model on it", ->
|
||||
model = {a: 'b'}
|
||||
|
||||
registry.addViewProvider (model) ->
|
||||
if model.a is 'b'
|
||||
element = document.createElement('div')
|
||||
element.className = 'test-element'
|
||||
element
|
||||
|
||||
view = registry.getView({a: 'b'})
|
||||
expect(view.className).toBe 'test-element'
|
||||
|
||||
expect(-> registry.getView({a: 'c'})).toThrow()
|
||||
|
||||
describe "when no view provider is registered for the object's constructor", ->
|
||||
it "throws an exception", ->
|
||||
expect(-> registry.getView(new Object)).toThrow()
|
||||
|
||||
@@ -200,3 +200,34 @@ describe "WindowEventHandler", ->
|
||||
|
||||
expect(dispatchedCommands.length).toBe 1
|
||||
expect(dispatchedCommands[0].type).toBe 'foo-command'
|
||||
|
||||
describe "native key bindings", ->
|
||||
it "correctly dispatches them to active elements with the '.native-key-bindings' class", ->
|
||||
webContentsSpy = jasmine.createSpyObj("webContents", ["copy", "paste"])
|
||||
spyOn(atom.applicationDelegate, "getCurrentWindow").andReturn({
|
||||
webContents: webContentsSpy
|
||||
})
|
||||
|
||||
nativeKeyBindingsInput = document.createElement("input")
|
||||
nativeKeyBindingsInput.classList.add("native-key-bindings")
|
||||
jasmine.attachToDOM(nativeKeyBindingsInput)
|
||||
nativeKeyBindingsInput.focus()
|
||||
|
||||
atom.dispatchApplicationMenuCommand("core:copy")
|
||||
atom.dispatchApplicationMenuCommand("core:paste")
|
||||
|
||||
expect(webContentsSpy.copy).toHaveBeenCalled()
|
||||
expect(webContentsSpy.paste).toHaveBeenCalled()
|
||||
|
||||
webContentsSpy.copy.reset()
|
||||
webContentsSpy.paste.reset()
|
||||
|
||||
normalInput = document.createElement("input")
|
||||
jasmine.attachToDOM(normalInput)
|
||||
normalInput.focus()
|
||||
|
||||
atom.dispatchApplicationMenuCommand("core:copy")
|
||||
atom.dispatchApplicationMenuCommand("core:paste")
|
||||
|
||||
expect(webContentsSpy.copy).not.toHaveBeenCalled()
|
||||
expect(webContentsSpy.paste).not.toHaveBeenCalled()
|
||||
|
||||
@@ -43,6 +43,9 @@ describe "Workspace", ->
|
||||
pane3 = pane2.splitRight(copyActiveItem: true)
|
||||
pane4 = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(null).then (editor) -> editor.setText("An untitled editor.")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b').then (editor) ->
|
||||
pane2.activateItem(editor.copy())
|
||||
@@ -65,18 +68,19 @@ describe "Workspace", ->
|
||||
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspace.getTextEditors().length).toBe 4
|
||||
[editor1, editor2, editor3, editor4] = atom.workspace.getTextEditors()
|
||||
|
||||
expect(atom.workspace.getTextEditors().length).toBe 5
|
||||
[editor1, editor2, untitledEditor, editor3, editor4] = atom.workspace.getTextEditors()
|
||||
expect(editor1.getPath()).toBe atom.project.getDirectories()[0]?.resolve('b')
|
||||
expect(editor2.getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.txt')
|
||||
expect(editor2.getCursorScreenPosition()).toEqual [0, 2]
|
||||
expect(editor3.getPath()).toBe atom.project.getDirectories()[0]?.resolve('b')
|
||||
expect(editor4.getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.js')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(untitledEditor.getPath()).toBeUndefined()
|
||||
expect(untitledEditor.getText()).toBe("An untitled editor.")
|
||||
|
||||
expect(atom.workspace.getActiveTextEditor().getPath()).toBe editor3.getPath()
|
||||
expect(document.title).toBe "#{path.basename(editor3.getPath())} - #{atom.project.getPaths()[0]} - Atom"
|
||||
expect(document.title).toMatch ///^#{path.basename(editor3.getLongTitle())}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
|
||||
describe "where there are no open panes or editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
@@ -661,7 +665,7 @@ describe "Workspace", ->
|
||||
describe "::isTextEditor(obj)", ->
|
||||
it "returns true when the passed object is an instance of `TextEditor`", ->
|
||||
expect(workspace.isTextEditor(atom.workspace.buildTextEditor())).toBe(true)
|
||||
expect(workspace.isTextEditor({getText: ->})).toBe(false)
|
||||
expect(workspace.isTextEditor({getText: -> null})).toBe(false)
|
||||
expect(workspace.isTextEditor(null)).toBe(false)
|
||||
expect(workspace.isTextEditor(undefined)).toBe(false)
|
||||
|
||||
@@ -732,7 +736,7 @@ describe "Workspace", ->
|
||||
describe "when the project has no path", ->
|
||||
it "sets the title to 'untitled'", ->
|
||||
atom.project.setPaths([])
|
||||
expect(document.title).toBe 'untitled - Atom'
|
||||
expect(document.title).toMatch ///^untitled///
|
||||
|
||||
describe "when the project has a path", ->
|
||||
beforeEach ->
|
||||
@@ -742,25 +746,25 @@ describe "Workspace", ->
|
||||
describe "when there is an active pane item", ->
|
||||
it "sets the title to the pane item's title plus the project path", ->
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]} - Atom"
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editor = atom.workspace.getActivePaneItem()
|
||||
editor.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
expect(document.title).toBe "#{editor.getTitle()} - #{atom.project.getPaths()[0]} - Atom"
|
||||
expect(document.title).toMatch ///^#{editor.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
atom.workspace.getActivePane().activateNextItem()
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]} - Atom"
|
||||
expect(document.title).toMatch ///^#{item.getTitle()}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
atom.workspace.getActivePane().destroy()
|
||||
expect(atom.workspace.getActivePaneItem()).toBeUndefined()
|
||||
expect(document.title).toBe "#{atom.project.getPaths()[0]} - Atom"
|
||||
expect(document.title).toMatch ///^#{atom.project.getPaths()[0]}///
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
@@ -783,8 +787,8 @@ describe "Workspace", ->
|
||||
applicationDelegate: atom.applicationDelegate, assert: atom.assert.bind(atom)
|
||||
})
|
||||
workspace2.deserialize(atom.workspace.serialize(), atom.deserializers)
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]} - Atom"
|
||||
item = workspace2.getActivePaneItem()
|
||||
expect(document.title).toMatch ///^#{item.getLongTitle()}\ \u2014\ #{atom.project.getPaths()[0]}///
|
||||
workspace2.destroy()
|
||||
|
||||
describe "document edited status", ->
|
||||
@@ -1445,11 +1449,12 @@ describe "Workspace", ->
|
||||
save = -> atom.workspace.saveActivePaneItem()
|
||||
expect(save).toThrow()
|
||||
|
||||
describe "::destroyActivePaneItemOrEmptyPane", ->
|
||||
describe "::closeActivePaneItemOrEmptyPaneOrWindow", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'close')
|
||||
waitsForPromise -> atom.workspace.open()
|
||||
|
||||
it "closes the active pane item until all that remains is a single empty pane", ->
|
||||
it "closes the active pane item, or the active pane if it is empty, or the current window if there is only the empty root pane", ->
|
||||
atom.config.set('core.destroyEmptyPanes', false)
|
||||
|
||||
pane1 = atom.workspace.getActivePane()
|
||||
@@ -1457,19 +1462,22 @@ describe "Workspace", ->
|
||||
|
||||
expect(atom.workspace.getPanes().length).toBe 2
|
||||
expect(pane2.getItems().length).toBe 1
|
||||
atom.workspace.destroyActivePaneItemOrEmptyPane()
|
||||
atom.workspace.closeActivePaneItemOrEmptyPaneOrWindow()
|
||||
|
||||
expect(atom.workspace.getPanes().length).toBe 2
|
||||
expect(pane2.getItems().length).toBe 0
|
||||
|
||||
atom.workspace.destroyActivePaneItemOrEmptyPane()
|
||||
atom.workspace.closeActivePaneItemOrEmptyPaneOrWindow()
|
||||
|
||||
expect(atom.workspace.getPanes().length).toBe 1
|
||||
expect(pane1.getItems().length).toBe 1
|
||||
|
||||
atom.workspace.destroyActivePaneItemOrEmptyPane()
|
||||
atom.workspace.closeActivePaneItemOrEmptyPaneOrWindow()
|
||||
expect(atom.workspace.getPanes().length).toBe 1
|
||||
expect(pane1.getItems().length).toBe 0
|
||||
|
||||
atom.workspace.destroyActivePaneItemOrEmptyPane()
|
||||
atom.workspace.closeActivePaneItemOrEmptyPaneOrWindow()
|
||||
expect(atom.workspace.getPanes().length).toBe 1
|
||||
|
||||
atom.workspace.closeActivePaneItemOrEmptyPaneOrWindow()
|
||||
expect(atom.close).toHaveBeenCalled()
|
||||
|
||||
@@ -66,13 +66,42 @@ class ApplicationDelegate
|
||||
ipc.send("call-window-method", "setFullScreen", fullScreen)
|
||||
|
||||
openWindowDevTools: ->
|
||||
remote.getCurrentWindow().openDevTools()
|
||||
new Promise (resolve) ->
|
||||
# Defer DevTools interaction to the next tick, because using them during
|
||||
# event handling causes some wrong input events to be triggered on
|
||||
# `TextEditorComponent` (Ref.: https://github.com/atom/atom/issues/9697).
|
||||
process.nextTick ->
|
||||
if remote.getCurrentWindow().isDevToolsOpened()
|
||||
resolve()
|
||||
else
|
||||
remote.getCurrentWindow().once("devtools-opened", -> resolve())
|
||||
ipc.send("call-window-method", "openDevTools")
|
||||
|
||||
closeWindowDevTools: ->
|
||||
new Promise (resolve) ->
|
||||
# Defer DevTools interaction to the next tick, because using them during
|
||||
# event handling causes some wrong input events to be triggered on
|
||||
# `TextEditorComponent` (Ref.: https://github.com/atom/atom/issues/9697).
|
||||
process.nextTick ->
|
||||
unless remote.getCurrentWindow().isDevToolsOpened()
|
||||
resolve()
|
||||
else
|
||||
remote.getCurrentWindow().once("devtools-closed", -> resolve())
|
||||
ipc.send("call-window-method", "closeDevTools")
|
||||
|
||||
toggleWindowDevTools: ->
|
||||
remote.getCurrentWindow().toggleDevTools()
|
||||
new Promise (resolve) =>
|
||||
# Defer DevTools interaction to the next tick, because using them during
|
||||
# event handling causes some wrong input events to be triggered on
|
||||
# `TextEditorComponent` (Ref.: https://github.com/atom/atom/issues/9697).
|
||||
process.nextTick =>
|
||||
if remote.getCurrentWindow().isDevToolsOpened()
|
||||
@closeWindowDevTools().then(resolve)
|
||||
else
|
||||
@openWindowDevTools().then(resolve)
|
||||
|
||||
executeJavaScriptInWindowDevTools: (code) ->
|
||||
remote.getCurrentWindow().executeJavaScriptInDevTools(code)
|
||||
ipc.send("call-window-method", "executeJavaScriptInDevTools", code)
|
||||
|
||||
setWindowDocumentEdited: (edited) ->
|
||||
ipc.send("call-window-method", "setDocumentEdited", edited)
|
||||
@@ -80,6 +109,9 @@ class ApplicationDelegate
|
||||
setRepresentedFilename: (filename) ->
|
||||
ipc.send("call-window-method", "setRepresentedFilename", filename)
|
||||
|
||||
addRecentDocument: (filename) ->
|
||||
ipc.send("add-recent-document", filename)
|
||||
|
||||
setRepresentedDirectoryPaths: (paths) ->
|
||||
loadSettings = getWindowLoadSettings()
|
||||
loadSettings['initialPaths'] = paths
|
||||
|
||||
@@ -151,7 +151,7 @@ class AtomEnvironment extends Model
|
||||
@packages = new PackageManager({
|
||||
devMode, configDirPath, resourcePath, safeMode, @config, styleManager: @styles,
|
||||
commandRegistry: @commands, keymapManager: @keymaps, notificationManager: @notifications,
|
||||
grammarRegistry: @grammars
|
||||
grammarRegistry: @grammars, deserializerManager: @deserializers, viewRegistry: @views
|
||||
})
|
||||
|
||||
@themes = new ThemeManager({
|
||||
@@ -670,8 +670,7 @@ class AtomEnvironment extends Model
|
||||
@emitter.emit 'will-throw-error', eventObject
|
||||
|
||||
if openDevTools
|
||||
@openDevTools()
|
||||
@executeJavaScriptInDevTools('DevToolsAPI.showConsole()')
|
||||
@openDevTools().then => @executeJavaScriptInDevTools('DevToolsAPI.showConsole()')
|
||||
|
||||
@emitter.emit 'did-throw-error', {message, url, line, column, originalError}
|
||||
|
||||
@@ -721,10 +720,15 @@ class AtomEnvironment extends Model
|
||||
###
|
||||
|
||||
# Extended: Open the dev tools for the current window.
|
||||
#
|
||||
# Returns a {Promise} that resolves when the DevTools have been opened.
|
||||
openDevTools: ->
|
||||
@applicationDelegate.openWindowDevTools()
|
||||
|
||||
# Extended: Toggle the visibility of the dev tools for the current window.
|
||||
#
|
||||
# Returns a {Promise} that resolves when the DevTools have been opened or
|
||||
# closed.
|
||||
toggleDevTools: ->
|
||||
@applicationDelegate.toggleWindowDevTools()
|
||||
|
||||
@@ -881,6 +885,8 @@ class AtomEnvironment extends Model
|
||||
else
|
||||
@project.addPath(pathToOpen)
|
||||
|
||||
@applicationDelegate.addRecentDocument(pathToOpen)
|
||||
|
||||
unless fs.isDirectorySync(pathToOpen)
|
||||
@workspace?.open(pathToOpen, {initialLine, initialColumn})
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ class AtomApplication
|
||||
@listenForArgumentsFromNewProcess()
|
||||
@setupJavaScriptArguments()
|
||||
@handleEvents()
|
||||
@setupDockMenu()
|
||||
@storageFolder = new StorageFolder(process.env.ATOM_HOME)
|
||||
|
||||
if options.pathsToOpen?.length > 0 or options.urlsToOpen?.length > 0 or options.test
|
||||
@@ -280,6 +281,16 @@ class AtomApplication
|
||||
ipc.on 'write-to-stderr', (event, output) ->
|
||||
process.stderr.write(output)
|
||||
|
||||
ipc.on 'add-recent-document', (event, filename) ->
|
||||
app.addRecentDocument(filename)
|
||||
|
||||
setupDockMenu: ->
|
||||
if process.platform is 'darwin'
|
||||
dockMenu = Menu.buildFromTemplate [
|
||||
{label: 'New Window', click: => @emit('application:new-window')}
|
||||
]
|
||||
app.dock.setMenu dockMenu
|
||||
|
||||
# Public: Executes the given command.
|
||||
#
|
||||
# If it isn't handled globally, delegate to the currently focused window.
|
||||
|
||||
@@ -158,25 +158,39 @@ require('source-map-support').install({
|
||||
}
|
||||
})
|
||||
|
||||
var sourceMapPrepareStackTrace = Error.prepareStackTrace
|
||||
var prepareStackTrace = sourceMapPrepareStackTrace
|
||||
var prepareStackTraceWithSourceMapping = Error.prepareStackTrace
|
||||
|
||||
// Prevent coffee-script from reassigning Error.prepareStackTrace
|
||||
Object.defineProperty(Error, 'prepareStackTrace', {
|
||||
get: function () { return prepareStackTrace },
|
||||
set: function (newValue) {}
|
||||
})
|
||||
let prepareStackTrace = prepareStackTraceWithSourceMapping
|
||||
|
||||
// Enable Grim to access the raw stack without reassigning Error.prepareStackTrace
|
||||
Error.prototype.getRawStack = function () { // eslint-disable-line no-extend-native
|
||||
prepareStackTrace = getRawStack
|
||||
var result = this.stack
|
||||
prepareStackTrace = sourceMapPrepareStackTrace
|
||||
return result
|
||||
function prepareStackTraceWithRawStackAssignment (error, frames) {
|
||||
if (error.rawStack) { // avoid infinite recursion
|
||||
return prepareStackTraceWithSourceMapping(error, frames)
|
||||
} else {
|
||||
error.rawStack = frames
|
||||
return prepareStackTrace(error, frames)
|
||||
}
|
||||
}
|
||||
|
||||
function getRawStack (_, stack) {
|
||||
return stack
|
||||
Error.stackTraceLimit = 30
|
||||
|
||||
Object.defineProperty(Error, 'prepareStackTrace', {
|
||||
get: function () {
|
||||
return prepareStackTraceWithRawStackAssignment
|
||||
},
|
||||
|
||||
set: function (newValue) {
|
||||
prepareStackTrace = newValue
|
||||
process.nextTick(function () {
|
||||
prepareStackTrace = prepareStackTraceWithSourceMapping
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Error.prototype.getRawStack = function () { // eslint-disable-line no-extend-native
|
||||
// Access this.stack to ensure prepareStackTrace has been run on this error
|
||||
// because it assigns this.rawStack as a side-effect
|
||||
this.stack
|
||||
return this.rawStack
|
||||
}
|
||||
|
||||
Object.keys(COMPILERS).forEach(function (extension) {
|
||||
|
||||
@@ -21,7 +21,6 @@ module.exports =
|
||||
followSymlinks:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
title: 'Follow symlinks'
|
||||
description: 'Follow symbolic links when searching files and when opening files with the fuzzy finder.'
|
||||
disabledPackages:
|
||||
type: 'array'
|
||||
@@ -54,7 +53,12 @@ module.exports =
|
||||
destroyEmptyPanes:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
description: 'When the last item of a pane is removed, remove that pane as well.'
|
||||
title: 'Remove Empty Panes'
|
||||
description: 'When the last tab of a pane is closed, remove that pane as well.'
|
||||
closeEmptyWindows:
|
||||
type: 'boolean'
|
||||
default: true
|
||||
description: 'When a window with no open tabs or panes is given the \'Close Tab\' command, close that window.'
|
||||
fileEncoding:
|
||||
description: 'Default character set encoding to use when reading and writing files.'
|
||||
type: 'string'
|
||||
|
||||
@@ -381,8 +381,8 @@ class Config
|
||||
# ```
|
||||
#
|
||||
# * `keyPath` {String} name of the key to observe
|
||||
# * `options` {Object}
|
||||
# * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
|
||||
# * `options` (optional) {Object}
|
||||
# * `scope` (optional) {ScopeDescriptor} describing a path from
|
||||
# the root of the syntax tree to a token. Get one by calling
|
||||
# {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
|
||||
# See [the scopes docs](https://atom.io/docs/latest/behind-atom-scoped-settings-scopes-and-scope-descriptors)
|
||||
@@ -412,8 +412,8 @@ class Config
|
||||
#
|
||||
# * `keyPath` (optional) {String} name of the key to observe. Must be
|
||||
# specified if `scopeDescriptor` is specified.
|
||||
# * `optional` (optional) {Object}
|
||||
# * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
|
||||
# * `options` (optional) {Object}
|
||||
# * `scope` (optional) {ScopeDescriptor} describing a path from
|
||||
# the root of the syntax tree to a token. Get one by calling
|
||||
# {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
|
||||
# See [the scopes docs](https://atom.io/docs/latest/behind-atom-scoped-settings-scopes-and-scope-descriptors)
|
||||
@@ -827,6 +827,7 @@ class Config
|
||||
|
||||
allSettings = {'*': @settings}
|
||||
allSettings = _.extend allSettings, @scopedSettingsStore.propertiesForSource(@getUserConfigPath())
|
||||
allSettings = sortObject(allSettings)
|
||||
try
|
||||
CSON.writeFileSync(@configFilePath, allSettings)
|
||||
catch error
|
||||
@@ -1190,6 +1191,13 @@ Config.addSchemaEnforcers
|
||||
isPlainObject = (value) ->
|
||||
_.isObject(value) and not _.isArray(value) and not _.isFunction(value) and not _.isString(value) and not (value instanceof Color)
|
||||
|
||||
sortObject = (value) ->
|
||||
return value unless isPlainObject(value)
|
||||
result = {}
|
||||
for key in Object.keys(value).sort()
|
||||
result[key] = sortObject(value[key])
|
||||
result
|
||||
|
||||
withoutEmptyObjects = (object) ->
|
||||
resultObject = undefined
|
||||
if isPlainObject(object)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
_ = require 'underscore-plus'
|
||||
Model = require './model'
|
||||
|
||||
EmptyLineRegExp = /(\r\n[\t ]*\r\n)|(\n[\t ]*\n)/g
|
||||
|
||||
# Extended: The `Cursor` class represents the little blinking line identifying
|
||||
# where text can be inserted.
|
||||
#
|
||||
@@ -467,10 +469,13 @@ class Cursor extends Model
|
||||
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
|
||||
|
||||
beginningOfWordPosition = null
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) ->
|
||||
if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious
|
||||
beginningOfWordPosition = range.start
|
||||
if not beginningOfWordPosition?.isEqual(currentBufferPosition)
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, matchText, stop}) ->
|
||||
# Ignore 'empty line' matches between '\r' and '\n'
|
||||
return if matchText is '' and range.start.column isnt 0
|
||||
|
||||
if range.start.isLessThan(currentBufferPosition)
|
||||
if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious
|
||||
beginningOfWordPosition = range.start
|
||||
stop()
|
||||
|
||||
if beginningOfWordPosition?
|
||||
@@ -496,13 +501,12 @@ class Cursor extends Model
|
||||
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
|
||||
|
||||
endOfWordPosition = null
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) ->
|
||||
if allowNext
|
||||
if range.end.isGreaterThan(currentBufferPosition)
|
||||
endOfWordPosition = range.end
|
||||
stop()
|
||||
else
|
||||
if range.start.isLessThanOrEqual(currentBufferPosition)
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, matchText, stop}) ->
|
||||
# Ignore 'empty line' matches between '\r' and '\n'
|
||||
return if matchText is '' and range.start.column isnt 0
|
||||
|
||||
if range.end.isGreaterThan(currentBufferPosition)
|
||||
if allowNext or range.start.isLessThanOrEqual(currentBufferPosition)
|
||||
endOfWordPosition = range.end
|
||||
stop()
|
||||
|
||||
@@ -603,14 +607,14 @@ class Cursor extends Model
|
||||
# non-word characters in the regex. (default: true)
|
||||
#
|
||||
# Returns a {RegExp}.
|
||||
wordRegExp: ({includeNonWordCharacters}={}) ->
|
||||
includeNonWordCharacters ?= true
|
||||
nonWordCharacters = @config.get('editor.nonWordCharacters', scope: @getScopeDescriptor())
|
||||
segments = ["^[\t ]*$"]
|
||||
segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
if includeNonWordCharacters
|
||||
segments.push("[#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
new RegExp(segments.join("|"), "g")
|
||||
wordRegExp: (options) ->
|
||||
scope = @getScopeDescriptor()
|
||||
nonWordCharacters = _.escapeRegExp(@config.get('editor.nonWordCharacters', {scope}))
|
||||
|
||||
source = "^[\t ]*$|[^\\s#{nonWordCharacters}]+"
|
||||
if options?.includeNonWordCharacters ? true
|
||||
source += "|" + "[#{nonWordCharacters}]+"
|
||||
new RegExp(source, "g")
|
||||
|
||||
# Public: Get the RegExp used by the cursor to determine what a "subword" is.
|
||||
#
|
||||
@@ -666,10 +670,9 @@ class Cursor extends Model
|
||||
{row, column} = eof
|
||||
position = new Point(row, column - 1)
|
||||
|
||||
@editor.scanInBufferRange /^\n*$/g, scanRange, ({range, stop}) ->
|
||||
unless range.start.isEqual(start)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.scanInBufferRange EmptyLineRegExp, scanRange, ({range, stop}) ->
|
||||
position = range.start.traverse(Point(1, 0))
|
||||
stop() unless position.isEqual(start)
|
||||
position
|
||||
|
||||
getBeginningOfPreviousParagraphBufferPosition: ->
|
||||
@@ -679,8 +682,7 @@ class Cursor extends Model
|
||||
scanRange = [[row-1, column], [0, 0]]
|
||||
position = new Point(0, 0)
|
||||
zero = new Point(0, 0)
|
||||
@editor.backwardsScanInBufferRange /^\n*$/g, scanRange, ({range, stop}) ->
|
||||
unless range.start.isEqual(zero)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.backwardsScanInBufferRange EmptyLineRegExp, scanRange, ({range, stop}) ->
|
||||
position = range.start.traverse(Point(1, 0))
|
||||
stop() unless position.isEqual(start)
|
||||
position
|
||||
|
||||
@@ -39,6 +39,9 @@ class DeserializerManager
|
||||
delete @deserializers[deserializer.name] for deserializer in deserializers
|
||||
return
|
||||
|
||||
getDeserializerCount: ->
|
||||
Object.keys(@deserializers).length
|
||||
|
||||
# Public: Deserialize the state and params.
|
||||
#
|
||||
# * `state` The state {Object} to deserialize.
|
||||
|
||||
@@ -32,7 +32,7 @@ KeymapManager::loadUserKeymap = ->
|
||||
return unless fs.isFileSync(userKeymapPath)
|
||||
|
||||
try
|
||||
@loadKeymap(userKeymapPath, watch: true, suppressErrors: true)
|
||||
@loadKeymap(userKeymapPath, watch: true, suppressErrors: true, priority: 100)
|
||||
catch error
|
||||
if error.message.indexOf('Unable to watch path') > -1
|
||||
message = """
|
||||
|
||||
@@ -3,7 +3,7 @@ TokenIterator = require './token-iterator'
|
||||
|
||||
module.exports =
|
||||
class LinesYardstick
|
||||
constructor: (@model, @presenter, @lineNodesProvider, grammarRegistry) ->
|
||||
constructor: (@model, @lineNodesProvider, grammarRegistry) ->
|
||||
@tokenIterator = new TokenIterator({grammarRegistry})
|
||||
@rangeForMeasurement = document.createRange()
|
||||
@invalidateCache()
|
||||
@@ -11,14 +11,12 @@ class LinesYardstick
|
||||
invalidateCache: ->
|
||||
@pixelPositionsByLineIdAndColumn = {}
|
||||
|
||||
prepareScreenRowsForMeasurement: (screenRows) ->
|
||||
@presenter.setScreenRowsToMeasure(screenRows)
|
||||
@lineNodesProvider.updateSync(@presenter.getPreMeasurementState())
|
||||
measuredRowForPixelPosition: (pixelPosition) ->
|
||||
targetTop = pixelPosition.top
|
||||
row = Math.floor(targetTop / @model.getLineHeightInPixels())
|
||||
row if 0 <= row <= @model.getLastScreenRow()
|
||||
|
||||
clearScreenRowsForMeasurement: ->
|
||||
@presenter.clearScreenRowsToMeasure()
|
||||
|
||||
screenPositionForPixelPosition: (pixelPosition, measureVisibleLinesOnly) ->
|
||||
screenPositionForPixelPosition: (pixelPosition) ->
|
||||
targetTop = pixelPosition.top
|
||||
targetLeft = pixelPosition.left
|
||||
defaultCharWidth = @model.getDefaultCharWidth()
|
||||
@@ -28,12 +26,10 @@ class LinesYardstick
|
||||
row = Math.min(row, @model.getLastScreenRow())
|
||||
row = Math.max(0, row)
|
||||
|
||||
@prepareScreenRowsForMeasurement([row]) unless measureVisibleLinesOnly
|
||||
|
||||
line = @model.tokenizedLineForScreenRow(row)
|
||||
lineNode = @lineNodesProvider.lineNodeForLineIdAndScreenRow(line?.id, row)
|
||||
|
||||
return new Point(row, 0) unless lineNode? and line?
|
||||
return Point(row, 0) unless lineNode? and line?
|
||||
|
||||
textNodes = @lineNodesProvider.textNodesForLineIdAndScreenRow(line.id, row)
|
||||
column = 0
|
||||
@@ -70,33 +66,27 @@ class LinesYardstick
|
||||
left = @leftPixelPositionForCharInTextNode(lineNode, textNode, indexWithinTextNode)
|
||||
charWidth = left - previousLeft
|
||||
|
||||
return new Point(row, previousColumn) if targetLeft <= previousLeft + (charWidth / 2)
|
||||
return Point(row, previousColumn) if targetLeft <= previousLeft + (charWidth / 2)
|
||||
|
||||
previousLeft = left
|
||||
previousColumn = column
|
||||
column += charLength
|
||||
|
||||
@clearScreenRowsForMeasurement() unless measureVisibleLinesOnly
|
||||
|
||||
if targetLeft <= previousLeft + (charWidth / 2)
|
||||
new Point(row, previousColumn)
|
||||
Point(row, previousColumn)
|
||||
else
|
||||
new Point(row, column)
|
||||
Point(row, column)
|
||||
|
||||
pixelPositionForScreenPosition: (screenPosition, clip=true, measureVisibleLinesOnly) ->
|
||||
pixelPositionForScreenPosition: (screenPosition, clip=true) ->
|
||||
screenPosition = Point.fromObject(screenPosition)
|
||||
screenPosition = @model.clipScreenPosition(screenPosition) if clip
|
||||
|
||||
targetRow = screenPosition.row
|
||||
targetColumn = screenPosition.column
|
||||
|
||||
@prepareScreenRowsForMeasurement([targetRow]) unless measureVisibleLinesOnly
|
||||
|
||||
top = targetRow * @model.getLineHeightInPixels()
|
||||
left = @leftPixelPositionForScreenPosition(targetRow, targetColumn)
|
||||
|
||||
@clearScreenRowsForMeasurement() unless measureVisibleLinesOnly
|
||||
|
||||
{top, left}
|
||||
|
||||
leftPixelPositionForScreenPosition: (row, column) ->
|
||||
@@ -173,18 +163,3 @@ class LinesYardstick
|
||||
offset = lineNode.getBoundingClientRect().left
|
||||
|
||||
left + width - offset
|
||||
|
||||
pixelRectForScreenRange: (screenRange, measureVisibleLinesOnly) ->
|
||||
lineHeight = @model.getLineHeightInPixels()
|
||||
|
||||
if screenRange.end.row > screenRange.start.row
|
||||
top = @pixelPositionForScreenPosition(screenRange.start, true, measureVisibleLinesOnly).top
|
||||
left = 0
|
||||
height = (screenRange.end.row - screenRange.start.row + 1) * lineHeight
|
||||
width = @presenter.getScrollWidth()
|
||||
else
|
||||
{top, left} = @pixelPositionForScreenPosition(screenRange.start, false, measureVisibleLinesOnly)
|
||||
height = lineHeight
|
||||
width = @pixelPositionForScreenPosition(screenRange.end, false, measureVisibleLinesOnly).left - left
|
||||
|
||||
{top, left, width, height}
|
||||
|
||||
@@ -31,7 +31,8 @@ class PackageManager
|
||||
constructor: (params) ->
|
||||
{
|
||||
configDirPath, @devMode, safeMode, @resourcePath, @config, @styleManager,
|
||||
@notificationManager, @keymapManager, @commandRegistry, @grammarRegistry
|
||||
@notificationManager, @keymapManager, @commandRegistry, @grammarRegistry,
|
||||
@deserializerManager, @viewRegistry
|
||||
} = params
|
||||
|
||||
@emitter = new Emitter
|
||||
@@ -46,6 +47,7 @@ class PackageManager
|
||||
@packagesCache = require('../package.json')?._atomPackages ? {}
|
||||
@loadedPackages = {}
|
||||
@activePackages = {}
|
||||
@activatingPackages = {}
|
||||
@packageStates = {}
|
||||
@serviceHub = new ServiceHub
|
||||
|
||||
@@ -61,6 +63,7 @@ class PackageManager
|
||||
reset: ->
|
||||
@serviceHub.clear()
|
||||
@deactivatePackages()
|
||||
@loadedPackages = {}
|
||||
@packageStates = {}
|
||||
|
||||
###
|
||||
@@ -375,7 +378,8 @@ class PackageManager
|
||||
options = {
|
||||
path: packagePath, metadata, packageManager: this, @config, @styleManager,
|
||||
@commandRegistry, @keymapManager, @devMode, @notificationManager,
|
||||
@grammarRegistry, @themeManager, @menuManager, @contextMenuManager
|
||||
@grammarRegistry, @themeManager, @menuManager, @contextMenuManager,
|
||||
@deserializerManager, @viewRegistry
|
||||
}
|
||||
if metadata.theme
|
||||
pack = new ThemePackage(options)
|
||||
@@ -434,9 +438,12 @@ class PackageManager
|
||||
if pack = @getActivePackage(name)
|
||||
Promise.resolve(pack)
|
||||
else if pack = @loadPackage(name)
|
||||
@activatingPackages[pack.name] = pack
|
||||
pack.activate().then =>
|
||||
@activePackages[pack.name] = pack
|
||||
@emitter.emit 'did-activate-package', pack
|
||||
if @activatingPackages[pack.name]?
|
||||
delete @activatingPackages[pack.name]
|
||||
@activePackages[pack.name] = pack
|
||||
@emitter.emit 'did-activate-package', pack
|
||||
pack
|
||||
else
|
||||
Promise.reject(new Error("Failed to load package '#{name}'"))
|
||||
@@ -472,6 +479,7 @@ class PackageManager
|
||||
@setPackageState(pack.name, state) if state = pack.serialize?()
|
||||
pack.deactivate()
|
||||
delete @activePackages[pack.name]
|
||||
delete @activatingPackages[pack.name]
|
||||
@emitter.emit 'did-deactivate-package', pack
|
||||
|
||||
handleMetadataError: (error, packagePath) ->
|
||||
|
||||
@@ -33,7 +33,7 @@ class Package
|
||||
{
|
||||
@path, @metadata, @packageManager, @config, @styleManager, @commandRegistry,
|
||||
@keymapManager, @devMode, @notificationManager, @grammarRegistry, @themeManager,
|
||||
@menuManager, @contextMenuManager
|
||||
@menuManager, @contextMenuManager, @deserializerManager, @viewRegistry
|
||||
} = params
|
||||
|
||||
@emitter = new Emitter
|
||||
@@ -84,12 +84,24 @@ class Package
|
||||
@loadKeymaps()
|
||||
@loadMenus()
|
||||
@loadStylesheets()
|
||||
@loadDeserializers()
|
||||
@configSchemaRegisteredOnLoad = @registerConfigSchemaFromMetadata()
|
||||
@settingsPromise = @loadSettings()
|
||||
@requireMainModule() unless @mainModule? or @activationShouldBeDeferred()
|
||||
if @shouldRequireMainModuleOnLoad() and not @mainModule?
|
||||
@requireMainModule()
|
||||
catch error
|
||||
@handleError("Failed to load the #{@name} package", error)
|
||||
this
|
||||
|
||||
shouldRequireMainModuleOnLoad: ->
|
||||
not (
|
||||
@metadata.deserializers? or
|
||||
@metadata.viewProviders? or
|
||||
@metadata.configSchema? or
|
||||
@activationShouldBeDeferred() or
|
||||
localStorage.getItem(@getCanDeferMainModuleRequireStorageKey()) is 'true'
|
||||
)
|
||||
|
||||
reset: ->
|
||||
@stylesheets = []
|
||||
@keymaps = []
|
||||
@@ -117,9 +129,12 @@ class Package
|
||||
|
||||
activateNow: ->
|
||||
try
|
||||
@activateConfig()
|
||||
@requireMainModule() unless @mainModule?
|
||||
@configSchemaRegisteredOnActivate = @registerConfigSchemaFromMainModule()
|
||||
@registerViewProviders()
|
||||
@activateStylesheets()
|
||||
if @mainModule? and not @mainActivated
|
||||
@mainModule.activateConfig?()
|
||||
@mainModule.activate?(@packageManager.getPackageState(@name) ? {})
|
||||
@mainActivated = true
|
||||
@activateServices()
|
||||
@@ -128,15 +143,22 @@ class Package
|
||||
|
||||
@resolveActivationPromise?()
|
||||
|
||||
activateConfig: ->
|
||||
return if @configActivated
|
||||
registerConfigSchemaFromMetadata: ->
|
||||
if configSchema = @metadata.configSchema
|
||||
@config.setSchema @name, {type: 'object', properties: configSchema}
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
@requireMainModule() unless @mainModule?
|
||||
if @mainModule?
|
||||
registerConfigSchemaFromMainModule: ->
|
||||
if @mainModule? and not @configSchemaRegisteredOnLoad
|
||||
if @mainModule.config? and typeof @mainModule.config is 'object'
|
||||
@config.setSchema @name, {type: 'object', properties: @mainModule.config}
|
||||
@mainModule.activateConfig?()
|
||||
@configActivated = true
|
||||
return true
|
||||
false
|
||||
|
||||
# TODO: Remove. Settings view calls this method currently.
|
||||
activateConfig: -> @registerConfigSchemaFromMainModule()
|
||||
|
||||
activateStylesheets: ->
|
||||
return if @stylesheetsActivated
|
||||
@@ -253,6 +275,26 @@ class Package
|
||||
@stylesheets = @getStylesheetPaths().map (stylesheetPath) =>
|
||||
[stylesheetPath, @themeManager.loadStylesheet(stylesheetPath, true)]
|
||||
|
||||
loadDeserializers: ->
|
||||
if @metadata.deserializers?
|
||||
for name, implementationPath of @metadata.deserializers
|
||||
do =>
|
||||
deserializePath = path.join(@path, implementationPath)
|
||||
deserializeFunction = null
|
||||
atom.deserializers.add
|
||||
name: name,
|
||||
deserialize: =>
|
||||
@registerViewProviders()
|
||||
deserializeFunction ?= require(deserializePath)
|
||||
deserializeFunction.apply(this, arguments)
|
||||
return
|
||||
|
||||
registerViewProviders: ->
|
||||
if @metadata.viewProviders? and not @registeredViewProviders
|
||||
for implementationPath in @metadata.viewProviders
|
||||
@viewRegistry.addViewProvider(require(path.join(@path, implementationPath)))
|
||||
@registeredViewProviders = true
|
||||
|
||||
getStylesheetsPath: ->
|
||||
path.join(@path, 'styles')
|
||||
|
||||
@@ -343,21 +385,18 @@ class Package
|
||||
@activationPromise = null
|
||||
@resolveActivationPromise = null
|
||||
@activationCommandSubscriptions?.dispose()
|
||||
@configSchemaRegisteredOnActivate = false
|
||||
@deactivateResources()
|
||||
@deactivateConfig()
|
||||
@deactivateKeymaps()
|
||||
if @mainActivated
|
||||
try
|
||||
@mainModule?.deactivate?()
|
||||
@mainModule?.deactivateConfig?()
|
||||
@mainActivated = false
|
||||
catch e
|
||||
console.error "Error deactivating package '#{@name}'", e.stack
|
||||
@emitter.emit 'did-deactivate'
|
||||
|
||||
deactivateConfig: ->
|
||||
@mainModule?.deactivateConfig?()
|
||||
@configActivated = false
|
||||
|
||||
deactivateResources: ->
|
||||
grammar.deactivate() for grammar in @grammars
|
||||
settings.deactivate() for settings in @settings
|
||||
@@ -392,7 +431,13 @@ class Package
|
||||
mainModulePath = @getMainModulePath()
|
||||
if fs.isFileSync(mainModulePath)
|
||||
@mainModuleRequired = true
|
||||
|
||||
previousViewProviderCount = @viewRegistry.getViewProviderCount()
|
||||
previousDeserializerCount = @deserializerManager.getDeserializerCount()
|
||||
@mainModule = require(mainModulePath)
|
||||
if (@viewRegistry.getViewProviderCount() is previousViewProviderCount and
|
||||
@deserializerManager.getDeserializerCount() is previousDeserializerCount)
|
||||
localStorage.setItem(@getCanDeferMainModuleRequireStorageKey(), 'true')
|
||||
|
||||
getMainModulePath: ->
|
||||
return @mainModulePath if @resolvedMainModulePath
|
||||
@@ -586,6 +631,9 @@ class Package
|
||||
electronVersion = process.versions['electron'] ? process.versions['atom-shell']
|
||||
"installed-packages:#{@name}:#{@metadata.version}:electron-#{electronVersion}:incompatible-native-modules"
|
||||
|
||||
getCanDeferMainModuleRequireStorageKey: ->
|
||||
"installed-packages:#{@name}:#{@metadata.version}:can-defer-main-module-require"
|
||||
|
||||
# Get the incompatible native modules that this package depends on.
|
||||
# This recurses through all dependencies and requires all modules that
|
||||
# contain a `.node` file.
|
||||
|
||||
@@ -308,12 +308,20 @@ class Project extends Model
|
||||
findBufferForPath: (filePath) ->
|
||||
_.find @buffers, (buffer) -> buffer.getPath() is filePath
|
||||
|
||||
findBufferForId: (id) ->
|
||||
_.find @buffers, (buffer) -> buffer.getId() is id
|
||||
|
||||
# Only to be used in specs
|
||||
bufferForPathSync: (filePath) ->
|
||||
absoluteFilePath = @resolvePath(filePath)
|
||||
existingBuffer = @findBufferForPath(absoluteFilePath) if filePath
|
||||
existingBuffer ? @buildBufferSync(absoluteFilePath)
|
||||
|
||||
# Only to be used when deserializing
|
||||
bufferForIdSync: (id) ->
|
||||
existingBuffer = @findBufferForId(id) if id
|
||||
existingBuffer ? @buildBufferSync()
|
||||
|
||||
# Given a file path, this retrieves or creates a new {TextBuffer}.
|
||||
#
|
||||
# If the `filePath` already has a `buffer`, that value is used instead. Otherwise,
|
||||
@@ -329,9 +337,6 @@ class Project extends Model
|
||||
else
|
||||
@buildBuffer(absoluteFilePath)
|
||||
|
||||
bufferForId: (id) ->
|
||||
_.find @buffers, (buffer) -> buffer.id is id
|
||||
|
||||
# Still needed when deserializing a tokenized buffer
|
||||
buildBufferSync: (absoluteFilePath) ->
|
||||
buffer = new TextBuffer({filePath: absoluteFilePath})
|
||||
|
||||
@@ -55,7 +55,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) ->
|
||||
'window:log-deprecation-warnings': -> Grim.logDeprecations()
|
||||
'window:toggle-auto-indent': -> config.set("editor.autoIndent", not config.get("editor.autoIndent"))
|
||||
'pane:reopen-closed-item': -> @getModel().reopenItem()
|
||||
'core:close': -> @getModel().destroyActivePaneItemOrEmptyPane()
|
||||
'core:close': -> @getModel().closeActivePaneItemOrEmptyPaneOrWindow()
|
||||
'core:save': -> @getModel().saveActivePaneItem()
|
||||
'core:save-as': -> @getModel().saveActivePaneItemAs()
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class TextEditorComponent
|
||||
@linesComponent = new LinesComponent({@presenter, @hostElement, @useShadowDOM, @domElementPool, @assert, @grammars})
|
||||
@scrollViewNode.appendChild(@linesComponent.getDomNode())
|
||||
|
||||
@linesYardstick = new LinesYardstick(@editor, @presenter, @linesComponent, @grammars)
|
||||
@linesYardstick = new LinesYardstick(@editor, @linesComponent, @grammars)
|
||||
@presenter.setLinesYardstick(@linesYardstick)
|
||||
|
||||
@horizontalScrollbarComponent = new ScrollbarComponent({orientation: 'horizontal', onScroll: @onHorizontalScroll})
|
||||
@@ -127,8 +127,10 @@ class TextEditorComponent
|
||||
@domNode
|
||||
|
||||
updateSync: ->
|
||||
@updateSyncPreMeasurement()
|
||||
|
||||
@oldState ?= {}
|
||||
@newState = @presenter.getState()
|
||||
@newState = @presenter.getPostMeasurementState()
|
||||
|
||||
if @editor.getLastSelection()? and not @editor.getLastSelection().isEmpty()
|
||||
@domNode.classList.add('has-selection')
|
||||
@@ -170,6 +172,9 @@ class TextEditorComponent
|
||||
@updateParentViewFocusedClassIfNeeded()
|
||||
@updateParentViewMiniClass()
|
||||
|
||||
updateSyncPreMeasurement: ->
|
||||
@linesComponent.updateSync(@presenter.getPreMeasurementState())
|
||||
|
||||
readAfterUpdateSync: =>
|
||||
@overlayManager?.measureOverlays()
|
||||
|
||||
@@ -429,14 +434,42 @@ class TextEditorComponent
|
||||
getVisibleRowRange: ->
|
||||
@presenter.getVisibleRowRange()
|
||||
|
||||
pixelPositionForScreenPosition: ->
|
||||
@linesYardstick.pixelPositionForScreenPosition(arguments...)
|
||||
pixelPositionForScreenPosition: (screenPosition, clip) ->
|
||||
unless @presenter.isRowVisible(screenPosition.row)
|
||||
@presenter.setScreenRowsToMeasure([screenPosition.row])
|
||||
@updateSyncPreMeasurement()
|
||||
|
||||
screenPositionForPixelPosition: ->
|
||||
@linesYardstick.screenPositionForPixelPosition(arguments...)
|
||||
pixelPosition = @linesYardstick.pixelPositionForScreenPosition(screenPosition, clip)
|
||||
@presenter.clearScreenRowsToMeasure()
|
||||
pixelPosition
|
||||
|
||||
pixelRectForScreenRange: ->
|
||||
@linesYardstick.pixelRectForScreenRange(arguments...)
|
||||
screenPositionForPixelPosition: (pixelPosition) ->
|
||||
row = @linesYardstick.measuredRowForPixelPosition(pixelPosition)
|
||||
if row? and not @presenter.isRowVisible(row)
|
||||
@presenter.setScreenRowsToMeasure([row])
|
||||
@updateSyncPreMeasurement()
|
||||
|
||||
position = @linesYardstick.screenPositionForPixelPosition(pixelPosition)
|
||||
@presenter.clearScreenRowsToMeasure()
|
||||
position
|
||||
|
||||
pixelRectForScreenRange: (screenRange) ->
|
||||
rowsToMeasure = []
|
||||
unless @presenter.isRowVisible(screenRange.start.row)
|
||||
rowsToMeasure.push(screenRange.start.row)
|
||||
unless @presenter.isRowVisible(screenRange.end.row)
|
||||
rowsToMeasure.push(screenRange.end.row)
|
||||
|
||||
if rowsToMeasure.length > 0
|
||||
@presenter.setScreenRowsToMeasure(rowsToMeasure)
|
||||
@updateSyncPreMeasurement()
|
||||
|
||||
rect = @presenter.absolutePixelRectForScreenRange(screenRange)
|
||||
|
||||
if rowsToMeasure.length > 0
|
||||
@presenter.clearScreenRowsToMeasure()
|
||||
|
||||
rect
|
||||
|
||||
pixelRangeForScreenRange: (screenRange, clip=true) ->
|
||||
{start, end} = Range.fromObject(screenRange)
|
||||
|
||||
@@ -121,14 +121,6 @@ class TextEditorPresenter
|
||||
@updating = false
|
||||
|
||||
@resetTrackedUpdates()
|
||||
|
||||
# Public: Gets this presenter's state, updating it just in time before returning from this function.
|
||||
# Returns a state {Object}, useful for rendering to screen.
|
||||
getState: ->
|
||||
@linesYardstick.prepareScreenRowsForMeasurement()
|
||||
|
||||
@getPostMeasurementState()
|
||||
|
||||
@state
|
||||
|
||||
resetTrackedUpdates: ->
|
||||
@@ -377,7 +369,8 @@ class TextEditorPresenter
|
||||
endRow = @constrainRow(@getEndTileRow() + @tileSize)
|
||||
|
||||
screenRows = [startRow...endRow]
|
||||
if longestScreenRow = @model.getLongestScreenRow()
|
||||
longestScreenRow = @model.getLongestScreenRow()
|
||||
if longestScreenRow?
|
||||
screenRows.push(longestScreenRow)
|
||||
if @screenRowsToMeasure?
|
||||
screenRows.push(@screenRowsToMeasure...)
|
||||
@@ -1154,16 +1147,29 @@ class TextEditorPresenter
|
||||
hasOverlayPositionRequirements: ->
|
||||
@hasPixelRectRequirements() and @boundingClientRect? and @windowWidth and @windowHeight
|
||||
|
||||
absolutePixelRectForScreenRange: (screenRange) ->
|
||||
lineHeight = @model.getLineHeightInPixels()
|
||||
|
||||
if screenRange.end.row > screenRange.start.row
|
||||
top = @linesYardstick.pixelPositionForScreenPosition(screenRange.start, true).top
|
||||
left = 0
|
||||
height = (screenRange.end.row - screenRange.start.row + 1) * lineHeight
|
||||
width = @getScrollWidth()
|
||||
else
|
||||
{top, left} = @linesYardstick.pixelPositionForScreenPosition(screenRange.start, false)
|
||||
height = lineHeight
|
||||
width = @linesYardstick.pixelPositionForScreenPosition(screenRange.end, false).left - left
|
||||
|
||||
{top, left, width, height}
|
||||
|
||||
pixelRectForScreenRange: (screenRange) ->
|
||||
rect = @linesYardstick.pixelRectForScreenRange(screenRange, true)
|
||||
rect = @absolutePixelRectForScreenRange(screenRange)
|
||||
rect.top -= @getScrollTop()
|
||||
rect.left -= @getScrollLeft()
|
||||
|
||||
rect.top = Math.round(rect.top)
|
||||
rect.left = Math.round(rect.left)
|
||||
rect.width = Math.round(rect.width)
|
||||
rect.height = Math.round(rect.height)
|
||||
|
||||
rect
|
||||
|
||||
fetchDecorations: ->
|
||||
@@ -1549,3 +1555,6 @@ class TextEditorPresenter
|
||||
|
||||
getVisibleRowRange: ->
|
||||
[@startRow, @endRow]
|
||||
|
||||
isRowVisible: (row) ->
|
||||
@startRow <= row < @endRow
|
||||
|
||||
@@ -581,10 +581,7 @@ class TextEditor extends Model
|
||||
#
|
||||
# Returns a {String}.
|
||||
getTitle: ->
|
||||
if sessionPath = @getPath()
|
||||
path.basename(sessionPath)
|
||||
else
|
||||
'untitled'
|
||||
@getFileName() ? 'untitled'
|
||||
|
||||
# Essential: Get unique title for display in other parts of the UI, such as
|
||||
# the window title.
|
||||
@@ -593,41 +590,52 @@ class TextEditor extends Model
|
||||
# If the editor's buffer is saved, its unique title is formatted as one
|
||||
# of the following,
|
||||
# * "<filename>" when it is the only editing buffer with this file name.
|
||||
# * "<unique-dir-prefix>/.../<filename>", where the "..." may be omitted
|
||||
# if the the direct parent directory is already different.
|
||||
# * "<filename> — <unique-dir-prefix>" when other buffers have this file name.
|
||||
#
|
||||
# Returns a {String}
|
||||
getLongTitle: ->
|
||||
if sessionPath = @getPath()
|
||||
title = @getTitle()
|
||||
if @getPath()
|
||||
fileName = @getFileName()
|
||||
|
||||
# find text editors with identical file name.
|
||||
paths = []
|
||||
allPathSegments = []
|
||||
for textEditor in atom.workspace.getTextEditors() when textEditor isnt this
|
||||
if textEditor.getTitle() is title
|
||||
paths.push(textEditor.getPath())
|
||||
if paths.length is 0
|
||||
return title
|
||||
fileName = path.basename(sessionPath)
|
||||
if textEditor.getFileName() is fileName
|
||||
allPathSegments.push(textEditor.getDirectoryPath().split(path.sep))
|
||||
|
||||
# find the first directory in all these paths that is unique
|
||||
nLevel = 0
|
||||
while (_.some(paths, (apath) -> path.basename(apath) is path.basename(sessionPath)))
|
||||
sessionPath = path.dirname(sessionPath)
|
||||
paths = _.map(paths, (apath) -> path.dirname(apath))
|
||||
nLevel += 1
|
||||
if allPathSegments.length is 0
|
||||
return fileName
|
||||
|
||||
directory = path.basename sessionPath
|
||||
if nLevel > 1
|
||||
path.join(directory, "...", fileName)
|
||||
else
|
||||
path.join(directory, fileName)
|
||||
ourPathSegments = @getDirectoryPath().split(path.sep)
|
||||
allPathSegments.push ourPathSegments
|
||||
|
||||
loop
|
||||
firstSegment = ourPathSegments[0]
|
||||
|
||||
commonBase = _.all(allPathSegments, (pathSegments) -> pathSegments.length > 1 and pathSegments[0] is firstSegment)
|
||||
if commonBase
|
||||
pathSegments.shift() for pathSegments in allPathSegments
|
||||
else
|
||||
break
|
||||
|
||||
"#{fileName} \u2014 #{path.join(pathSegments...)}"
|
||||
else
|
||||
'untitled'
|
||||
|
||||
# Essential: Returns the {String} path of this editor's text buffer.
|
||||
getPath: -> @buffer.getPath()
|
||||
|
||||
getFileName: ->
|
||||
if fullPath = @getPath()
|
||||
path.basename(fullPath)
|
||||
else
|
||||
null
|
||||
|
||||
getDirectoryPath: ->
|
||||
if fullPath = @getPath()
|
||||
path.dirname(fullPath)
|
||||
else
|
||||
null
|
||||
|
||||
# Extended: Returns the {String} character set encoding of this editor's text
|
||||
# buffer.
|
||||
getEncoding: -> @buffer.getEncoding()
|
||||
@@ -669,7 +677,7 @@ class TextEditor extends Model
|
||||
# this editor.
|
||||
shouldPromptToSave: ({windowCloseRequested}={}) ->
|
||||
if windowCloseRequested
|
||||
@isModified()
|
||||
false
|
||||
else
|
||||
@isModified() and not @buffer.hasMultipleEditors()
|
||||
|
||||
@@ -678,16 +686,16 @@ class TextEditor extends Model
|
||||
getSaveDialogOptions: -> {}
|
||||
|
||||
checkoutHeadRevision: ->
|
||||
if filePath = this.getPath()
|
||||
if @getPath()
|
||||
checkoutHead = =>
|
||||
@project.repositoryForDirectory(new Directory(path.dirname(filePath)))
|
||||
@project.repositoryForDirectory(new Directory(@getDirectoryPath()))
|
||||
.then (repository) =>
|
||||
repository?.checkoutHeadForEditor(this)
|
||||
|
||||
if @config.get('editor.confirmCheckoutHeadRevision')
|
||||
@applicationDelegate.confirm
|
||||
message: 'Confirm Checkout HEAD Revision'
|
||||
detailedMessage: "Are you sure you want to discard all changes to \"#{path.basename(filePath)}\" since the last Git commit?"
|
||||
detailedMessage: "Are you sure you want to discard all changes to \"#{@getFileName()}\" since the last Git commit?"
|
||||
buttons:
|
||||
OK: checkoutHead
|
||||
Cancel: null
|
||||
|
||||
@@ -14,6 +14,7 @@ class ThemePackage extends Package
|
||||
|
||||
load: ->
|
||||
@loadTime = 0
|
||||
@configSchemaRegisteredOnLoad = @registerConfigSchemaFromMetadata()
|
||||
this
|
||||
|
||||
activate: ->
|
||||
|
||||
@@ -22,7 +22,11 @@ class TokenizedBuffer extends Model
|
||||
changeCount: 0
|
||||
|
||||
@deserialize: (state, atomEnvironment) ->
|
||||
state.buffer = atomEnvironment.project.bufferForPathSync(state.bufferPath)
|
||||
if state.bufferId
|
||||
state.buffer = atomEnvironment.project.bufferForIdSync(state.bufferId)
|
||||
else
|
||||
# TODO: remove this fallback after everyone transitions to the latest version.
|
||||
state.buffer = atomEnvironment.project.bufferForPathSync(state.bufferPath)
|
||||
state.config = atomEnvironment.config
|
||||
state.grammarRegistry = atomEnvironment.grammars
|
||||
state.packageManager = atomEnvironment.packages
|
||||
@@ -53,6 +57,7 @@ class TokenizedBuffer extends Model
|
||||
serialize: ->
|
||||
deserializer: 'TokenizedBuffer'
|
||||
bufferPath: @buffer.getPath()
|
||||
bufferId: @buffer.getId()
|
||||
tabLength: @tabLength
|
||||
ignoreInvisibles: @ignoreInvisibles
|
||||
largeFileMode: @largeFileMode
|
||||
|
||||
@@ -3,6 +3,8 @@ Grim = require 'grim'
|
||||
{Disposable} = require 'event-kit'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
AnyConstructor = Symbol('any-constructor')
|
||||
|
||||
# Essential: `ViewRegistry` handles the association between model and view
|
||||
# types in Atom. We call this association a View Provider. As in, for a given
|
||||
# model, this class can provide a view via {::getView}, as long as the
|
||||
@@ -76,16 +78,27 @@ class ViewRegistry
|
||||
# textEditorElement
|
||||
# ```
|
||||
#
|
||||
# * `modelConstructor` Constructor {Function} for your model.
|
||||
# * `modelConstructor` (optional) Constructor {Function} for your model. If
|
||||
# a constructor is given, the `createView` function will only be used
|
||||
# for model objects inheriting from that constructor. Otherwise, it will
|
||||
# will be called for any object.
|
||||
# * `createView` Factory {Function} that is passed an instance of your model
|
||||
# and must return a subclass of `HTMLElement` or `undefined`.
|
||||
# and must return a subclass of `HTMLElement` or `undefined`. If it returns
|
||||
# `undefined`, then the registry will continue to search for other view
|
||||
# providers.
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to remove the
|
||||
# added provider.
|
||||
addViewProvider: (modelConstructor, createView) ->
|
||||
if arguments.length is 1
|
||||
Grim.deprecate("atom.views.addViewProvider now takes 2 arguments: a model constructor and a createView function. See docs for details.")
|
||||
provider = modelConstructor
|
||||
switch typeof modelConstructor
|
||||
when 'function'
|
||||
provider = {createView: modelConstructor, modelConstructor: AnyConstructor}
|
||||
when 'object'
|
||||
Grim.deprecate("atom.views.addViewProvider now takes 2 arguments: a model constructor and a createView function. See docs for details.")
|
||||
provider = modelConstructor
|
||||
else
|
||||
throw new TypeError("Arguments to addViewProvider must be functions")
|
||||
else
|
||||
provider = {modelConstructor, createView}
|
||||
|
||||
@@ -93,6 +106,9 @@ class ViewRegistry
|
||||
new Disposable =>
|
||||
@providers = @providers.filter (p) -> p isnt provider
|
||||
|
||||
getViewProviderCount: ->
|
||||
@providers.length
|
||||
|
||||
# Essential: Get the view associated with an object in the workspace.
|
||||
#
|
||||
# If you're just *using* the workspace, you shouldn't need to access the view
|
||||
@@ -153,25 +169,34 @@ class ViewRegistry
|
||||
|
||||
createView: (object) ->
|
||||
if object instanceof HTMLElement
|
||||
object
|
||||
else if object?.element instanceof HTMLElement
|
||||
object.element
|
||||
else if object?.jquery
|
||||
object[0]
|
||||
else if provider = @findProvider(object)
|
||||
element = provider.createView?(object, @atomEnvironment)
|
||||
unless element?
|
||||
element = new provider.viewConstructor
|
||||
element.initialize?(object) ? element.setModel?(object)
|
||||
element
|
||||
else if viewConstructor = object?.getViewClass?()
|
||||
view = new viewConstructor(object)
|
||||
view[0]
|
||||
else
|
||||
throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.")
|
||||
return object
|
||||
|
||||
findProvider: (object) ->
|
||||
find @providers, ({modelConstructor}) -> object instanceof modelConstructor
|
||||
if object?.element instanceof HTMLElement
|
||||
return object.element
|
||||
|
||||
if object?.jquery
|
||||
return object[0]
|
||||
|
||||
for provider in @providers
|
||||
if provider.modelConstructor is AnyConstructor
|
||||
if element = provider.createView(object, @atomEnvironment)
|
||||
return element
|
||||
continue
|
||||
|
||||
if object instanceof provider.modelConstructor
|
||||
if element = provider.createView?(object, @atomEnvironment)
|
||||
return element
|
||||
|
||||
if viewConstructor = provider.viewConstructor
|
||||
element = new viewConstructor
|
||||
element.initialize?(object) ? element.setModel?(object)
|
||||
return element
|
||||
|
||||
if viewConstructor = object?.getViewClass?()
|
||||
view = new viewConstructor(object)
|
||||
return view[0]
|
||||
|
||||
throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.")
|
||||
|
||||
updateDocument: (fn) ->
|
||||
@documentWriters.push(fn)
|
||||
|
||||
@@ -42,9 +42,8 @@ class WindowEventHandler
|
||||
# `.native-key-bindings` class.
|
||||
handleNativeKeybindings: ->
|
||||
bindCommandToAction = (command, action) =>
|
||||
@addEventListener @document, command, (event) =>
|
||||
if event.target.webkitMatchesSelector('.native-key-bindings')
|
||||
@applicationDelegate.getCurrentWindow().webContents[action]()
|
||||
@subscriptions.add @atomEnvironment.commands.add '.native-key-bindings', command, (event) =>
|
||||
@applicationDelegate.getCurrentWindow().webContents[action]()
|
||||
|
||||
bindCommandToAction('core:copy', 'copy')
|
||||
bindCommandToAction('core:paste', 'paste')
|
||||
|
||||
@@ -155,21 +155,28 @@ class Workspace extends Model
|
||||
projectPaths = @project.getPaths() ? []
|
||||
if item = @getActivePaneItem()
|
||||
itemPath = item.getPath?()
|
||||
itemTitle = item.getTitle?()
|
||||
itemTitle = item.getLongTitle?() ? item.getTitle?()
|
||||
projectPath = _.find projectPaths, (projectPath) ->
|
||||
itemPath is projectPath or itemPath?.startsWith(projectPath + path.sep)
|
||||
itemTitle ?= "untitled"
|
||||
projectPath ?= projectPaths[0]
|
||||
|
||||
titleParts = []
|
||||
if item? and projectPath?
|
||||
document.title = "#{itemTitle} - #{projectPath} - #{appName}"
|
||||
@applicationDelegate.setRepresentedFilename(itemPath ? projectPath)
|
||||
titleParts.push itemTitle, projectPath
|
||||
representedPath = itemPath ? projectPath
|
||||
else if projectPath?
|
||||
document.title = "#{projectPath} - #{appName}"
|
||||
@applicationDelegate.setRepresentedFilename(projectPath)
|
||||
titleParts.push projectPath
|
||||
representedPath = projectPath
|
||||
else
|
||||
document.title = "#{itemTitle} - #{appName}"
|
||||
@applicationDelegate.setRepresentedFilename("")
|
||||
titleParts.push itemTitle
|
||||
representedPath = ""
|
||||
|
||||
unless process.platform is 'darwin'
|
||||
titleParts.push appName
|
||||
|
||||
document.title = titleParts.join(" \u2014 ")
|
||||
@applicationDelegate.setRepresentedFilename(representedPath)
|
||||
|
||||
# On OS X, fades the application window's proxy icon when the current file
|
||||
# has been modified.
|
||||
@@ -681,9 +688,15 @@ class Workspace extends Model
|
||||
destroyActivePane: ->
|
||||
@getActivePane()?.destroy()
|
||||
|
||||
# Destroy the active pane item or the active pane if it is empty.
|
||||
destroyActivePaneItemOrEmptyPane: ->
|
||||
if @getActivePaneItem()? then @destroyActivePaneItem() else @destroyActivePane()
|
||||
# Close the active pane item, or the active pane if it is empty,
|
||||
# or the current window if there is only the empty root pane.
|
||||
closeActivePaneItemOrEmptyPaneOrWindow: ->
|
||||
if @getActivePaneItem()?
|
||||
@destroyActivePaneItem()
|
||||
else if @getPanes().length > 1
|
||||
@destroyActivePane()
|
||||
else if @config.get('core.closeEmptyWindows')
|
||||
atom.close()
|
||||
|
||||
# Increase the editor font size by 1px.
|
||||
increaseFontSize: ->
|
||||
|
||||
@@ -31,12 +31,18 @@
|
||||
font-size: @font-size - 2px;
|
||||
height: auto;
|
||||
line-height: 1.3em;
|
||||
&.icon:before {
|
||||
font-size: @font-size - 2px;
|
||||
}
|
||||
}
|
||||
.btn.btn-sm,
|
||||
.btn-group-sm > .btn {
|
||||
padding: @component-padding/4 @component-padding/2;
|
||||
height: auto;
|
||||
line-height: 1.3em;
|
||||
&.icon:before {
|
||||
font-size: @font-size + 1px;
|
||||
}
|
||||
}
|
||||
.btn.btn-lg,
|
||||
.btn-group-lg > .btn {
|
||||
@@ -44,6 +50,9 @@
|
||||
padding: @component-padding - 2px @component-padding + 2px;
|
||||
height: auto;
|
||||
line-height: 1.3em;
|
||||
&.icon:before {
|
||||
font-size: @font-size + 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group > .btn {
|
||||
@@ -63,6 +72,18 @@
|
||||
border-bottom-right-radius: @component-border-radius;
|
||||
}
|
||||
|
||||
// Icon buttons
|
||||
.btn.icon {
|
||||
&:before {
|
||||
width: initial;
|
||||
height: initial;
|
||||
margin-right: .3125em;
|
||||
}
|
||||
&:empty:before {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-toolbar {
|
||||
> .btn-group + .btn-group, > .btn-group + .btn, > .btn + .btn {
|
||||
float: none;
|
||||
|
||||
Reference in New Issue
Block a user