Merge remote-tracking branch 'origin/master' into zelon-master

Conflicts:
	docs/build-instructions/windows.md
This commit is contained in:
probablycorey
2014-05-22 09:29:53 -07:00
21 changed files with 349 additions and 268 deletions

View File

@@ -1,63 +1,74 @@
# :tada: Contributing to Atom :tada:
These are just guidelines, not rules, use your best judgement and feel free
to propose changes to this document in a pull request.
The following is a set of guidelines for contributing to Atom and its packages,
which are hosted in the [Atom Organization](https://github.com/atom) on GitHub.
If you're unsure which package is causing your problem or if you're having an
issue with Atom core, please use the feedback form in the application or email
[atom@github.com](mailto:atom@github.com). These are just guidelines, not rules,
use your best judgement and feel free to propose changes to this document in a
pull request.
## Issues
* Include screenshots and animated GIFs whenever possible, they are immensely
helpful.
* Include the behavior you expected to happen and other places you've seen
that behavior such as Emacs, vi, Xcode, etc.
* Check the Console app for stack traces to include if reporting a crash.
* Check the Dev tools (`alt-cmd-i`) for errors and stack traces to include.
## Submitting Issues
* Include screenshots and animated GIFs whenever possible; they are immensely
helpful.
* Include the behavior you expected and other places you've seen that behavior
such as Emacs, vi, Xcode, etc.
* Check the dev tools (`alt-cmd-i`) for errors and stack traces to include.
* On Mac, check Console.app for stack traces to include if reporting a crash.
* Perform a cursory search to see if a similar issue has already been submitted.
### Package Repositories
This is the repository for the core Atom editor only. Atom comes bundled with
many packages and themes that are stored in other repos under the
[atom organization](https://github.com/atom) such as [tabs](https://github.com/atom/tabs),
[Atom organization](https://github.com/atom) such as
[tabs](https://github.com/atom/tabs),
[find-and-replace](https://github.com/atom/find-and-replace),
[language-javascript](https://github.com/atom/language-javascript),
and [atom-light-ui](http://github.com/atom/atom-light-ui).
[language-javascript](https://github.com/atom/language-javascript), and
[atom-light-ui](http://github.com/atom/atom-light-ui).
If you think you know which package is causing the issue you are reporting, feel
free to open up the issue in that specific repository instead. When in doubt
just open the issue here but be aware that it may get closed here and reopened
in the proper package's repository.
For more information on how to work with Atom's official packages, see
[Contributing to Atom Packages](https://atom.io/docs/latest/contributing-to-packages.html)
## Pull Requests
* Include screenshots and animated GIFs whenever possible.
* Follow the [CoffeeScript](#coffeescript-styleguide),
[JavaScript](https://github.com/styleguide/javascript),
and [CSS](https://github.com/styleguide/css) styleguides
* Include thoughtfully worded [Jasmine](http://jasmine.github.io/)
specs
* Avoid placing files in `vendor`. 3rd-party packages should be added as a
`package.json` dependency.
* Files end with a newline.
* Requires should be in the following order:
* Include screenshots and animated GIFs in your pull request whenever possible.
* Follow the [CoffeeScript](#coffeescript-styleguide),
[JavaScript](https://github.com/styleguide/javascript),
and [CSS](https://github.com/styleguide/css) styleguides.
* Include thoughtfully-worded, well-structured
[Jasmine](http://jasmine.github.io/) specs.
* Document new code based on the
[Documentation Styleguide](#documentation-styleguide)
* End files with a newline.
* Place requires in the following order:
* Built in Node Modules (such as `path`)
* Built in Atom and Atom Shell Modules (such as `atom`, `shell`)
* Local Modules (using relative paths)
* Class variables and methods should be in the following order:
* Class methods (methods starting with a `@`)
* Instance methods
* Beware of platform differences
* Place class properties in the following order:
* Class methods and properties (methods starting with a `@`)
* Instance methods and properties
* Avoid platform-dependent code:
* Use `require('atom').fs.getHomeDirectory()` to get the home directory.
* Use `path.join()` to concatenate filenames.
* Use `os.tmpdir()` instead of `/tmp
* Use `os.tmpdir()` rather than `/tmp` when you need to reference the
temporary directory.
## Git Commit Messages
* Use the present tense
* Reference issues and pull requests liberally
* Consider starting the commit message with an applicable emoji:
* Use the present tense ("Add feature" not "Added feature")
* 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
* Consider starting the commit message with an applicable emoji:
* :lipstick: `:lipstick:` when improving the format/structure of the code
* :racehorse: `:racehorse:` when improving performance
* :non-potable_water: `:non-potable_water:` when plugging memory leaks
* :memo: `:memo:` when writing docs
* :penguin: `:penguin:` when fixing something on Linux
* :apple: `:apple:` when fixing something on Mac OS
* :bug: `:bug:` when fixing a bug
* :bug: `:bug:` when fixing a bug
* :fire: `:fire:` when removing code or files
* :green_heart: `:green_heart:` when fixing the CI build
* :white_check_mark: `:white_check_mark:` when adding tests
@@ -66,17 +77,24 @@ in the proper package's repository.
## CoffeeScript Styleguide
* Set parameter defaults without spaces around the equal sign
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`
* Use parentheses if it improves code clarity.
* Prefer alphabetic keywords to symbolic keywords:
* `a is b` instead of `a == b`
* Avoid spaces inside the curly-braces of hash literals:
* `{a: 1, b: 2}` instead of `{ a: 1, b: 2 }`
* Include a single line of whitespace between methods.
## Documentation Styleguide
* Use [TomDoc](http://tomdoc.org).
* Use [Markdown](https://daringfireball.net/projects/markdown).
* Reference classes with `{ClassName}`.
* Reference instance methods with `{ClassName::methodName}`.
* Reference class methods with `{ClassName.methodName}`.
* Delegate to comments elsewhere with `{Delegates to: ClassName.methodName}`
style notation.
* Reference methods and classes in markdown with the custom `{}` notation:
* Reference classes with `{ClassName}`
* Reference instance methods with `{ClassName::methodName}`
* Reference class methods with `{ClassName.methodName}`
* Delegate to comments elsewhere with `{Delegates to: ClassName.methodName}`
style notation.
### Example

View File

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

View File

@@ -4,39 +4,32 @@
* Windows 7 or later
* [Visual C++ 2010 Express](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_4) with [SP1](http://www.microsoft.com/en-us/download/details.aspx?id=23691)
* [node.js - 32bit](http://nodejs.org/download/) v0.10.x
* [Python 2.7.x](http://www.python.org/download/)
* [node.js](http://nodejs.org/download/) v0.10.x
* [Python](http://www.python.org/download/) v2.7.x
* [GitHub for Windows](http://windows.github.com/)
to your PATH
* Open the Windows GitHub shell (NOT the Standard PowerShell, the shortcut labeled 'Git Shell' - make sure you have logged in at least once to the GitHub for Windows GUI App)
* `$env:Path = $env:Path + ";C:\path\to\atom\repo\node_modules"`
## Instructions
```bat
# Use the `Git Shell` app which was installed by GitHub for Windows. Also Make
# sure you have logged into the GitHub for Windows GUI App.
cd C:\
git clone https://github.com/atom/atom
git clone https://github.com/atom/atom/
cd atom
script\build
```
## Why do I have to use GitHub for Windows? Can't I just use my existing Git?
## Why do I have to use GitHub for Windows?
You totally can! GitHub for Windows's Git Shell just takes less work to set up. You need to have Posix tools in your `%PATH%` (i.e. `grep`, `sed`, et al.), which isn't the default configuration when you install Git. To fix this, you probably need to fiddle with your system PATH.
You don't, You can use your existing Git! GitHub for Windows's Git Shell is just
easier to set up. You need to have Posix tools in your `%PATH%` (i.e. `grep`,
`sed`, et al.), which isn't the default configuration when you install Git. To
fix this, you probably need to fiddle with your system PATH.
## Troubleshooting
Some of the most common errors include:
### Common Errors
* `node is not recognized`
gyp WARN install got an error, rolling back install
and
>> The system cannot find the path specified.
These two error messages can usually be ignored. The solution to these errors is to re-run `script\build`, possibly several times.
If your Visual Studio is in a non-standard location, and you get the error `You must have Visual Studio 2010 or 2012 installed`, you need to modify `apm\node_modules\atom-package-manager\lib\config.js` around line 110 and replace the variable with your Visual Studio directory plus Common7/IDE. Make sure your path does not contain unescaped backward slashes that appear in normal Windows paths.
Example:
vs2010Path = "H:/VS2010/Common7/IDE"
* If you just installed node you need to restart your computer before node is
available on your Path.

View File

@@ -0,0 +1,52 @@
# Contributing to Official Atom Packages
If you think you know which package is causing the issue you are reporting, feel
free to open up the issue in that specific repository instead. When in doubt
just open the issue here but be aware that it may get closed here and reopened
in the proper package's repository.
## Hacking on Packages
### Cloning
The first step is creating your own clone. You can of course do this manually
with git, or you can use the `apm develop` command to create a clone based on
the package's `repository` field in the `package.json`.
For example, if you want to make changes to the `tree-view` package, run the
following command:
```
> apm develop tree-view
Cloning https://github.com/atom/tree-view ✓
Installing modules ✓
~/.atom/dev/packages/tree-view -> ~/github/tree-view
```
This clones the `tree-view` repository to `~/github`. If you prefer a different
path, specify it via the `ATOM_REPOS_HOME` environment variable.
### Running in Development Mode
Editing a package in Atom is a bit of a circular experience: you're using Atom
to modify itself. What happens if you temporarily break something? You don't
want the version of Atom you're using to edit to become useless in the process.
For this reason, you'll only want to load packages in **development mode** while
you are working on them. You'll perform your editing in **stable mode**, only
switching to development mode to test your changes.
To open a development mode window, use the "Application: Open Dev" command,
which is normally bound to `cmd-shift-o`. You can also run dev mode from the
command line with `atom --dev`.
To load your package in development mode, create a symlink to it in
`~/.atom/dev/packages`. This occurs automatically when you clone the package
with `apm develop`. You can also run `apm link --dev` and `apm unlink --dev`
from the package directory to create and remove dev-mode symlinks.
### Installing Dependencies
Finally, you need to install the cloned package's dependencies by running
`apm install` within the package directory. This step is also performed
automatically the first time you run `apm develop`, but you'll want to keep
dependencies up to date by running `apm update` after pulling upstream changes.

View File

@@ -1,142 +0,0 @@
# Contributing to Atom Packages
The following is a set of guidelines for contributing to Atom packages, which
are hosted in the [Atom Organization](https://github.com/atom) on GitHub. If
you're unsure which package is causing your problem or if you're having an issue
with Atom core, please use the feedback form in the application or email
[atom@github.com](mailto:atom@github.com).
## Submitting Issues
* Include screenshots and animated GIFs whenever possible; they are immensely
helpful.
* Include the behavior you expected and other places you've seen that behavior
such as Emacs, vi, Xcode, etc.
* Check the dev tools (`alt-cmd-i`) for errors and stack traces to include.
* Check Console.app for stack traces to include if reporting a crash.
* Perform a cursory search to see if a similar issue has already been submitted.
## Hacking on Packages
### Cloning
The first step is creating your own clone. You can of course do this manually
with git, or you can use the `apm develop` command to create a clone based on
the package's `repository` field in the `package.json`.
For example, if you want to make changes to the `tree-view` package, run the
following command:
```
> apm develop tree-view
Cloning https://github.com/atom/tree-view ✓
Installing modules ✓
~/.atom/dev/packages/tree-view -> ~/github/tree-view
```
This clones the `tree-view` repository to `~/github`. If you prefer a different
path, specify it via the `ATOM_REPOS_HOME` environment variable.
### Running in Development Mode
Editing a package in Atom is a bit of a circular experience: you're using Atom
to modify itself. What happens if you temporarily break something? You don't
want the version of Atom you're using to edit to become useless in the process.
For this reason, you'll only want to load packages in **development mode** while
you are working on them. You'll perform your editing in **stable mode**, only
switching to development mode to test your changes.
To open a development mode window, use the "Application: Open Dev" command,
which is normally bound to `cmd-shift-o`. You can also run dev mode from the
command line with `atom --dev`.
To load your package in development mode, create a symlink to it in
`~/.atom/dev/packages`. This occurs automatically when you clone the package
with `apm develop`. You can also run `apm link --dev` and `apm unlink --dev`
from the package directory to create and remove dev-mode symlinks.
### Installing Dependencies
Finally, you need to install the cloned package's dependencies by running
`apm install` within the package directory. This step is also performed
automatically the first time you run `apm develop`, but you'll want to keep
dependencies up to date by running `apm update` after pulling upstream changes.
## Submitting Pull Requests
### Code Guidelines
* Include screenshots and animated GIFs in your pull request whenever possible.
* Follow the [CoffeeScript](#coffeescript-styleguide),
[JavaScript](https://github.com/styleguide/javascript),
and [CSS](https://github.com/styleguide/css) styleguides.
* Include thoughtfully-worded, well-structured
[Jasmine](http://pivotal.github.com/jasmine) specs.
* Document new code based on the
[Documentation Styleguide](#documentation-styleguide)
* End files with a newline.
* Place requires in the following order:
* Built in Node Modules (such as `path`)
* Built in Atom and Atom Shell Modules (such as `atom`, `shell`)
* Local Modules (using relative paths)
* Place class properties in the following order:
* Class methods and properties (methods starting with a `@`)
* Instance methods and properties
* Avoid platform-dependent code:
* Use `require('atom').fs.getHomeDirectory()` to get the home directory.
* Use `path.join()` to concatenate filenames.
* Use `os.tmpdir()` rather than `/tmp` when you need to reference the
temporary directory.
### Commit Message Guidelines
* Use the present tense ("Add feature" not "Added feature")
* 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
* Consider starting the commit message with an applicable emoji:
* :lipstick: when improving the format/structure of the code
* :racehorse: when improving performance
* :non-potable_water: when plugging memory leaks
* :memo: when writing docs
* :bulb: Check out the [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com)
for more ideas.
## CoffeeScript Styleguide
* Use parentheses if it improves code clarity.
* Prefer alphabetic keywords to symbolic keywords:
* `a is b` instead of `a == b`
* Avoid spaces inside the curly-braces of hash literals:
* `{a: 1, b: 2}` instead of `{ a: 1, b: 2 }`
* Set parameter defaults without spaces around the equal sign:
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`
* Include a single line of whitespace between methods.
## Documentation Styleguide
* Use [TomDoc](http://tomdoc.org).
* Use [Markdown](https://daringfireball.net/projects/markdown).
* Reference methods and classes in markdown with the custom `{}` notation:
* Reference classes with `{ClassName}`
* Reference instance methods with `{ClassName::methodName}`
* Reference class methods with `{ClassName.methodName}`
### Example
```coffee
# Public: Disable the package with the given name.
#
# This method emits multiple events:
#
# * `package-will-be-disabled` - before the package is disabled.
# * `package-disabled` - after the package is disabled.
#
# name - The {String} name of the package to disable.
# options - The {Object} with disable options (default: {}):
# :trackTime - `true` to track the amount of time disabling took.
# :ignoreErrors - `true` to catch and ignore errors thrown.
# callback - The {Function} to call after the package has been disabled.
#
# Returns `undefined`.
disablePackage: (name, options, callback) ->
```

1
docs/contributing.md Symbolic link
View File

@@ -0,0 +1 @@
../CONTRIBUTING.md

View File

@@ -5,6 +5,7 @@
* [Creating a Package](creating-a-package.md)
* [Creating a Theme](creating-a-theme.md)
* [Publishing a Package](publishing-a-package.md)
* [Writing Specs](writing-specs.md)
* [Converting a TextMate Bundle](converting-a-text-mate-bundle.md)
* [Converting a TextMate Theme](converting-a-text-mate-theme.md)
* [Contributing](contributing.md)

61
docs/writing-specs.md Normal file
View File

@@ -0,0 +1,61 @@
# Writting specs
Atom uses [Jasmine](http://jasmine.github.io/2.0/introduction.html) as its spec framework. Any new functionality should have specs to guard against regressions.
## Create a new spec
[Atom specs](https://github.com/atom/atom/tree/master/spec) and [package specs](https://github.com/atom/markdown-preview/tree/master/spec) are added to their respective `spec` directory. The example below creates a spec for Atom core.
0. Create a spec file
Spec files **must** end with `-spec` so add `sample-spec.coffee` to `atom/spec`.
0. Add one or more `describe` method
The `describe` method takes two arguments, a description and a function. If the description explains a behavior it typically begins with `when` if it is more like a unit test it begins with the method name.
```coffee
describe "when a test is written", ->
# contents
```
or
```coffee
describe "Editor::moveUp", ->
# contents
```
0. Add one or more `it` method
The `it` method also takes two arugments, a description and a function. Try and make the description flow with the `it` method. For example, a description of `this should work` doesn't read well as `it this should work`. But a description of `should work` sounds great as `it should work`.
```coffee
describe "when a test is written", ->
it "has some expectations that should pass", ->
# Expectations
```
0. Add one or more expectations
The best way to learn about expectations is to read the [jamsine documentation](http://jasmine.github.io/2.0/introduction.html#section-Expectations) about them. Below is a simple example.
```coffee
describe "when a test is written", ->
it "has some expectations that should pass", ->
expect("apples").toEqual("apples")
expect("oranges").not.toEqual("apples")
```
## Runnings specs
Most of the time you'll want to run specs by triggering the `window:run-package-specs` command. This command is not only to run package specs, it is also for Atom core specs. This will run all the specs in the current project's spec directory. If you want to run the Atom core specs and **all** the default package specs trigger the `window:run-all-specs` command.
To run a limited subset of specs use the `fdescribe` or `fit` methods. You can use those to focus a single spec or several specs. In the example above, focusing an individual spec looks like this:
```coffee
describe "when a test is written", ->
fit "has some expectations that should pass", ->
expect("apples").toEqual("apples")
expect("oranges").not.toEqual("apples")
```

View File

@@ -149,6 +149,8 @@ ASCII art professional!
* [Getting your project on GitHub guide](http://guides.github.com/overviews/desktop)
* [Writing specs](writing-specs.md) for your package
* [Creating a package guide](creating-a-package.html) for more information
on the mechanics of packages

View File

@@ -20,7 +20,7 @@
"atomShellVersion": "0.12.5",
"dependencies": {
"async": "0.2.6",
"atom-keymap": "^0.19.0",
"atom-keymap": "^0.21.0",
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
"clear-cut": "0.4.0",
"coffee-script": "1.7.0",
@@ -58,7 +58,7 @@
"temp": "0.5.0",
"text-buffer": "^2.2.2",
"theorist": "^1",
"underscore-plus": "^1.2.2",
"underscore-plus": "^1.3.0",
"vm-compatibility-layer": "0.1.0"
},
"packageDependencies": {
@@ -75,7 +75,7 @@
"autosave": "0.13.0",
"background-tips": "0.13.0",
"bookmarks": "0.22.0",
"bracket-matcher": "0.36.0",
"bracket-matcher": "0.38.0",
"command-palette": "0.21.0",
"deprecation-cop": "0.5.0",
"dev-live-reload": "0.30.0",
@@ -89,7 +89,7 @@
"image-view": "0.33.0",
"keybinding-resolver": "0.17.0",
"link": "0.22.0",
"markdown-preview": "0.71.0",
"markdown-preview": "0.72.0",
"metrics": "0.32.0",
"open-on-github": "0.28.0",
"package-generator": "0.30.0",
@@ -99,7 +99,7 @@
"spell-check": "0.35.0",
"status-bar": "0.40.0",
"styleguide": "0.29.0",
"symbols-view": "0.52.0",
"symbols-view": "0.53.0",
"tabs": "0.40.0",
"timecop": "0.18.0",
"tree-view": "0.93.0",
@@ -125,7 +125,7 @@
"language-php": "0.14.0",
"language-property-list": "0.7.0",
"language-python": "0.17.0",
"language-ruby": "0.24.0",
"language-ruby": "0.25.0",
"language-ruby-on-rails": "0.14.0",
"language-sass": "0.11.0",
"language-shellscript": "0.8.0",

View File

@@ -1,5 +1,7 @@
#!/usr/bin/env node
var fs = require('fs');
var nodeVersion = process.versions.node.split('.')
var nodeMajorVersion = +nodeVersion[0]
var nodeMinorVersion = +nodeVersion[1]
@@ -8,6 +10,12 @@ if (nodeMajorVersion === 0 && nodeMinorVersion < 10) {
process.exit(1);
}
// Make sure python2.7 is installed
if (process.platform == 'win32' && !fs.existsSync('C:\\Python27\\')) {
console.warn("You must have Python 2.7 installed at 'C:\\Python27\\'");
process.exit(1);
}
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
var fs = require('fs');
var path = require('path');

View File

@@ -1,4 +1,6 @@
{extend, flatten, toArray, last} = require 'underscore-plus'
_ = require 'underscore-plus'
{extend, flatten, toArray, last} = _
ReactEditorView = require '../src/react-editor-view'
nbsp = String.fromCharCode(160)
@@ -88,6 +90,51 @@ describe "EditorComponent", ->
expect(component.lineNodeForScreenRow(3).offsetTop).toBe 3 * lineHeightInPixels
expect(component.lineNodeForScreenRow(4).offsetTop).toBe 4 * lineHeightInPixels
describe "when showInvisibles is enabled", ->
invisibles = null
beforeEach ->
invisibles =
eol: 'E'
space: 'S'
tab: 'T'
cr: 'C'
atom.config.set("editor.showInvisibles", true)
atom.config.set("editor.invisibles", invisibles)
it "re-renders the lines when the showInvisibles config option changes", ->
editor.setText " a line with tabs\tand spaces "
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
atom.config.set("editor.showInvisibles", false)
expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces "
atom.config.set("editor.showInvisibles", true)
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
it "displays spaces, tabs, and newlines as visible characters", ->
editor.setText " a line with tabs\tand spaces "
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
it "displays newlines as their own token outside of the other tokens' scopes", ->
editor.setText "var"
expect(component.lineNodeForScreenRow(0).innerHTML).toBe "<span class=\"source js\"><span class=\"storage modifier js\">var</span></span><span class=\"invisible-character\">#{invisibles.eol}</span>"
it "displays trailing carriage returns using a visible, non-empty value", ->
editor.setText "a line that ends with a carriage return\r\n"
expect(component.lineNodeForScreenRow(0).textContent).toBe "a line that ends with a carriage return#{invisibles.cr}#{invisibles.eol}"
describe "when soft wrapping is enabled", ->
beforeEach ->
editor.setText "a line that wraps "
editor.setSoftWrap(true)
node.style.width = 15 * charWidth + 'px'
component.measureHeightAndWidth()
it "doesn't show end of line invisibles at the end of wrapped lines", ->
expect(component.lineNodeForScreenRow(0).textContent).toBe "a line that "
expect(component.lineNodeForScreenRow(1).textContent).toBe "wraps#{invisibles.space}#{invisibles.eol}"
describe "when indent guides are enabled", ->
beforeEach ->
component.setShowIndentGuide(true)
@@ -274,19 +321,22 @@ describe "EditorComponent", ->
expect(cursorRect.width).toBe rangeRect.width
it "blinks cursors when they aren't moving", ->
jasmine.unspy(window, 'setTimeout')
spyOn(_._, 'now').andCallFake -> window.now # Ensure _.debounce is based on our fake spec timeline
cursorsNode = node.querySelector('.cursors')
expect(cursorsNode.classList.contains('blinking')).toBe true
expect(cursorsNode.classList.contains('blink-off')).toBe false
advanceClock(component.props.cursorBlinkPeriod / 2)
expect(cursorsNode.classList.contains('blink-off')).toBe true
advanceClock(component.props.cursorBlinkPeriod / 2)
expect(cursorsNode.classList.contains('blink-off')).toBe false
# Stop blinking after moving the cursor
editor.moveCursorRight()
expect(cursorsNode.classList.contains('blinking')).toBe false
expect(cursorsNode.classList.contains('blink-off')).toBe false
# Resume blinking after resume delay passes
waits component.props.cursorBlinkResumeDelay
runs ->
expect(cursorsNode.classList.contains('blinking')).toBe true
advanceClock(component.props.cursorBlinkResumeDelay)
advanceClock(component.props.cursorBlinkPeriod / 2)
expect(cursorsNode.classList.contains('blink-off')).toBe true
it "renders the hidden input field at the position of the last cursor if it is on screen", ->
inputNode = node.querySelector('.hidden-input')

View File

@@ -33,10 +33,10 @@ describe "EditorView", ->
$('#jasmine-content').append(this)
waitsForPromise ->
atom.packages.activatePackage('language-text', sync: true)
atom.packages.activatePackage('language-text')
waitsForPromise ->
atom.packages.activatePackage('language-javascript', sync: true)
atom.packages.activatePackage('language-javascript')
getLineHeight = ->
return cachedLineHeight if cachedLineHeight?

View File

@@ -261,7 +261,9 @@ window.waitsForPromise = (args...) ->
window.resetTimeouts = ->
window.now = 0
window.timeoutCount = 0
window.intervalCount = 0
window.timeouts = []
window.intervalTimeouts = {}
window.fakeSetTimeout = (callback, ms) ->
id = ++window.timeoutCount
@@ -272,13 +274,15 @@ window.fakeClearTimeout = (idToClear) ->
window.timeouts = window.timeouts.filter ([id]) -> id != idToClear
window.fakeSetInterval = (callback, ms) ->
id = ++window.intervalCount
action = ->
callback()
window.fakeSetTimeout(action, ms)
window.fakeSetTimeout(action, ms)
window.intervalTimeouts[id] = window.fakeSetTimeout(action, ms)
window.intervalTimeouts[id] = window.fakeSetTimeout(action, ms)
id
window.fakeClearInterval = (idToClear) ->
window.fakeClearTimeout(idToClear)
window.fakeClearTimeout(@intervalTimeouts[idToClear])
window.advanceClock = (delta=1) ->
window.now += delta

View File

@@ -20,7 +20,7 @@ describe "ThemeManager", ->
describe "theme getters and setters", ->
beforeEach ->
atom.packages.loadPackages(sync: true)
atom.packages.loadPackages()
it 'getLoadedThemes get all the loaded themes', ->
themes = themeManager.getLoadedThemes()
@@ -280,15 +280,17 @@ describe "ThemeManager", ->
describe "when a non-existent theme is present in the config", ->
it "logs a warning but does not throw an exception (regression)", ->
reloaded = false
waitsForPromise ->
themeManager.activateThemes()
runs ->
themeManager.once 'reloaded', -> reloaded = true
spyOn(console, 'warn')
expect(-> atom.config.set('core.themes', ['atom-light-ui', 'theme-really-does-not-exist'])).not.toThrow()
waitsFor (done) ->
themeManager.once 'reloaded', done
waitsFor -> reloaded
runs ->
expect(console.warn.callCount).toBe 1

View File

@@ -13,10 +13,10 @@ CursorsComponent = React.createClass
render: ->
{editor, scrollTop, scrollLeft} = @props
{blinking} = @state
{blinkOff} = @state
className = 'cursors'
className += ' blinking' if blinking
className += ' blink-off' if blinkOff
div {className},
if @isMounted()
@@ -26,32 +26,30 @@ CursorsComponent = React.createClass
CursorComponent({key: cursor.id, cursor, scrollTop, scrollLeft})
getInitialState: ->
blinking: true
blinkOff: false
componentDidMount: ->
{editor} = @props
@startBlinkingCursors()
componentWillUnmount: ->
clearInterval(@cursorBlinkIntervalHandle)
@stopBlinkingCursors()
componentWillUpdate: ({cursorsMoved}) ->
@pauseCursorBlinking() if cursorsMoved
componentDidUpdate: ->
@syncCursorAnimations() if @props.selectionAdded
startBlinkingCursors: ->
@setState(blinking: true) if @isMounted()
@toggleCursorBlinkHandle = setInterval(@toggleCursorBlink, @props.cursorBlinkPeriod / 2) if @isMounted()
startBlinkingCursorsAfterDelay: null # Created lazily
stopBlinkingCursors: ->
clearInterval(@toggleCursorBlinkHandle)
toggleCursorBlink: ->
@setState(blinkOff: not @state.blinkOff)
pauseCursorBlinking: ->
@state.blinking = false
@state.blinkOff = false
@stopBlinkingCursors()
@startBlinkingCursorsAfterDelay ?= debounce(@startBlinkingCursors, @props.cursorBlinkResumeDelay)
@startBlinkingCursorsAfterDelay()
syncCursorAnimations: ->
node = @getDOMNode()
cursorNodes = toArray(node.children)
node.removeChild(cursorNode) for cursorNode in cursorNodes
node.appendChild(cursorNode) for cursorNode in cursorNodes

View File

@@ -1,6 +1,6 @@
React = require 'react'
{div, span} = require 'reactionary'
{debounce} = require 'underscore-plus'
{debounce, defaults} = require 'underscore-plus'
scrollbarStyle = require 'scrollbar-style'
GutterComponent = require './gutter-component'
@@ -31,9 +31,10 @@ EditorComponent = React.createClass
mouseWheelScreenRow: null
render: ->
{focused, fontSize, lineHeight, fontFamily, showIndentGuide} = @state
{editor, cursorBlinkResumeDelay} = @props
{focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles} = @state
{editor, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
maxLineNumberDigits = editor.getScreenLineCount().toString().length
invisibles = if showInvisibles then @state.invisibles else {}
if @isMounted()
renderedRowRange = @getRenderedRowRange()
@@ -61,8 +62,9 @@ EditorComponent = React.createClass
ref: 'scrollView', editor, fontSize, fontFamily, showIndentGuide,
lineHeight: lineHeightInPixels, renderedRowRange, @pendingChanges,
scrollTop, scrollLeft, scrollHeight, scrollWidth, @scrollingVertically,
@cursorsMoved, @selectionChanged, @selectionAdded, cursorBlinkResumeDelay,
@onInputFocused, @onInputBlurred, @mouseWheelScreenRow
@cursorsMoved, @selectionChanged, @selectionAdded, cursorBlinkPeriod,
cursorBlinkResumeDelay, @onInputFocused, @onInputBlurred, @mouseWheelScreenRow,
invisibles
}
ScrollbarComponent
@@ -101,12 +103,13 @@ EditorComponent = React.createClass
{editor, lineOverdrawMargin} = @props
[visibleStartRow, visibleEndRow] = editor.getVisibleRowRange()
renderedStartRow = Math.max(0, visibleStartRow - lineOverdrawMargin)
renderedEndRow = Math.min(editor.getLineCount(), visibleEndRow + lineOverdrawMargin)
renderedEndRow = Math.min(editor.getScreenLineCount(), visibleEndRow + lineOverdrawMargin)
[renderedStartRow, renderedEndRow]
getInitialState: -> {}
getDefaultProps: ->
cursorBlinkPeriod: 800
cursorBlinkResumeDelay: 100
lineOverdrawMargin: 8
@@ -268,6 +271,8 @@ EditorComponent = React.createClass
@subscribe atom.config.observe 'editor.fontFamily', @setFontFamily
@subscribe atom.config.observe 'editor.fontSize', @setFontSize
@subscribe atom.config.observe 'editor.showIndentGuide', @setShowIndentGuide
@subscribe atom.config.observe 'editor.invisibles', @setInvisibles
@subscribe atom.config.observe 'editor.showInvisibles', @setShowInvisibles
measureScrollbars: ->
@measuringScrollbars = false
@@ -291,6 +296,25 @@ EditorComponent = React.createClass
setShowIndentGuide: (showIndentGuide) ->
@setState({showIndentGuide})
# Public: Defines which characters are invisible.
#
# invisibles - An {Object} defining the invisible characters:
# :eol - The end of line invisible {String} (default: `\u00ac`).
# :space - The space invisible {String} (default: `\u00b7`).
# :tab - The tab invisible {String} (default: `\u00bb`).
# :cr - The carriage return invisible {String} (default: `\u00a4`).
setInvisibles: (invisibles={}) ->
defaults invisibles,
eol: '\u00ac'
space: '\u00b7'
tab: '\u00bb'
cr: '\u00a4'
@setState({invisibles})
setShowInvisibles: (showInvisibles) ->
@setState({showInvisibles})
onFocus: ->
@refs.scrollView.focus()

View File

@@ -16,9 +16,9 @@ EditorScrollViewComponent = React.createClass
overflowChangedWhilePaused: false
render: ->
{editor, fontSize, fontFamily, lineHeight, showIndentGuide} = @props
{editor, fontSize, fontFamily, lineHeight, showIndentGuide, invisibles} = @props
{renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollHeight, scrollWidth, scrollingVertically, mouseWheelScreenRow} = @props
{selectionChanged, selectionAdded, cursorBlinkResumeDelay, cursorsMoved, onInputFocused, onInputBlurred} = @props
{selectionChanged, selectionAdded, cursorBlinkPeriod, cursorBlinkResumeDelay, cursorsMoved, onInputFocused, onInputBlurred} = @props
if @isMounted()
inputStyle = @getHiddenInputPosition()
@@ -33,11 +33,11 @@ EditorScrollViewComponent = React.createClass
onFocus: onInputFocused
onBlur: onInputBlurred
CursorsComponent({editor, scrollTop, scrollLeft, cursorsMoved, selectionAdded, cursorBlinkResumeDelay})
CursorsComponent({editor, scrollTop, scrollLeft, cursorsMoved, selectionAdded, cursorBlinkPeriod, cursorBlinkResumeDelay})
LinesComponent {
ref: 'lines', editor, fontSize, fontFamily, lineHeight, showIndentGuide,
renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollingVertically,
selectionChanged, scrollHeight, scrollWidth, mouseWheelScreenRow
selectionChanged, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles
}
componentDidMount: ->

View File

@@ -131,7 +131,7 @@ GutterComponent = React.createClass
style = "visibility: hidden;"
innerHTML = @buildLineNumberInnerHTML(bufferRow, softWrapped, maxLineNumberDigits)
"<div class=\"line-number editor-colors\" style=\"#{style}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
"<div class=\"line-number\" style=\"#{style}\" data-screen-row=\"#{screenRow}\">#{innerHTML}</div>"
buildLineNumberInnerHTML: (bufferRow, softWrapped, maxLineNumberDigits) ->
if softWrapped

View File

@@ -16,6 +16,7 @@ InputComponent = React.createClass
{lastChar: ''}
componentDidMount: ->
@getDOMNode().addEventListener 'paste', @onPaste
@getDOMNode().addEventListener 'input', @onInput
@getDOMNode().addEventListener 'compositionupdate', @onCompositionUpdate
@@ -32,6 +33,9 @@ InputComponent = React.createClass
shouldComponentUpdate: (newProps) ->
not isEqual(newProps.style, @props.style)
onPaste: (e) ->
e.preventDefault()
onInput: (e) ->
e.stopPropagation()
valueCharCodes = punycode.ucs2.decode(@getDOMNode().value)

View File

@@ -35,7 +35,7 @@ LinesComponent = React.createClass
shouldComponentUpdate: (newProps) ->
return true if newProps.selectionChanged
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'fontSize', 'fontFamily', 'lineHeight', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically')
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'fontSize', 'fontFamily', 'lineHeight', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles')
{renderedRowRange, pendingChanges} = newProps
for change in pendingChanges
@@ -46,7 +46,7 @@ LinesComponent = React.createClass
componentDidUpdate: (prevProps) ->
@measureLineHeightAndCharWidth() unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily', 'lineHeight')
@clearScreenRowCaches() unless prevProps.lineHeight is @props.lineHeight
@removeLineNodes() unless prevProps.showIndentGuide is @props.showIndentGuide
@removeLineNodes() unless isEqualForProperties(prevProps, @props, 'showIndentGuide', 'invisibles')
@updateLines()
@clearScopedCharWidths() unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily')
@measureCharactersInNewLines() unless @props.scrollingVertically
@@ -132,7 +132,7 @@ LinesComponent = React.createClass
"&nbsp;"
buildLineInnerHTML: (line) ->
{invisibles, mini, showIndentGuide} = @props
{invisibles, mini, showIndentGuide, invisibles} = @props
{tokens, text} = line
innerHTML = ""
@@ -143,9 +143,22 @@ LinesComponent = React.createClass
innerHTML += @updateScopeStack(scopeStack, token.scopes)
hasIndentGuide = not mini and showIndentGuide and token.hasLeadingWhitespace or (token.hasTrailingWhitespace and lineIsWhitespaceOnly)
innerHTML += token.getValueAsHtml({invisibles, hasIndentGuide})
innerHTML += @popScope(scopeStack) while scopeStack.length > 0
innerHTML += @buildEndOfLineHTML(line, invisibles)
innerHTML
buildEndOfLineHTML: (line, invisibles) ->
return '' if @props.mini or line.isSoftWrapped()
html = ''
if invisibles.cr? and line.lineEnding is '\r\n'
html += "<span class='invisible-character'>#{invisibles.cr}</span>"
if invisibles.eol?
html += "<span class='invisible-character'>#{invisibles.eol}</span>"
html
updateScopeStack: (scopeStack, desiredScopes) ->
html = ""

View File

@@ -20,16 +20,8 @@
z-index: 1;
}
&.is-focused .cursors.blinking .cursor {
-webkit-animation: blink 0.8s;
-webkit-animation-iteration-count: infinite;
}
@-webkit-keyframes blink {
0% { opacity: .7; }
50% { opacity: .7; }
51% { opacity: 0; }
100% { opacity: 0; }
&.is-focused .cursors.blink-off .cursor {
visibility: hidden;
}
.horizontal-scrollbar {