Comparar commits
192 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 8f8c0cbf45 | |||
| 7e04d85e1e | |||
| 136836e615 | |||
| 82882624ce | |||
| c03e849089 | |||
| 25f98e223f | |||
| c2290ab8b4 | |||
| 9c39587d6e | |||
| 972aa75f3e | |||
| 1d1e4b33d4 | |||
| 79a61d47b2 | |||
| 26c9e7cfa9 | |||
| 28f9f22614 | |||
| 69caf6c98f | |||
| 51f1bb896c | |||
| 0fd834004e | |||
| d2654cb70c | |||
| 268553cda9 | |||
| 64a61d86d4 | |||
| 216a9e11c0 | |||
| 04373d2d9d | |||
| 9a2e768882 | |||
| d13d0543b9 | |||
| 6178294537 | |||
| 4871a7a06d | |||
| fe23d82e23 | |||
| d6e94b1105 | |||
| f0a2b17914 | |||
| ab2252f80f | |||
| f1cf8711a8 | |||
| d7cfb757d6 | |||
| ea2113e753 | |||
| 05f4604005 | |||
| 71b2400dbc | |||
| 1de46330d7 | |||
| de4e24582d | |||
| dffb0b35e7 | |||
| d8ec4573ec | |||
| 09a6e9a42e | |||
| 57fe9c6903 | |||
| cf3e1a9b6d | |||
| 4629905b65 | |||
| d4e8de60ce | |||
| 3237636206 | |||
| 934fe1c6d0 | |||
| 9aff1476d4 | |||
| 084d310366 | |||
| d122d18a34 | |||
| 6fb3dea9cd | |||
| ceb2a3857d | |||
| ab80da4363 | |||
| 19545e1113 | |||
| 88603e2771 | |||
| 5f323cc67c | |||
| 39d15d6087 | |||
| eaba8ef016 | |||
| 6cc4cf87b9 | |||
| c6a914a6e1 | |||
| 32af574206 | |||
| 552ab150cb | |||
| 2698925d10 | |||
| 13b1632dfc | |||
| de204b0321 | |||
| def07344e9 | |||
| 4ef3f39a7b | |||
| 2bf26e8db5 | |||
| 24b3b664dc | |||
| 2352829b28 | |||
| 3ca34dad40 | |||
| 15dc6442d0 | |||
| d775038f8d | |||
| 3829970a09 | |||
| 633306e6bf | |||
| 7d58008ed3 | |||
| bc9e32a3cf | |||
| a98791ebd0 | |||
| a72e1813e9 | |||
| fe9ffbeb12 | |||
| e61992c1f6 | |||
| 612332cd9f | |||
| 40d81ca22c | |||
| faf02460f5 | |||
| f010e8a888 | |||
| 712e9c7203 | |||
| 128a030880 | |||
| 907c3245a9 | |||
| da7cbeddc7 | |||
| 8b55c4981d | |||
| b7a59748c4 | |||
| b49de61402 | |||
| c38edbfe0b | |||
| 557ada7c81 | |||
| 236ec6fc3b | |||
| 35890588ca | |||
| c6c0ecbb37 | |||
| 1fdbf4612c | |||
| 948fa3a91c | |||
| a624d52f42 | |||
| 2b50f2409e | |||
| 0b11ce64a2 | |||
| f70c6928cd | |||
| cbb6d58d1b | |||
| 4dc2e48e8c | |||
| 7385bd97c6 | |||
| 063078cd34 | |||
| 789b8a2987 | |||
| 53ea135799 | |||
| 32242687bd | |||
| 3aca556827 | |||
| 372a64bd5d | |||
| 3f83d42afc | |||
| 8bf877e400 | |||
| 29f45b787b | |||
| 68491a512d | |||
| bd72f245b1 | |||
| 4ef5082887 | |||
| 27822fb37c | |||
| be803bf122 | |||
| db29a1b6a2 | |||
| 25d4bb8d6c | |||
| 8cc47a2069 | |||
| c825e20f62 | |||
| 69eace6292 | |||
| 2284435551 | |||
| 1252b94fcb | |||
| 8d0afc087f | |||
| f789c7e2df | |||
| 80bbd035db | |||
| 0397df2f4d | |||
| cd8796b561 | |||
| 68103601e6 | |||
| c278ead451 | |||
| c44e9b6308 | |||
| 1c37537d19 | |||
| 684dec4b24 | |||
| 30f72c1519 | |||
| 69733a8f7a | |||
| b9261a33be | |||
| e1a78d1f0f | |||
| c7de0ba3bc | |||
| 16cce1efb1 | |||
| b3025a4dfc | |||
| 11a1a27dbd | |||
| e76fe439ba | |||
| cc8513afdf | |||
| c17f5bd41e | |||
| 2d9ef9aad6 | |||
| f7bb8aab9e | |||
| c5c1980c9a | |||
| f234b8fa27 | |||
| 778e3bbd67 | |||
| 5d8d5da602 | |||
| fe449e31e3 | |||
| 617f0ae79a | |||
| cf3de0e8dc | |||
| 173c47f780 | |||
| 77e12e3658 | |||
| 8db9551700 | |||
| ece0c76158 | |||
| 8881847c61 | |||
| 4254efd49e | |||
| 6968075d38 | |||
| 3cb29e1d5c | |||
| e632b091f9 | |||
| afa92e51f6 | |||
| d746c7566c | |||
| 0a075c6934 | |||
| cef3b7d634 | |||
| c31ba651cf | |||
| d4d3426b4e | |||
| 89e3d2efec | |||
| c2326d096b | |||
| 18146346f6 | |||
| 519c514092 | |||
| 4187615fa7 | |||
| d98ea0180c | |||
| 226a4bf391 | |||
| a689a5906e | |||
| 18370bb663 | |||
| 1465736974 | |||
| 3907344fef | |||
| 7c414d47cb | |||
| 214d73d812 | |||
| 8c6fe7a540 | |||
| 164d5591a0 | |||
| 85cb7c88fa | |||
| 4ccbd03daf | |||
| 3df33a7367 | |||
| 13b592d1f3 | |||
| ab8df8dcde | |||
| 2b25d433c7 | |||
| 1ba8218743 |
+1
-1
@@ -1,6 +1,6 @@
|
||||
[submodule "vendor/bootstrap"]
|
||||
path = vendor/bootstrap
|
||||
url = https://github.com/twitter/bootstrap
|
||||
url = https://github.com/twbs/bootstrap
|
||||
[submodule "vendor/apm"]
|
||||
path = vendor/apm
|
||||
url = https://github.com/github/apm.git
|
||||
|
||||
@@ -8,6 +8,7 @@ pairs:
|
||||
jp: Justin Palmer; justin
|
||||
gt: Garen Torikian; garen
|
||||
mc: Matt Colyer; mcolyer
|
||||
jr: Jason Rudolph; jasonrudolph
|
||||
email:
|
||||
domain: github.com
|
||||
#global: true
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
* Fixed: Atom failing to launch if the theme being used was not found
|
||||
|
||||
* Improved: Theme changes now immediately take effect
|
||||
* Fixed: Wrap in quotes/parens now works in split panes
|
||||
* Improved: Autocomplete now includes CSS property names and values
|
||||
* Improved: Settings GUI is now a pane item
|
||||
* Added: Support package filtering in Settings GUI
|
||||
* Added: Dynamically load all config options in the Settings GUI
|
||||
* Added: Ability to bookmark lines and navigate bookmarks
|
||||
* Fixed: Error when inserting newlines in CSS
|
||||
* Fixed: Folding all will fold comments as well
|
||||
* Added: Ability to fold all code at a given indentation level
|
||||
|
||||
* Improved: cmd-n now opens a new tab and cmd-shift-n now opens a new window.
|
||||
* Added: Inspect Element context menu
|
||||
* Fixed: Save As dialog now defaults to directory path of current editor
|
||||
|
||||
+2
-1
@@ -77,6 +77,7 @@ module.exports = (grunt) ->
|
||||
'box-sizing': false
|
||||
'bulletproof-font-face': false
|
||||
'compatible-vendor-prefixes': false
|
||||
'display-property-grouping': false
|
||||
'fallback-colors': false
|
||||
'font-sizes': false
|
||||
'gradients': false
|
||||
@@ -112,6 +113,6 @@ module.exports = (grunt) ->
|
||||
|
||||
grunt.registerTask('compile', ['coffee', 'less', 'cson'])
|
||||
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('ci', ['lint', 'partial-clean', 'update-atom-shell', 'build', 'test'])
|
||||
grunt.registerTask('ci', ['lint', 'partial-clean', 'update-atom-shell', 'build', 'set-development-version', 'test'])
|
||||
grunt.registerTask('deploy', ['partial-clean', 'update-atom-shell', 'build', 'codesign'])
|
||||
grunt.registerTask('default', ['update-atom-shell', 'build', 'set-development-version', 'install'])
|
||||
|
||||
+27
-11
@@ -1,5 +1,6 @@
|
||||
#!/bin/sh
|
||||
ATOM_PATH=/Applications/Atom.app
|
||||
ATOM_BINARY=$ATOM_PATH/Contents/MacOS/Atom
|
||||
|
||||
if [ ! -d $ATOM_PATH ]; then sleep 5; fi # Wait for Atom to reappear, Sparkle may be replacing it.
|
||||
|
||||
@@ -8,7 +9,32 @@ if [ ! -d $ATOM_PATH ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
open -a $ATOM_PATH -n --args --executed-from="$(pwd)" --pid=$$ $@
|
||||
while getopts ":whv-:" opt; do
|
||||
case "$opt" in
|
||||
-)
|
||||
case "${OPTARG}" in
|
||||
wait)
|
||||
WAIT=1
|
||||
;;
|
||||
help|version)
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
w)
|
||||
WAIT=1
|
||||
;;
|
||||
h|v)
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $EXPECT_OUTPUT ]; then
|
||||
$ATOM_BINARY --executed-from="$(pwd)" --pid=$$ $@
|
||||
else
|
||||
open -a $ATOM_PATH -n --args --executed-from="$(pwd)" --pid=$$ $@
|
||||
fi
|
||||
|
||||
# Used to exit process when atom is used as $EDITOR
|
||||
on_die() {
|
||||
@@ -16,16 +42,6 @@ on_die() {
|
||||
}
|
||||
trap 'on_die' SIGQUIT SIGTERM
|
||||
|
||||
# Don't exit process if we were told to wait.
|
||||
while [ "$#" -gt "0" ]; do
|
||||
case $1 in
|
||||
-W|--wait)
|
||||
WAIT=1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ $WAIT ]; then
|
||||
while true; do
|
||||
sleep 1
|
||||
|
||||
@@ -22,7 +22,7 @@ You can always use `meta-p` to explore available commands and their
|
||||
bindings, but here's a list of a few useful commands.
|
||||
|
||||
- `meta-o` : open a file or directory
|
||||
- `meta-n` : open new window
|
||||
- `meta-shift-n` : open new window
|
||||
- `meta-r` : reload the current window
|
||||
- `meta-alt-ctrl-s` : run test specs
|
||||
- `meta-t` : open file finder to navigate files in your project
|
||||
@@ -146,10 +146,13 @@ Atom loads configuration settings from the `config.cson` file in your `~/.atom`
|
||||
directory, which contains CoffeeScript-style JSON:
|
||||
|
||||
```coffeescript
|
||||
core:
|
||||
hideGitIgnoredFiles: true
|
||||
editor:
|
||||
fontSize: 18
|
||||
'editor':
|
||||
'fontSize': 16
|
||||
'core':
|
||||
'themes': [
|
||||
'atom-dark-ui'
|
||||
'atom-dark-syntax'
|
||||
]
|
||||
```
|
||||
|
||||
Configuration is broken into namespaces, which are defined by the config hash's
|
||||
@@ -192,12 +195,12 @@ to apply styles to elements, Atom keymaps use selectors to associate keystrokes
|
||||
with events in specific contexts. Here's a small example, excerpted from Atom's
|
||||
built-in keymaps:
|
||||
|
||||
```coffee-script
|
||||
```coffeescript
|
||||
'.editor':
|
||||
'enter': 'editor:newline'
|
||||
|
||||
".select-list .editor.mini":
|
||||
'enter': 'core:confirm',
|
||||
'.select-list .editor.mini':
|
||||
'enter': 'core:confirm'
|
||||
```
|
||||
|
||||
This keymap defines the meaning of `enter` in two different contexts. In a
|
||||
@@ -220,7 +223,7 @@ active at the same time. For example, you'll usually select a theme for the UI
|
||||
and another theme for syntax highlighting. You can select themes by specifying
|
||||
them in the `core.themes` array in your `config.cson`:
|
||||
|
||||
```coffee-script
|
||||
```coffeescript
|
||||
core:
|
||||
themes: ["atom-light-ui", "atom-light-syntax"]
|
||||
# or, if the sun is going down:
|
||||
|
||||
+6
-5
@@ -25,9 +25,9 @@
|
||||
"temp": "0.5.0",
|
||||
"rimraf": "2.1.4",
|
||||
"plist": "git://github.com/nathansobo/node-plist.git",
|
||||
"space-pen": "1.0.0",
|
||||
"space-pen": "1.2.0",
|
||||
"less": "git://github.com/nathansobo/less.js.git",
|
||||
"roaster": "0.0.5",
|
||||
"roaster": "0.0.7",
|
||||
"jqueryui-browser": "1.10.2-1",
|
||||
"optimist": "0.4.0",
|
||||
"season": "0.10.0",
|
||||
@@ -37,7 +37,7 @@
|
||||
"tantamount": "0.3.0",
|
||||
"coffeestack": "0.4.0",
|
||||
"c-tmbundle": "1.0.0",
|
||||
"coffee-script-tmbundle": "2.0.0",
|
||||
"coffee-script-tmbundle": "4.0.0",
|
||||
"css-tmbundle": "1.0.0",
|
||||
"git-tmbundle": "1.0.0",
|
||||
"go-tmbundle": "1.0.0",
|
||||
@@ -78,9 +78,10 @@
|
||||
"grunt-cson": "0.5.0",
|
||||
"grunt-contrib-csslint": "~0.1.2",
|
||||
"grunt-contrib-coffee": "~0.7.0",
|
||||
"grunt-contrib-less": "~0.5.2",
|
||||
"grunt-contrib-less": "~0.6.4",
|
||||
"jasmine-focused": "~0.7.0",
|
||||
"walkdir": "0.0.7"
|
||||
"walkdir": "0.0.7",
|
||||
"js-yaml": "~2.1.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -5,7 +5,7 @@ cd "$(dirname "${BASH_SOURCE[0]}" )/.."
|
||||
TARGET=${1:-atom-shell}
|
||||
DISTURL="https://gh-contractor-zcbenz.s3.amazonaws.com/atom-shell"
|
||||
CURRENT_VERSION=$(cat "${TARGET}/version" 2>&1)
|
||||
LATEST_VERSION=$(curl -fsSkL $DISTURL/version)
|
||||
LATEST_VERSION=ea1f81aa5260fe2d4a844f18e0251278fbadc093 # v0.2.1
|
||||
|
||||
if [ -z "${LATEST_VERSION}" ] ; then
|
||||
echo "Could determine lastest version of atom-shell" >&2
|
||||
|
||||
Arquivo executável
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
usage = """
|
||||
Usage:
|
||||
update-octicons PATH-TO-OCTICONS
|
||||
"""
|
||||
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
YAML = require 'js-yaml'
|
||||
|
||||
scriptPath = process.argv[1]
|
||||
pathToOcticons = process.argv[2] ? path.join(process.env.HOME, 'github', 'octicons')
|
||||
atomDir = path.resolve(scriptPath, "../../..")
|
||||
|
||||
unless fs.existsSync(pathToOcticons)
|
||||
console.error(usage)
|
||||
process.exit(1)
|
||||
|
||||
# Copy font-file
|
||||
fontSrc = path.join(pathToOcticons, 'octicons', 'octicons.woff')
|
||||
fontDest = path.join(atomDir, 'static', 'octicons.woff')
|
||||
fs.createReadStream(fontSrc).pipe(fs.createWriteStream(fontDest))
|
||||
|
||||
# Update Octicon UTF codes
|
||||
glyphsSrc = path.join(pathToOcticons, 'data', 'glyphs.yml')
|
||||
octiconUtfDest = path.join atomDir, 'static', 'octicon-utf-codes.less'
|
||||
output = []
|
||||
for {css, code} in YAML.load(fs.readFileSync(glyphsSrc).toString())
|
||||
output.push "@#{css}: \"\\#{code}\";"
|
||||
|
||||
fs.writeFileSync octiconUtfDest, "#{output.join('\n')}\n"
|
||||
@@ -217,18 +217,6 @@ describe "the `atom` global", ->
|
||||
runs ->
|
||||
expect(syntax.getProperty(['.source.pref'], 'editor.increaseIndentPattern')).toBe '^abc$'
|
||||
|
||||
describe ".activatePackageConfig(id)", ->
|
||||
it "calls the optional .activateConfigMenu method on the package's main module", ->
|
||||
pack = atom.activatePackageConfig('package-with-activate-config')
|
||||
expect(pack.mainModule.activateCalled).toBeFalsy()
|
||||
expect(pack.mainModule.activateConfigCalled).toBeTruthy()
|
||||
|
||||
it "loads the package's config defaults", ->
|
||||
expect(config.get('package-with-config-defaults.numbers.one')).toBeUndefined()
|
||||
atom.activatePackageConfig('package-with-config-defaults')
|
||||
expect(config.get('package-with-config-defaults.numbers.one')).toBe 1
|
||||
expect(config.get('package-with-config-defaults.numbers.two')).toBe 2
|
||||
|
||||
describe ".deactivatePackage(id)", ->
|
||||
describe "atom packages", ->
|
||||
it "calls `deactivate` on the package's main module", ->
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
ConfigPanel = require 'config-panel'
|
||||
Editor = require 'editor'
|
||||
|
||||
describe "ConfigPanel", ->
|
||||
it "automatically binds named input fields to their corresponding config keys", ->
|
||||
class TestPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@div =>
|
||||
@input outlet: 'intInput', id: 'foo.int', type: 'int'
|
||||
@input outlet: 'floatInput', id: 'foo.float', type: 'float'
|
||||
@input outlet: 'stringInput', id: 'foo.string', type: 'string'
|
||||
@input outlet: 'booleanInput', id: 'foo.boolean', type: 'checkbox'
|
||||
|
||||
config.set('foo.int', 22)
|
||||
config.set('foo.boolean', true)
|
||||
|
||||
panel = new TestPanel
|
||||
expect(panel.intInput.val()).toBe '22'
|
||||
expect(panel.floatInput.val()).toBe ''
|
||||
expect(panel.stringInput.val()).toBe ''
|
||||
expect(panel.booleanInput.attr('checked')).toBeTruthy()
|
||||
|
||||
config.set('foo.int', 10)
|
||||
expect(panel.intInput.val()).toBe '10'
|
||||
expect(panel.floatInput.val()).toBe ''
|
||||
expect(panel.stringInput.val()).toBe ''
|
||||
|
||||
config.set('foo.string', 'hey')
|
||||
expect(panel.intInput.val()).toBe '10'
|
||||
expect(panel.floatInput.val()).toBe ''
|
||||
expect(panel.stringInput.val()).toBe 'hey'
|
||||
|
||||
config.set('foo.boolean', false)
|
||||
expect(panel.booleanInput.attr('checked')).toBeFalsy()
|
||||
|
||||
panel.intInput.val('90.2').change()
|
||||
expect(config.get('foo.int')).toBe 90
|
||||
|
||||
panel.floatInput.val('90.2').change()
|
||||
expect(config.get('foo.float')).toBe 90.2
|
||||
|
||||
panel.intInput.val('0').change()
|
||||
expect(config.get('foo.int')).toBe 0
|
||||
|
||||
panel.floatInput.val('0').change()
|
||||
expect(config.get('foo.float')).toBe 0
|
||||
|
||||
panel.stringInput.val('moo').change()
|
||||
expect(config.get('foo.string')).toBe 'moo'
|
||||
|
||||
panel.intInput.val('abcd').change()
|
||||
expect(config.get('foo.int')).toBe 'abcd'
|
||||
|
||||
panel.floatInput.val('defg').change()
|
||||
expect(config.get('foo.float')).toBe 'defg'
|
||||
|
||||
panel.intInput.val('').change()
|
||||
expect(config.get('foo.int')).toBe undefined
|
||||
panel.floatInput.val('').change()
|
||||
expect(config.get('foo.float')).toBe undefined
|
||||
panel.stringInput.val('').change()
|
||||
expect(config.get('foo.string')).toBe undefined
|
||||
|
||||
it "automatically binds named editors to their corresponding config keys", ->
|
||||
class TestPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@div =>
|
||||
@subview 'intEditor', new Editor(mini: true, attributes: { id: 'foo.int', type: 'int' })
|
||||
@subview 'floatEditor', new Editor(mini: true, attributes: { id: 'foo.float', type: 'float' })
|
||||
@subview 'stringEditor', new Editor(mini: true, attributes: { id: 'foo.string', type: 'string' })
|
||||
|
||||
config.set('foo.int', 1)
|
||||
config.set('foo.float', 1.1)
|
||||
config.set('foo.string', 'I think therefore I am.')
|
||||
panel = new TestPanel
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(panel.intEditor.getText()).toBe '1'
|
||||
expect(panel.floatEditor.getText()).toBe '1.1'
|
||||
expect(panel.stringEditor.getText()).toBe 'I think therefore I am.'
|
||||
|
||||
config.set('foo.int', 2)
|
||||
config.set('foo.float', 2.2)
|
||||
config.set('foo.string', 'We are what we think.')
|
||||
expect(panel.intEditor.getText()).toBe '2'
|
||||
expect(panel.floatEditor.getText()).toBe '2.2'
|
||||
expect(panel.stringEditor.getText()).toBe 'We are what we think.'
|
||||
|
||||
panel.intEditor.setText('3')
|
||||
panel.floatEditor.setText('3.3')
|
||||
panel.stringEditor.setText('All limitations are self imposed.')
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(config.get('foo.int')).toBe 3
|
||||
expect(config.get('foo.float')).toBe 3.3
|
||||
expect(config.get('foo.string')).toBe 'All limitations are self imposed.'
|
||||
|
||||
|
||||
panel.intEditor.setText('not an int')
|
||||
panel.floatEditor.setText('not a float')
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(config.get('foo.int')).toBe 'not an int'
|
||||
expect(config.get('foo.float')).toBe 'not a float'
|
||||
|
||||
panel.intEditor.setText('')
|
||||
panel.floatEditor.setText('')
|
||||
panel.stringEditor.setText('')
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(config.get('foo.int')).toBe undefined
|
||||
expect(config.get('foo.float')).toBe undefined
|
||||
expect(config.get('foo.string')).toBe undefined
|
||||
|
||||
panel.intEditor.setText('0')
|
||||
panel.floatEditor.setText('0')
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(config.get('foo.int')).toBe 0
|
||||
expect(config.get('foo.float')).toBe 0
|
||||
|
||||
it "does not save the config value until it has been changed to a new value", ->
|
||||
class TestPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@div =>
|
||||
@subview "fooInt", new Editor(mini: true, attributes: {id: 'foo.int', type: 'int'})
|
||||
|
||||
config.set('foo.int', 1)
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
config.observe "foo.int", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
testPanel = new TestPanel
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
testPanel.fooInt.setText("1")
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
testPanel.fooInt.setText("2")
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
@@ -1,46 +0,0 @@
|
||||
ConfigView = require 'config-view'
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
describe "ConfigView", ->
|
||||
configView = null
|
||||
|
||||
beforeEach ->
|
||||
configView = new ConfigView
|
||||
|
||||
describe "serialization", ->
|
||||
it "remembers which panel was visible", ->
|
||||
configView.showPanel('Editor')
|
||||
newConfigView = deserialize(configView.serialize())
|
||||
configView.remove()
|
||||
newConfigView.attachToDom()
|
||||
expect(newConfigView.activePanelName).toBe 'Editor'
|
||||
|
||||
it "shows the previously active panel if it is added after deserialization", ->
|
||||
configView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
configView.showPanel('Panel 1')
|
||||
newConfigView = deserialize(configView.serialize())
|
||||
configView.remove()
|
||||
newConfigView.attachToDom()
|
||||
newConfigView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
expect(newConfigView.activePanelName).toBe 'Panel 1'
|
||||
|
||||
describe ".addPanel(name, view)", ->
|
||||
it "adds a menu entry to the left and a panel that can be activated by clicking it", ->
|
||||
configView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
configView.addPanel('Panel 2', $$ -> @div id: 'panel-2')
|
||||
|
||||
expect(configView.panelMenu.find('li a:contains(Panel 1)')).toExist()
|
||||
expect(configView.panelMenu.find('li a:contains(Panel 2)')).toExist()
|
||||
expect(configView.panelMenu.children(':first')).toHaveClass 'active'
|
||||
|
||||
configView.attachToDom()
|
||||
configView.panelMenu.find('li a:contains(Panel 1)').click()
|
||||
expect(configView.panelMenu.children('.active').length).toBe 1
|
||||
expect(configView.panelMenu.find('li:contains(Panel 1)')).toHaveClass('active')
|
||||
expect(configView.panels.find('#panel-1')).toBeVisible()
|
||||
expect(configView.panels.find('#panel-2')).toBeHidden()
|
||||
configView.panelMenu.find('li a:contains(Panel 2)').click()
|
||||
expect(configView.panelMenu.children('.active').length).toBe 1
|
||||
expect(configView.panelMenu.find('li:contains(Panel 2)')).toHaveClass('active')
|
||||
expect(configView.panels.find('#panel-1')).toBeHidden()
|
||||
expect(configView.panels.find('#panel-2')).toBeVisible()
|
||||
+201
-130
@@ -103,6 +103,15 @@ describe "EditSession", ->
|
||||
editSession.moveCursorDown()
|
||||
expect(editSession.getCursorScreenPosition()).toEqual([1, 4])
|
||||
|
||||
describe "when there is a selection", ->
|
||||
beforeEach ->
|
||||
editSession.setSelectedBufferRange([[4, 9],[5, 10]])
|
||||
|
||||
it "moves above the selection", ->
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorUp()
|
||||
expect(cursor.getBufferPosition()).toEqual [3, 9]
|
||||
|
||||
it "merges cursors when they overlap", ->
|
||||
editSession.addCursorAtScreenPosition([1, 0])
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
@@ -152,6 +161,15 @@ describe "EditSession", ->
|
||||
editSession.moveCursorUp()
|
||||
expect(editSession.getCursorScreenPosition().column).toBe 0
|
||||
|
||||
describe "when there is a selection", ->
|
||||
beforeEach ->
|
||||
editSession.setSelectedBufferRange([[4, 9],[5, 10]])
|
||||
|
||||
it "moves below the selection", ->
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorDown()
|
||||
expect(cursor.getBufferPosition()).toEqual [6, 10]
|
||||
|
||||
it "merges cursors when they overlap", ->
|
||||
editSession.setCursorScreenPosition([12, 2])
|
||||
editSession.addCursorAtScreenPosition([11, 2])
|
||||
@@ -187,6 +205,18 @@ describe "EditSession", ->
|
||||
editSession.moveCursorLeft()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [5, 4]
|
||||
|
||||
describe "when there is a selection", ->
|
||||
beforeEach ->
|
||||
editSession.setSelectedBufferRange([[5, 22],[5, 27]])
|
||||
|
||||
it "moves to the left of the selection", ->
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorLeft()
|
||||
expect(cursor.getBufferPosition()).toEqual [5, 22]
|
||||
|
||||
editSession.moveCursorLeft()
|
||||
expect(cursor.getBufferPosition()).toEqual [5, 21]
|
||||
|
||||
it "merges cursors when they overlap", ->
|
||||
editSession.setCursorScreenPosition([0, 0])
|
||||
editSession.addCursorAtScreenPosition([0, 1])
|
||||
@@ -221,6 +251,18 @@ describe "EditSession", ->
|
||||
|
||||
expect(editSession.getCursorScreenPosition()).toEqual(lastPosition)
|
||||
|
||||
describe "when there is a selection", ->
|
||||
beforeEach ->
|
||||
editSession.setSelectedBufferRange([[5, 22],[5, 27]])
|
||||
|
||||
it "moves to the left of the selection", ->
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorRight()
|
||||
expect(cursor.getBufferPosition()).toEqual [5, 27]
|
||||
|
||||
editSession.moveCursorRight()
|
||||
expect(cursor.getBufferPosition()).toEqual [5, 28]
|
||||
|
||||
it "merges cursors when they overlap", ->
|
||||
editSession.setCursorScreenPosition([12, 2])
|
||||
editSession.addCursorAtScreenPosition([12, 1])
|
||||
@@ -247,44 +289,72 @@ describe "EditSession", ->
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [12,2]
|
||||
|
||||
describe ".moveCursorToBeginningOfLine()", ->
|
||||
it "moves cursor to the beginning of line", ->
|
||||
editSession.setCursorScreenPosition [0,5]
|
||||
editSession.addCursorAtScreenPosition [1,7]
|
||||
editSession.moveCursorToBeginningOfLine()
|
||||
expect(editSession.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editSession.setSoftWrapColumn(10)
|
||||
editSession.setCursorScreenPosition([1, 2])
|
||||
editSession.moveCursorToBeginningOfLine()
|
||||
cursor = editSession.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [1, 0]
|
||||
|
||||
describe "when soft wrap is off", ->
|
||||
it "moves cursor to the beginning of then line", ->
|
||||
editSession.setCursorScreenPosition [0,5]
|
||||
editSession.addCursorAtScreenPosition [1,7]
|
||||
editSession.moveCursorToBeginningOfLine()
|
||||
expect(editSession.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe ".moveCursorToEndOfLine()", ->
|
||||
it "moves cursor to the end of line", ->
|
||||
editSession.setCursorScreenPosition [0,0]
|
||||
editSession.addCursorAtScreenPosition [1,0]
|
||||
editSession.moveCursorToEndOfLine()
|
||||
expect(editSession.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,29]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,30]
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editSession.setSoftWrapColumn(10)
|
||||
editSession.setCursorScreenPosition([1, 2])
|
||||
editSession.moveCursorToEndOfLine()
|
||||
cursor = editSession.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [1, 9]
|
||||
|
||||
describe "when soft wrap is off", ->
|
||||
it "moves cursor to the end of line", ->
|
||||
editSession.setCursorScreenPosition [0,0]
|
||||
editSession.addCursorAtScreenPosition [1,0]
|
||||
editSession.moveCursorToEndOfLine()
|
||||
expect(editSession.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,29]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,30]
|
||||
|
||||
describe ".moveCursorToFirstCharacterOfLine()", ->
|
||||
it "moves to the first character of the current line or the beginning of the line if it's already on the first character", ->
|
||||
editSession.setCursorScreenPosition [0,5]
|
||||
editSession.addCursorAtScreenPosition [1,7]
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves to the first character of the current screen line or the beginning of the screen line if it's already on the first character", ->
|
||||
editSession.setSoftWrapColumn(10)
|
||||
editSession.setCursorScreenPosition [2,5]
|
||||
editSession.addCursorAtScreenPosition [8,7]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,2]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe "when triggered ", ->
|
||||
it "does not move the cursor", ->
|
||||
editSession.setCursorBufferPosition([10, 0])
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [10, 0]
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getScreenPosition()).toEqual [2,0]
|
||||
expect(cursor2.getScreenPosition()).toEqual [8,4]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
expect(cursor1.getScreenPosition()).toEqual [2,0]
|
||||
expect(cursor2.getScreenPosition()).toEqual [8,0]
|
||||
|
||||
describe "when soft wrap is of", ->
|
||||
it "moves to the first character of the current line or the beginning of the line if it's already on the first character", ->
|
||||
editSession.setCursorScreenPosition [0,5]
|
||||
editSession.addCursorAtScreenPosition [1,7]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,2]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe ".moveCursorToBeginningOfWord()", ->
|
||||
it "moves the cursor to the beginning of the word", ->
|
||||
@@ -313,6 +383,36 @@ describe "EditSession", ->
|
||||
editSession.moveCursorToBeginningOfWord()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [9, 2]
|
||||
|
||||
describe ".moveCursorToPreviousWordBoundary()", ->
|
||||
it "moves the cursor to the previous word boundary", ->
|
||||
editSession.setCursorBufferPosition [0, 8]
|
||||
editSession.addCursorAtBufferPosition [2, 0]
|
||||
editSession.addCursorAtBufferPosition [2, 4]
|
||||
editSession.addCursorAtBufferPosition [3, 14]
|
||||
[cursor1, cursor2, cursor3, cursor4] = editSession.getCursors()
|
||||
|
||||
editSession.moveCursorToPreviousWordBoundary()
|
||||
|
||||
expect(cursor1.getBufferPosition()).toEqual [0, 4]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1, 30]
|
||||
expect(cursor3.getBufferPosition()).toEqual [2, 0]
|
||||
expect(cursor4.getBufferPosition()).toEqual [3, 13]
|
||||
|
||||
describe ".moveCursorToNextWordBoundary()", ->
|
||||
it "moves the cursor to the previous word boundary", ->
|
||||
editSession.setCursorBufferPosition [0, 8]
|
||||
editSession.addCursorAtBufferPosition [2, 40]
|
||||
editSession.addCursorAtBufferPosition [3, 0]
|
||||
editSession.addCursorAtBufferPosition [3, 30]
|
||||
[cursor1, cursor2, cursor3, cursor4] = editSession.getCursors()
|
||||
|
||||
editSession.moveCursorToNextWordBoundary()
|
||||
|
||||
expect(cursor1.getBufferPosition()).toEqual [0, 13]
|
||||
expect(cursor2.getBufferPosition()).toEqual [3, 0]
|
||||
expect(cursor3.getBufferPosition()).toEqual [3, 4]
|
||||
expect(cursor4.getBufferPosition()).toEqual [3, 31]
|
||||
|
||||
describe ".moveCursorToEndOfWord()", ->
|
||||
it "moves the cursor to the end of the word", ->
|
||||
editSession.setCursorBufferPosition [0, 6]
|
||||
@@ -355,6 +455,14 @@ describe "EditSession", ->
|
||||
expect(cursor2.getBufferPosition()).toEqual [1, 13]
|
||||
expect(cursor3.getBufferPosition()).toEqual [2, 4]
|
||||
|
||||
# When the cursor is on whitespace
|
||||
editSession.setText("ab cde- ")
|
||||
editSession.setCursorBufferPosition [0,2]
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorToBeginningOfNextWord()
|
||||
|
||||
expect(cursor.getBufferPosition()).toEqual [0, 3]
|
||||
|
||||
it "does not blow up when there is no next word", ->
|
||||
editSession.setCursorBufferPosition [Infinity, Infinity]
|
||||
endPosition = editSession.getCursorBufferPosition()
|
||||
@@ -668,6 +776,46 @@ describe "EditSession", ->
|
||||
expect(selection2.getBufferRange()).toEqual [[3,48], [3,51]]
|
||||
expect(selection2.isReversed()).toBeFalsy()
|
||||
|
||||
describe ".selectToPreviousWordBoundary()", ->
|
||||
it "select to the previous word boundary", ->
|
||||
editSession.setCursorBufferPosition [0, 8]
|
||||
editSession.addCursorAtBufferPosition [2, 0]
|
||||
editSession.addCursorAtBufferPosition [3, 4]
|
||||
editSession.addCursorAtBufferPosition [3, 14]
|
||||
|
||||
editSession.selectToPreviousWordBoundary()
|
||||
|
||||
expect(editSession.getSelections().length).toBe 4
|
||||
[selection1, selection2, selection3, selection4] = editSession.getSelections()
|
||||
expect(selection1.getBufferRange()).toEqual [[0,8], [0,4]]
|
||||
expect(selection1.isReversed()).toBeTruthy()
|
||||
expect(selection2.getBufferRange()).toEqual [[2,0], [1,30]]
|
||||
expect(selection2.isReversed()).toBeTruthy()
|
||||
expect(selection3.getBufferRange()).toEqual [[3,4], [3,0]]
|
||||
expect(selection3.isReversed()).toBeTruthy()
|
||||
expect(selection4.getBufferRange()).toEqual [[3,14], [3,13]]
|
||||
expect(selection4.isReversed()).toBeTruthy()
|
||||
|
||||
describe ".selectToNextWordBoundary()", ->
|
||||
it "select to the next word boundary", ->
|
||||
editSession.setCursorBufferPosition [0, 8]
|
||||
editSession.addCursorAtBufferPosition [2, 40]
|
||||
editSession.addCursorAtBufferPosition [4, 0]
|
||||
editSession.addCursorAtBufferPosition [3, 30]
|
||||
|
||||
editSession.selectToNextWordBoundary()
|
||||
|
||||
expect(editSession.getSelections().length).toBe 4
|
||||
[selection1, selection2, selection3, selection4] = editSession.getSelections()
|
||||
expect(selection1.getBufferRange()).toEqual [[0,8], [0,13]]
|
||||
expect(selection1.isReversed()).toBeFalsy()
|
||||
expect(selection2.getBufferRange()).toEqual [[2,40], [3,0]]
|
||||
expect(selection2.isReversed()).toBeFalsy()
|
||||
expect(selection3.getBufferRange()).toEqual [[4,0], [4,4]]
|
||||
expect(selection3.isReversed()).toBeFalsy()
|
||||
expect(selection4.getBufferRange()).toEqual [[3,30], [3,31]]
|
||||
expect(selection4.isReversed()).toBeFalsy()
|
||||
|
||||
describe ".selectWord()", ->
|
||||
describe "when the cursor is inside a word", ->
|
||||
it "selects the entire word", ->
|
||||
@@ -1584,24 +1732,32 @@ describe "EditSession", ->
|
||||
expect(clipboard.readText()).toBe 'quicksort\nsort'
|
||||
|
||||
describe ".cutToEndOfLine()", ->
|
||||
describe "when nothing is selected", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "cuts up to the end of the line", ->
|
||||
editSession.setCursorBufferPosition([2, 20])
|
||||
editSession.addCursorAtBufferPosition([3, 20])
|
||||
editSession.setSoftWrapColumn(10)
|
||||
editSession.setCursorScreenPosition([2, 2])
|
||||
editSession.cutToEndOfLine()
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(pasteboard.read()[0]).toBe ' <= 1) return items;\ns.shift(), current, left = [], right = [];'
|
||||
expect(editSession.lineForScreenRow(2).text).toBe '= () {'
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "only cuts the selected text, not to the end of the line", ->
|
||||
editSession.setSelectedBufferRanges([[[2,20], [2, 30]], [[3, 20], [3, 20]]])
|
||||
describe "when soft wrap is off", ->
|
||||
describe "when nothing is selected", ->
|
||||
it "cuts up to the end of the line", ->
|
||||
editSession.setCursorBufferPosition([2, 20])
|
||||
editSession.addCursorAtBufferPosition([3, 20])
|
||||
editSession.cutToEndOfLine()
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(pasteboard.read()[0]).toBe ' <= 1) return items;\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
editSession.cutToEndOfLine()
|
||||
describe "when text is selected", ->
|
||||
it "only cuts the selected text, not to the end of the line", ->
|
||||
editSession.setSelectedBufferRanges([[[2,20], [2, 30]], [[3, 20], [3, 20]]])
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.lengthurn items;'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(pasteboard.read()[0]).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
editSession.cutToEndOfLine()
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.lengthurn items;'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(pasteboard.read()[0]).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe ".copySelectedText()", ->
|
||||
it "copies selected text onto the clipboard", ->
|
||||
@@ -1967,91 +2123,6 @@ describe "EditSession", ->
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor3.getBufferPosition()).toEqual [1,2]
|
||||
|
||||
describe "folding", ->
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
editSession.foldBufferRow(0)
|
||||
editSession.foldBufferRow(1)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
editSession.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
editSession.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editSession.lineForScreenRow(4).fold
|
||||
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7]
|
||||
|
||||
describe ".foldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be folded", ->
|
||||
it "creates a fold based on the syntactic region starting at the given row", ->
|
||||
editSession.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when bufferRow can't be folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
|
||||
editSession.foldBufferRow(8)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when the bufferRow is already folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
editSession.foldBufferRow(2)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editSession.lineForScreenRow(0).fold).not.toBeDefined()
|
||||
|
||||
editSession.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(0).fold).toBeDefined()
|
||||
|
||||
describe "when the bufferRow is in a multi-line comment", ->
|
||||
it "searches upward and downward for surrounding comment lines and folds them as a single fold", ->
|
||||
buffer.insert([1,0], " //this is a comment\n // and\n //more docs\n\n//second comment")
|
||||
editSession.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 3
|
||||
|
||||
describe "when the bufferRow is a single-line comment", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
buffer.insert([1,0], " //this is a single line comment\n")
|
||||
editSession.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(0).fold
|
||||
expect(fold.getStartRow()).toBe 0
|
||||
expect(fold.getEndRow()).toBe 13
|
||||
|
||||
describe ".unfoldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be unfolded", ->
|
||||
it "destroys a fold based on the syntactic region starting at the given row", ->
|
||||
editSession.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
|
||||
editSession.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "when bufferRow can't be unfolded", ->
|
||||
it "does not throw an error", ->
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
editSession.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
it "maintains cursor buffer position when a folding/unfolding", ->
|
||||
editSession.setCursorBufferPosition([5,5])
|
||||
editSession.foldAll()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual([5,5])
|
||||
|
||||
describe ".deleteLine()", ->
|
||||
it "deletes the first line when the cursor is there", ->
|
||||
editSession.getCursor().moveToTop()
|
||||
|
||||
@@ -44,12 +44,12 @@ describe "LanguageMode", ->
|
||||
expect(languageMode.doesBufferRowStartFold(2)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(3)).toBeFalsy()
|
||||
|
||||
describe ".rowRangeForFoldAtBufferRow(bufferRow)", ->
|
||||
describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(1)).toEqual [1, 9]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(4)).toEqual [4, 7]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(1)).toEqual [1, 9]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(4)).toEqual [4, 7]
|
||||
|
||||
describe "suggestedIndentForBufferRow", ->
|
||||
it "returns the suggested indentation based on auto-indent/outdent rules", ->
|
||||
@@ -58,6 +58,47 @@ describe "LanguageMode", ->
|
||||
expect(languageMode.suggestedIndentForBufferRow(2)).toBe 2
|
||||
expect(languageMode.suggestedIndentForBufferRow(9)).toBe 1
|
||||
|
||||
describe "rowRangeForParagraphAtBufferRow", ->
|
||||
describe "with code and comments", ->
|
||||
beforeEach ->
|
||||
buffer.setText '''
|
||||
var quicksort = function () {
|
||||
/* Single line comment block */
|
||||
var sort = function(items) {};
|
||||
|
||||
/*
|
||||
A multiline
|
||||
comment is here
|
||||
*/
|
||||
var sort = function(items) {};
|
||||
|
||||
// A comment
|
||||
//
|
||||
// Multiple comment
|
||||
// lines
|
||||
var sort = function(items) {};
|
||||
// comment line after fn
|
||||
};
|
||||
'''
|
||||
|
||||
it "will limit paragraph range to comments", ->
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(0)
|
||||
expect(range).toEqual [[0,0], [0,29]]
|
||||
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(10)
|
||||
expect(range).toEqual [[10,0], [10,14]]
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(11)
|
||||
expect(range).toBeFalsy()
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(12)
|
||||
expect(range).toEqual [[12,0], [13,10]]
|
||||
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(14)
|
||||
expect(range).toEqual [[14,0], [14,32]]
|
||||
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(15)
|
||||
expect(range).toEqual [[15,0], [15,26]]
|
||||
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('coffee-script-tmbundle', sync: true)
|
||||
@@ -98,12 +139,12 @@ describe "LanguageMode", ->
|
||||
expect(languageMode.doesBufferRowStartFold(3)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(19)).toBeTruthy()
|
||||
|
||||
describe ".rowRangeForFoldAtBufferRow(bufferRow)", ->
|
||||
describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(0)).toEqual [0, 20]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(1)).toEqual [1, 17]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(19)).toEqual [19, 20]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(0)).toEqual [0, 20]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(1)).toEqual [1, 17]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(19)).toEqual [19, 20]
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
@@ -158,3 +199,156 @@ describe "LanguageMode", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe "// @color: #4D926F;"
|
||||
|
||||
describe "folding", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('javascript-tmbundle', sync: true)
|
||||
editSession = project.open('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
it "maintains cursor buffer position when a folding/unfolding", ->
|
||||
editSession.setCursorBufferPosition([5,5])
|
||||
languageMode.foldAll()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual([5,5])
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
languageMode.foldBufferRow(0)
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
languageMode.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editSession.lineForScreenRow(4).fold
|
||||
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7]
|
||||
|
||||
describe ".foldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be folded", ->
|
||||
it "creates a fold based on the syntactic region starting at the given row", ->
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when bufferRow can't be folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
|
||||
languageMode.foldBufferRow(8)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when the bufferRow is already folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
languageMode.foldBufferRow(2)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editSession.lineForScreenRow(0).fold).not.toBeDefined()
|
||||
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(0).fold).toBeDefined()
|
||||
|
||||
describe "when the bufferRow is in a multi-line comment", ->
|
||||
it "searches upward and downward for surrounding comment lines and folds them as a single fold", ->
|
||||
buffer.insert([1,0], " //this is a comment\n // and\n //more docs\n\n//second comment")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 3
|
||||
|
||||
describe "when the bufferRow is a single-line comment", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
buffer.insert([1,0], " //this is a single line comment\n")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(0).fold
|
||||
expect(fold.getStartRow()).toBe 0
|
||||
expect(fold.getEndRow()).toBe 13
|
||||
|
||||
describe ".unfoldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be unfolded", ->
|
||||
it "destroys a fold based on the syntactic region starting at the given row", ->
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "when bufferRow can't be unfolded", ->
|
||||
it "does not throw an error", ->
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "folding with comments", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('javascript-tmbundle', sync: true)
|
||||
editSession = project.open('sample-with-comments.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
languageMode.foldBufferRow(0)
|
||||
languageMode.foldBufferRow(5)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
languageMode.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 4]
|
||||
|
||||
fold3 = editSession.lineForScreenRow(2).fold.destroy()
|
||||
|
||||
fold4 = editSession.lineForScreenRow(3).fold
|
||||
expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8]
|
||||
|
||||
describe ".foldAllAtIndentLevel()", ->
|
||||
it "folds every foldable range at a given indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(2)
|
||||
|
||||
fold1 = editSession.lineForScreenRow(6).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [6, 8]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(11).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 14]
|
||||
fold2.destroy()
|
||||
|
||||
it "does not fold anything but the indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(0)
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(5).fold
|
||||
expect(fold2).toBeFalsy()
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('source-tmbundle', sync: true)
|
||||
atom.activatePackage('css-tmbundle', sync: true)
|
||||
editSession = project.open('css.css', autoIndent: true)
|
||||
|
||||
describe "suggestedIndentForBufferRow", ->
|
||||
it "does not return negative values (regression)", ->
|
||||
editSession.setText('.test {\npadding: 0;\n}')
|
||||
expect(editSession.suggestedIndentForBufferRow(2)).toBe 0
|
||||
|
||||
@@ -31,6 +31,22 @@ describe "Project", ->
|
||||
expect(project.getPath()).toBe '/tmp'
|
||||
fsUtils.remove('/tmp/atom-test-save-sets-project-path')
|
||||
|
||||
describe "when an edit session is deserialized", ->
|
||||
it "emits an 'edit-session-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editSessionCreatedHandler')
|
||||
project.on 'edit-session-created', handler
|
||||
|
||||
editSession1 = project.open("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(project.getEditSessions().length).toBe 1
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
|
||||
editSession2 = deserialize(editSession1.serialize())
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(project.getEditSessions().length).toBe 2
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
expect(project.getEditSessions()[1]).toBe editSession2
|
||||
|
||||
describe ".open(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditSessionHandler] = []
|
||||
beforeEach ->
|
||||
|
||||
@@ -1,46 +1,79 @@
|
||||
TextMateScopeSelector = require 'text-mate-scope-selector'
|
||||
|
||||
describe "TextMateScopeSelector", ->
|
||||
it "matches the asterix", ->
|
||||
expect(new TextMateScopeSelector('*').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('*').matches(['b', 'c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c.d'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.*.c').matches(['a.b.d.c'])).toBeFalsy()
|
||||
describe ".matches(scopes)", ->
|
||||
it "matches the asterix", ->
|
||||
expect(new TextMateScopeSelector('*').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('*').matches(['b', 'c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c.d'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.*.c').matches(['a.b.d.c'])).toBeFalsy()
|
||||
|
||||
it "matches prefixes", ->
|
||||
expect(new TextMateScopeSelector('a').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a').matches(['a.b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a').matches(['abc'])).toBeFalsy()
|
||||
it "matches prefixes", ->
|
||||
expect(new TextMateScopeSelector('a').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a').matches(['a.b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.b').matches(['a.b.c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a').matches(['abc'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a.b-c').matches(['a.b-c.d'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.b').matches(['a.b-d'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('c++').matches(['c++'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('c++').matches(['c'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a_b_c').matches(['a_b_c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a_b_c').matches(['a_b'])).toBeFalsy()
|
||||
|
||||
it "matches disjunction", ->
|
||||
expect(new TextMateScopeSelector('a | b').matches(['b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a|b|c').matches(['c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a|b|c').matches(['d'])).toBeFalsy()
|
||||
it "matches filters", ->
|
||||
expect(new TextMateScopeSelector('R:g').matches(['g'])).toBeTruthy()
|
||||
|
||||
it "matches negation", ->
|
||||
expect(new TextMateScopeSelector('a - c').matches(['a', 'b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a-b').matches(['a', 'b'])).toBeFalsy()
|
||||
it "matches disjunction", ->
|
||||
expect(new TextMateScopeSelector('a | b').matches(['b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a|b|c').matches(['c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a|b|c').matches(['d'])).toBeFalsy()
|
||||
|
||||
it "matches conjunction", ->
|
||||
expect(new TextMateScopeSelector('a & b').matches(['b', 'a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a&b&c').matches(['c'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a&b&c').matches(['a', 'b', 'd'])).toBeFalsy()
|
||||
it "matches negation", ->
|
||||
expect(new TextMateScopeSelector('a - c').matches(['a', 'b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a - c').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('-c').matches(['b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('-c').matches(['c', 'b'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a-b').matches(['a', 'b'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a -b').matches(['a', 'b'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a -c').matches(['a', 'b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a-c').matches(['a', 'b'])).toBeFalsy()
|
||||
|
||||
it "matches composites", ->
|
||||
expect(new TextMateScopeSelector('a,b,c').matches(['b', 'c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a, b, c').matches(['d', 'e'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a, b, c').matches(['d', 'c.e'])).toBeTruthy()
|
||||
it "matches conjunction", ->
|
||||
expect(new TextMateScopeSelector('a & b').matches(['b', 'a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a&b&c').matches(['c'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a&b&c').matches(['a', 'b', 'd'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a & -b').matches(['a', 'b', 'd'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a & -b').matches(['a', 'd'])).toBeTruthy()
|
||||
|
||||
it "matches groups", ->
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['d'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['e'])).toBeFalsy()
|
||||
it "matches composites", ->
|
||||
expect(new TextMateScopeSelector('a,b,c').matches(['b', 'c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a, b, c').matches(['d', 'e'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a, b, c').matches(['d', 'c.e'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a,').matches(['a', 'c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a,').matches(['b', 'c'])).toBeFalsy()
|
||||
|
||||
it "matches paths", ->
|
||||
expect(new TextMateScopeSelector('a b').matches(['a', 'b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a b').matches(['b', 'a'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a c').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a b e').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy()
|
||||
it "matches groups", ->
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['d'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['e'])).toBeFalsy()
|
||||
|
||||
it "matches paths", ->
|
||||
expect(new TextMateScopeSelector('a b').matches(['a', 'b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a b').matches(['b', 'a'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a c').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a b e').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy()
|
||||
|
||||
describe ".toCssSelector()", ->
|
||||
it "converts the TextMate scope selector to a CSS selector", ->
|
||||
expect(new TextMateScopeSelector('a b c').toCssSelector()).toBe '.a .b .c'
|
||||
expect(new TextMateScopeSelector('a.b.c').toCssSelector()).toBe '.a.b.c'
|
||||
expect(new TextMateScopeSelector('*').toCssSelector()).toBe '*'
|
||||
expect(new TextMateScopeSelector('a - b').toCssSelector()).toBe '.a:not(.b)'
|
||||
expect(new TextMateScopeSelector('a & b').toCssSelector()).toBe '.a .b'
|
||||
expect(new TextMateScopeSelector('a & -b').toCssSelector()).toBe '.a:not(.b)'
|
||||
expect(new TextMateScopeSelector('a | b').toCssSelector()).toBe '.a, .b'
|
||||
expect(new TextMateScopeSelector('a - (b.c d)').toCssSelector()).toBe '.a:not(.b.c .d)'
|
||||
expect(new TextMateScopeSelector('a, b').toCssSelector()).toBe '.a, .b'
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
plist = require 'plist'
|
||||
TextMateTheme = require 'text-mate-theme'
|
||||
Theme = require 'theme'
|
||||
|
||||
describe "TextMateTheme", ->
|
||||
[theme, themePath] = []
|
||||
|
||||
beforeEach ->
|
||||
themePath = fsUtils.resolveOnLoadPath(path.join('fixtures', 'test.tmTheme'))
|
||||
theme = Theme.load(themePath)
|
||||
|
||||
afterEach ->
|
||||
theme.deactivate()
|
||||
|
||||
describe ".getRulesets()", ->
|
||||
rulesets = null
|
||||
|
||||
beforeEach ->
|
||||
rulesets = theme.getRulesets()
|
||||
|
||||
it "returns rulesets representing the theme's global style settings", ->
|
||||
expect(rulesets[0]).toEqual
|
||||
selector: '.editor, .editor .gutter'
|
||||
properties:
|
||||
'background-color': '#141414'
|
||||
'color': '#F8F8F8'
|
||||
|
||||
expect(rulesets[1]).toEqual
|
||||
selector: '.editor.is-focused .cursor'
|
||||
properties:
|
||||
'border-color': '#A7A7A7'
|
||||
|
||||
expect(rulesets[2]).toEqual
|
||||
selector: '.editor.is-focused .selection .region'
|
||||
properties:
|
||||
'background-color': "rgba(221, 240, 255, 0.2)"
|
||||
|
||||
it "returns an array of objects representing the theme's scope selectors", ->
|
||||
expect(rulesets[12]).toEqual
|
||||
comment: "Invalid – Deprecated"
|
||||
selector: ".invalid.deprecated"
|
||||
properties:
|
||||
'color': "#D2A8A1"
|
||||
'font-style': 'italic'
|
||||
'text-decoration': 'underline'
|
||||
|
||||
expect(rulesets[13]).toEqual
|
||||
comment: "Invalid – Illegal"
|
||||
selector: ".invalid.illegal"
|
||||
properties:
|
||||
'color': "#F8F8F8"
|
||||
'background-color': 'rgba(86, 45, 86, 0.75)'
|
||||
@@ -0,0 +1,32 @@
|
||||
$ = require 'jquery'
|
||||
|
||||
Theme = require 'theme'
|
||||
ThemeManager = require 'theme-manager'
|
||||
|
||||
describe "ThemeManager", ->
|
||||
describe "when the core.themes config value changes", ->
|
||||
it "add/removes stylesheets to reflect the new config value", ->
|
||||
themeManager = new ThemeManager()
|
||||
spyOn(themeManager, 'getUserStylesheetPath').andCallFake -> null
|
||||
themeManager.load()
|
||||
config.set('core.themes', [])
|
||||
expect($('style.userTheme').length).toBe 0
|
||||
|
||||
config.set('core.themes', ['atom-dark-syntax'])
|
||||
expect($('style.userTheme').length).toBe 1
|
||||
expect($('style.userTheme:eq(0)').attr('id')).toMatch /atom-dark-syntax.less$/
|
||||
|
||||
config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax'])
|
||||
expect($('style.userTheme').length).toBe 2
|
||||
expect($('style.userTheme:eq(0)').attr('id')).toMatch /atom-light-syntax.less$/
|
||||
expect($('style.userTheme:eq(1)').attr('id')).toMatch /atom-dark-syntax.less$/
|
||||
|
||||
config.set('core.themes', [])
|
||||
expect($('style.userTheme').length).toBe 0
|
||||
|
||||
describe "when a theme fails to load", ->
|
||||
it "logs a warning", ->
|
||||
themeManager = new ThemeManager()
|
||||
spyOn(console, 'warn')
|
||||
themeManager.loadTheme('a-theme-that-will-not-be-found')
|
||||
expect(console.warn).toHaveBeenCalled()
|
||||
@@ -3,7 +3,7 @@ fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
Theme = require 'theme'
|
||||
|
||||
describe "@load(name)", ->
|
||||
describe "Theme", ->
|
||||
theme = null
|
||||
|
||||
beforeEach ->
|
||||
@@ -12,48 +12,39 @@ describe "@load(name)", ->
|
||||
afterEach ->
|
||||
theme.deactivate()
|
||||
|
||||
describe "TextMateTheme", ->
|
||||
it "applies the theme's stylesheet to the current window", ->
|
||||
expect($(".editor").css("background-color")).not.toBe("rgb(20, 20, 20)")
|
||||
describe "when the theme is a file", ->
|
||||
it "loads and applies css", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('themes/theme-stylesheet.css')
|
||||
theme = new Theme(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "1234px"
|
||||
|
||||
themePath = fsUtils.resolveOnLoadPath(path.join('fixtures', 'test.tmTheme'))
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("background-color")).toBe("rgb(20, 20, 20)")
|
||||
it "parses, loads and applies less", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('themes/theme-stylesheet.less')
|
||||
theme = new Theme(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "4321px"
|
||||
|
||||
describe "AtomTheme", ->
|
||||
describe "when the theme is a file", ->
|
||||
it "loads and applies css", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('themes/theme-stylesheet.css')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "1234px"
|
||||
describe "when the theme contains a package.json file", ->
|
||||
it "loads and applies stylesheets from package.json in the correct order", ->
|
||||
expect($(".editor").css("padding-top")).not.toBe("101px")
|
||||
expect($(".editor").css("padding-right")).not.toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).not.toBe("103px")
|
||||
|
||||
it "parses, loads and applies less", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('themes/theme-stylesheet.less')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "4321px"
|
||||
themePath = project.resolve('themes/theme-with-package-file')
|
||||
theme = new Theme(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe("101px")
|
||||
expect($(".editor").css("padding-right")).toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).toBe("103px")
|
||||
|
||||
describe "when the theme contains a package.json file", ->
|
||||
it "loads and applies stylesheets from package.json in the correct order", ->
|
||||
expect($(".editor").css("padding-top")).not.toBe("101px")
|
||||
expect($(".editor").css("padding-right")).not.toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).not.toBe("103px")
|
||||
describe "when the theme does not contain a package.json file and is a directory", ->
|
||||
it "loads all stylesheet files in the directory", ->
|
||||
expect($(".editor").css("padding-top")).not.toBe "10px"
|
||||
expect($(".editor").css("padding-right")).not.toBe "20px"
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "30px"
|
||||
|
||||
themePath = project.resolve('themes/theme-with-package-file')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe("101px")
|
||||
expect($(".editor").css("padding-right")).toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).toBe("103px")
|
||||
|
||||
describe "when the theme does not contain a package.json file and is a directory", ->
|
||||
it "loads all stylesheet files in the directory", ->
|
||||
expect($(".editor").css("padding-top")).not.toBe "10px"
|
||||
expect($(".editor").css("padding-right")).not.toBe "20px"
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "30px"
|
||||
|
||||
themePath = project.resolve('themes/theme-without-package-file')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "10px"
|
||||
expect($(".editor").css("padding-right")).toBe "20px"
|
||||
expect($(".editor").css("padding-bottom")).toBe "30px"
|
||||
themePath = project.resolve('themes/theme-without-package-file')
|
||||
theme = new Theme(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "10px"
|
||||
expect($(".editor").css("padding-right")).toBe "20px"
|
||||
expect($(".editor").css("padding-bottom")).toBe "30px"
|
||||
|
||||
@@ -12,7 +12,11 @@ describe "TokenizedBuffer", ->
|
||||
TokenizedBuffer.prototype.chunkSize = 5
|
||||
jasmine.unspy(TokenizedBuffer.prototype, 'tokenizeInBackground')
|
||||
|
||||
startTokenizing = (tokenizedBuffer) ->
|
||||
tokenizedBuffer.setVisible(true)
|
||||
|
||||
fullyTokenize = (tokenizedBuffer) ->
|
||||
tokenizedBuffer.setVisible(true)
|
||||
advanceClock() while tokenizedBuffer.firstInvalidRow()?
|
||||
changeHandler?.reset()
|
||||
|
||||
@@ -20,7 +24,7 @@ describe "TokenizedBuffer", ->
|
||||
beforeEach ->
|
||||
buffer = project.bufferForPath('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer)
|
||||
tokenizedBuffer.setVisible(true)
|
||||
startTokenizing(tokenizedBuffer)
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
afterEach ->
|
||||
@@ -300,7 +304,7 @@ describe "TokenizedBuffer", ->
|
||||
atom.activatePackage('coffee-script-tmbundle', sync: true)
|
||||
buffer = project.bufferForPath('sample-with-tabs.coffee')
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer)
|
||||
tokenizedBuffer.setVisible(true)
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
tokenizedBuffer.destroy()
|
||||
@@ -333,7 +337,6 @@ describe "TokenizedBuffer", ->
|
||||
//\uD835\uDF97xyz
|
||||
"""
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer)
|
||||
tokenizedBuffer.setVisible(true)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
@@ -371,7 +374,6 @@ describe "TokenizedBuffer", ->
|
||||
buffer = project.bufferForPath(null, "<div class='name'><%= User.find(2).full_name %></div>")
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer)
|
||||
tokenizedBuffer.setGrammar(syntax.selectGrammar('test.erb'))
|
||||
tokenizedBuffer.setVisible(true)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
@@ -390,8 +392,25 @@ describe "TokenizedBuffer", ->
|
||||
it "returns the correct token (regression)", ->
|
||||
buffer = project.bufferForPath('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer)
|
||||
tokenizedBuffer.setVisible(true)
|
||||
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"]
|
||||
|
||||
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
|
||||
beforeEach ->
|
||||
buffer = project.bufferForPath('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer(buffer)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
describe "when the selector does not match the token at the position", ->
|
||||
it "returns a falsy value", ->
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.bogus', [0, 1])).toBeFalsy()
|
||||
|
||||
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]]
|
||||
|
||||
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)", ->
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.function', [1, 18])).toEqual [[1, 6], [1, 28]]
|
||||
|
||||
@@ -21,7 +21,10 @@ class AtomReporter extends View
|
||||
@div id: 'HTMLReporter', class: 'jasmine_reporter', =>
|
||||
@div outlet: 'specPopup', class: "spec-popup"
|
||||
@div outlet: "suites"
|
||||
@ul outlet: "symbolSummary", class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'coreHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'coreSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'packagesHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'packagesSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: "status", class: 'status', =>
|
||||
@div outlet: "time", class: 'time'
|
||||
@div outlet: "specCount", class: 'spec-count'
|
||||
@@ -118,9 +121,19 @@ class AtomReporter extends View
|
||||
@time.text "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
addSpecs: (specs) ->
|
||||
coreSpecs = 0
|
||||
packageSpecs = 0
|
||||
for spec in specs
|
||||
symbol = $$ -> @li class: "spec-summary pending spec-summary-#{spec.id}"
|
||||
@symbolSummary.append symbol
|
||||
if spec.coreSpec
|
||||
coreSpecs++
|
||||
@coreSummary.append symbol
|
||||
else
|
||||
packageSpecs++
|
||||
@packagesSummary.append symbol
|
||||
|
||||
@coreHeader.text("Core Specs (#{coreSpecs}):")
|
||||
@packagesHeader.text("Package Specs (#{packageSpecs}):")
|
||||
|
||||
specStarted: (spec) ->
|
||||
@runningSpecCount++
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports =
|
||||
activate: -> @activateCalled = true
|
||||
activateConfig: -> @activateConfigCalled = true
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
var quicksort = function () {
|
||||
/*
|
||||
this is a multiline comment
|
||||
it is, I promise
|
||||
*/
|
||||
var sort = function(items) {
|
||||
// This is a collection of
|
||||
// single line comments.
|
||||
// Wowza
|
||||
if (items.length <= 1) return items;
|
||||
var pivot = items.shift(), current, left = [], right = [];
|
||||
while(items.length > 0) {
|
||||
current = items.shift();
|
||||
current < pivot ? left.push(current) : right.push(current);
|
||||
}
|
||||
return sort(left).concat(pivot).concat(sort(right));
|
||||
};
|
||||
|
||||
return sort(Array.apply(this, arguments));
|
||||
};
|
||||
@@ -42,7 +42,6 @@ beforeEach ->
|
||||
window.git = Git.open(window.project.getPath())
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.windowMode = 'editor'
|
||||
atom.packageStates = {}
|
||||
spyOn(atom, 'saveWindowState')
|
||||
syntax.clearGrammarOverrides()
|
||||
|
||||
@@ -9,6 +9,8 @@ measure 'spec suite require time', ->
|
||||
for specPath in fsUtils.listTreeSync(fsUtils.resolveOnLoadPath("spec")) when /-spec\.coffee$/.test specPath
|
||||
require specPath
|
||||
|
||||
spec.coreSpec = true for spec in jasmine.getEnv().currentRunner().specs()
|
||||
|
||||
# Run extension specs
|
||||
for packageDirPath in config.packageDirPaths
|
||||
for packagePath in fsUtils.listSync(packageDirPath)
|
||||
|
||||
@@ -76,3 +76,42 @@ describe "underscore extensions", ->
|
||||
it "space separates the undasherized/capitalized versions of the namespace and event name", ->
|
||||
expect(_.humanizeEventName('space:final-frontier')).toBe 'Space: Final Frontier'
|
||||
expect(_.humanizeEventName('star-trek:the-next-generation')).toBe 'Star Trek: The Next Generation'
|
||||
|
||||
describe "_.deepExtend(objects...)", ->
|
||||
it "copies all key/values from each object into a new object", ->
|
||||
first =
|
||||
things:
|
||||
string: "oh"
|
||||
boolean: false
|
||||
anotherArray: ['a', 'b', 'c']
|
||||
object:
|
||||
first: 1
|
||||
second: 2
|
||||
|
||||
second =
|
||||
things:
|
||||
string: "cool"
|
||||
array: [1,2,3]
|
||||
anotherArray: ['aa', 'bb', 'cc']
|
||||
object:
|
||||
first: 1
|
||||
|
||||
result = _.deepExtend(first, second)
|
||||
|
||||
expect(result).toEqual
|
||||
things:
|
||||
string: "oh"
|
||||
boolean: false
|
||||
array: [1,2,3]
|
||||
anotherArray: ['a', 'b', 'c']
|
||||
object:
|
||||
first: 1
|
||||
second: 2
|
||||
|
||||
describe "_.isSubset(potentialSubset, potentialSuperset)", ->
|
||||
it "returns whether the first argument is a subset of the second", ->
|
||||
expect(_.isSubset([1, 2], [1, 2])).toBeTruthy()
|
||||
expect(_.isSubset([1, 2], [1, 2, 3])).toBeTruthy()
|
||||
expect(_.isSubset([], [1])).toBeTruthy()
|
||||
expect(_.isSubset([], [])).toBeTruthy()
|
||||
expect(_.isSubset([1, 2], [2, 3])).toBeFalsy()
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
Theme = require 'theme'
|
||||
|
||||
# Internal: Represents a theme that Atom can use.
|
||||
module.exports =
|
||||
class AtomTheme extends Theme
|
||||
|
||||
# Given a path, this loads it as a stylesheet.
|
||||
#
|
||||
# stylesheetPath - A {String} to a stylesheet
|
||||
loadStylesheet: (stylesheetPath)->
|
||||
@stylesheets[stylesheetPath] = window.loadStylesheet(stylesheetPath)
|
||||
|
||||
# Loads the stylesheets found in a `package.cson` file.
|
||||
load: ->
|
||||
if path.extname(@path) in ['.css', '.less']
|
||||
@loadStylesheet(@path)
|
||||
else
|
||||
metadataPath = fsUtils.resolveExtension(path.join(@path, 'package'), ['cson', 'json'])
|
||||
if fsUtils.isFileSync(metadataPath)
|
||||
stylesheetNames = fsUtils.readObjectSync(metadataPath)?.stylesheets
|
||||
if stylesheetNames
|
||||
for name in stylesheetNames
|
||||
filename = fsUtils.resolveExtension(path.join(@path, name), ['.css', '.less', ''])
|
||||
@loadStylesheet(filename)
|
||||
else
|
||||
@loadStylesheet(stylesheetPath) for stylesheetPath in fsUtils.listSync(@path, ['.css', '.less'])
|
||||
|
||||
super
|
||||
+3
-43
@@ -2,19 +2,19 @@ fsUtils = require 'fs-utils'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
Package = require 'package'
|
||||
Theme = require 'theme'
|
||||
ipc = require 'ipc'
|
||||
remote = require 'remote'
|
||||
crypto = require 'crypto'
|
||||
path = require 'path'
|
||||
dialog = remote.require 'dialog'
|
||||
telepath = require 'telepath'
|
||||
ThemeManager = require 'theme-manager'
|
||||
|
||||
window.atom =
|
||||
loadedThemes: []
|
||||
loadedPackages: {}
|
||||
activePackages: {}
|
||||
packageStates: {}
|
||||
themes: new ThemeManager()
|
||||
|
||||
getLoadSettings: ->
|
||||
remote.getCurrentWindow().loadSettings
|
||||
@@ -54,15 +54,6 @@ window.atom =
|
||||
getActivePackages: ->
|
||||
_.values(@activePackages)
|
||||
|
||||
activatePackageConfigs: ->
|
||||
@activatePackageConfig(pack.name) for pack in @getLoadedPackages()
|
||||
|
||||
activatePackageConfig: (name, options) ->
|
||||
if pack = @loadPackage(name, options)
|
||||
@activePackages[pack.name] = pack
|
||||
pack.activateConfig()
|
||||
pack
|
||||
|
||||
loadPackages: ->
|
||||
@loadPackage(name) for name in @getAvailablePackageNames() when not @isPackageDisabled(name)
|
||||
|
||||
@@ -135,34 +126,6 @@ window.atom =
|
||||
packages.push(metadata)
|
||||
packages
|
||||
|
||||
loadThemes: ->
|
||||
themeNames = config.get("core.themes")
|
||||
themeNames = [themeNames] unless _.isArray(themeNames)
|
||||
@loadTheme(themeName) for themeName in themeNames
|
||||
@loadUserStylesheet()
|
||||
|
||||
getAvailableThemePaths: ->
|
||||
themePaths = []
|
||||
for themeDirPath in config.themeDirPaths
|
||||
themePaths.push(fsUtils.listSync(themeDirPath, ['', '.tmTheme', '.css', 'less'])...)
|
||||
_.uniq(themePaths)
|
||||
|
||||
getAvailableThemeNames: ->
|
||||
path.basename(themePath).split('.')[0] for themePath in @getAvailableThemePaths()
|
||||
|
||||
loadTheme: (name) ->
|
||||
@loadedThemes.push Theme.load(name)
|
||||
|
||||
loadUserStylesheet: ->
|
||||
userStylesheetPath = fsUtils.resolve(path.join(config.configDirPath, 'user'), ['css', 'less'])
|
||||
if fsUtils.isFileSync(userStylesheetPath)
|
||||
userStyleesheetContents = loadStylesheet(userStylesheetPath)
|
||||
applyStylesheet(userStylesheetPath, userStyleesheetContents, 'userTheme')
|
||||
|
||||
getAtomThemeStylesheets: ->
|
||||
themeNames = config.get("core.themes") ? ['atom-dark-ui', 'atom-dark-syntax']
|
||||
themeNames = [themeNames] unless _.isArray(themeNames)
|
||||
|
||||
open: (url...) ->
|
||||
ipc.sendChannel('open', [url...])
|
||||
|
||||
@@ -172,9 +135,6 @@ window.atom =
|
||||
newWindow: ->
|
||||
ipc.sendChannel('new-window')
|
||||
|
||||
openConfig: ->
|
||||
ipc.sendChannel('open-config')
|
||||
|
||||
openWindow: (windowSettings) ->
|
||||
ipc.sendChannel('open-window', windowSettings)
|
||||
|
||||
@@ -237,7 +197,7 @@ window.atom =
|
||||
|
||||
getWindowStatePath: ->
|
||||
switch @windowMode
|
||||
when 'config', 'spec'
|
||||
when 'spec'
|
||||
filename = @windowMode
|
||||
when 'editor'
|
||||
{initialPath} = @getLoadSettings()
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
PackageConfigView = require 'package-config-view'
|
||||
ConfigPanel = require 'config-panel'
|
||||
packageManager = require 'package-manager'
|
||||
|
||||
### Internal ###
|
||||
module.exports =
|
||||
class AvailablePackagesConfigPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@div class: 'available-packages', =>
|
||||
@div outlet: 'loadingArea', class: 'alert alert-info loading-area', =>
|
||||
@span 'Loading available packages\u2026'
|
||||
@div outlet: 'errorArea', class: 'alert alert-error', =>
|
||||
@span 'Error fetching available packages.'
|
||||
@button outlet: 'retry', class: 'btn btn-mini btn-retry', 'Retry'
|
||||
@div outlet: 'packagesArea'
|
||||
|
||||
initialize: (@packageEventEmitter) ->
|
||||
@retry.on 'click', => @refresh()
|
||||
@refresh()
|
||||
|
||||
refresh: ->
|
||||
@loadingArea.show()
|
||||
@errorArea.hide()
|
||||
|
||||
packageManager.getAvailable (error, @packages=[]) =>
|
||||
@loadingArea.hide()
|
||||
if error?
|
||||
@errorArea.show()
|
||||
console.error(error.stack ? error)
|
||||
else
|
||||
@packagesArea.empty()
|
||||
for pack in @packages
|
||||
@packagesArea.append(new PackageConfigView(pack, @packageEventEmitter))
|
||||
@packageEventEmitter.trigger('available-packages-loaded', @packages)
|
||||
|
||||
getPackageCount: -> @packages.length
|
||||
@@ -1,58 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
{View} = require 'space-pen'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class ConfigPanel extends View
|
||||
initialize: ->
|
||||
@bindFormFields()
|
||||
@bindEditors()
|
||||
|
||||
bindFormFields: ->
|
||||
for input in @find('input[id]').toArray()
|
||||
do (input) =>
|
||||
input = $(input)
|
||||
name = input.attr('id')
|
||||
type = input.attr('type')
|
||||
|
||||
@observeConfig name, (value) ->
|
||||
if type is 'checkbox'
|
||||
input.attr('checked', value)
|
||||
else
|
||||
input.val(value) if value
|
||||
|
||||
input.on 'change', =>
|
||||
value = input.val()
|
||||
if type == 'checkbox'
|
||||
value = !!input.attr('checked')
|
||||
else
|
||||
value = @parseValue(type, value)
|
||||
config.set(name, value)
|
||||
|
||||
bindEditors: ->
|
||||
for editor in @find('.editor[id]').views()
|
||||
do (editor) =>
|
||||
name = editor.attr('id')
|
||||
type = editor.attr('type')
|
||||
|
||||
@observeConfig name, (value) ->
|
||||
return if value?.toString() == editor.getText()
|
||||
value ?= ""
|
||||
editor.setText(value.toString())
|
||||
|
||||
editor.getBuffer().on 'contents-modified', =>
|
||||
config.set(name, @parseValue(type, editor.getText()))
|
||||
|
||||
parseValue: (type, value) ->
|
||||
switch type
|
||||
when 'int'
|
||||
intValue = parseInt(value)
|
||||
value = intValue unless isNaN(intValue)
|
||||
when 'float'
|
||||
floatValue = parseFloat(value)
|
||||
value = floatValue unless isNaN(floatValue)
|
||||
value = undefined if value == ''
|
||||
value
|
||||
@@ -11,8 +11,6 @@ configDirPath = fsUtils.absolute("~/.atom")
|
||||
bundledPackagesDirPath = path.join(resourcePath, "src/packages")
|
||||
nodeModulesDirPath = path.join(resourcePath, "node_modules")
|
||||
bundledThemesDirPath = path.join(resourcePath, "themes")
|
||||
vendoredPackagesDirPath = path.join(resourcePath, "vendor/packages")
|
||||
vendoredThemesDirPath = path.join(resourcePath, "vendor/themes")
|
||||
userThemesDirPath = path.join(configDirPath, "themes")
|
||||
userPackagesDirPath = path.join(configDirPath, "packages")
|
||||
userStoragePath = path.join(configDirPath, ".storage")
|
||||
@@ -24,9 +22,9 @@ userStoragePath = path.join(configDirPath, ".storage")
|
||||
module.exports =
|
||||
class Config
|
||||
configDirPath: configDirPath
|
||||
themeDirPaths: [userThemesDirPath, bundledThemesDirPath, vendoredThemesDirPath]
|
||||
bundledPackageDirPaths: [vendoredPackagesDirPath, bundledPackagesDirPath, nodeModulesDirPath]
|
||||
packageDirPaths: [userPackagesDirPath, vendoredPackagesDirPath, bundledPackagesDirPath]
|
||||
themeDirPaths: [userThemesDirPath, bundledThemesDirPath]
|
||||
bundledPackageDirPaths: [bundledPackagesDirPath, nodeModulesDirPath]
|
||||
packageDirPaths: [userPackagesDirPath, bundledPackagesDirPath]
|
||||
userPackagesDirPath: userPackagesDirPath
|
||||
userStoragePath: userStoragePath
|
||||
lessSearchPaths: [path.join(resourcePath, 'static'), path.join(resourcePath, 'vendor')]
|
||||
@@ -100,6 +98,9 @@ class Config
|
||||
|
||||
### Public ###
|
||||
|
||||
getSettings: ->
|
||||
_.deepExtend(@settings, @defaultSettings)
|
||||
|
||||
# Retrieves the setting for the given key.
|
||||
#
|
||||
# keyPath - The {String} name of the key to retrieve
|
||||
@@ -116,7 +117,7 @@ class Config
|
||||
#
|
||||
# Returns the value from Atom's default settings, the user's configuration file,
|
||||
# or `NaN` if the key doesn't exist in either.
|
||||
getInt: (keyPath, defaultValueWhenFalsy) ->
|
||||
getInt: (keyPath) ->
|
||||
parseInt(@get(keyPath))
|
||||
|
||||
# Retrieves the setting for the given key as a positive integer.
|
||||
|
||||
+106
-38
@@ -128,6 +128,11 @@ class Cursor
|
||||
range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
|
||||
/^\s+$/.test @editSession.getTextInBufferRange(range)
|
||||
|
||||
isInsideWord: ->
|
||||
{row, column} = @getBufferPosition()
|
||||
range = [[row, column], [row, Infinity]]
|
||||
@editSession.getTextInBufferRange(range).search(@wordRegExp()) == 0
|
||||
|
||||
# Removes the setting for auto-scroll.
|
||||
clearAutoscroll: ->
|
||||
@needsAutoscroll = null
|
||||
@@ -169,29 +174,53 @@ class Cursor
|
||||
@editSession.lineForBufferRow(@getBufferRow())
|
||||
|
||||
# Moves the cursor up one screen row.
|
||||
moveUp: (rowCount = 1) ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
moveUp: (rowCount = 1, {moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
{ row, column } = range.start
|
||||
else
|
||||
{ row, column } = @getScreenPosition()
|
||||
|
||||
column = @goalColumn if @goalColumn?
|
||||
@setScreenPosition({row: row - rowCount, column: column})
|
||||
@goalColumn = column
|
||||
|
||||
# Moves the cursor down one screen row.
|
||||
moveDown: (rowCount = 1) ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
moveDown: (rowCount = 1, {moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
{ row, column } = range.end
|
||||
else
|
||||
{ row, column } = @getScreenPosition()
|
||||
|
||||
column = @goalColumn if @goalColumn?
|
||||
@setScreenPosition({row: row + rowCount, column: column})
|
||||
@goalColumn = column
|
||||
|
||||
# Moves the cursor left one screen column.
|
||||
moveLeft: ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
[row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity]
|
||||
@setScreenPosition({row, column})
|
||||
#
|
||||
# options -
|
||||
# moveToEndOfSelection: true will move to the left of the selection if a selection
|
||||
moveLeft: ({moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
@setScreenPosition(range.start)
|
||||
else
|
||||
{row, column} = @getScreenPosition()
|
||||
[row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity]
|
||||
@setScreenPosition({row, column})
|
||||
|
||||
# Moves the cursor right one screen column.
|
||||
moveRight: ->
|
||||
{ row, column } = @getScreenPosition()
|
||||
@setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
|
||||
#
|
||||
# options -
|
||||
# moveToEndOfSelection: true will move to the right of the selection if a selection
|
||||
moveRight: ({moveToEndOfSelection}={}) ->
|
||||
range = @marker.getScreenRange()
|
||||
if moveToEndOfSelection and not range.isEmpty()
|
||||
@setScreenPosition(range.end)
|
||||
else
|
||||
{ row, column } = @getScreenPosition()
|
||||
@setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)
|
||||
|
||||
# Moves the cursor to the top of the buffer.
|
||||
moveToTop: ->
|
||||
@@ -201,20 +230,20 @@ class Cursor
|
||||
moveToBottom: ->
|
||||
@setBufferPosition(@editSession.getEofBufferPosition())
|
||||
|
||||
# Moves the cursor to the beginning of the buffer line.
|
||||
# Moves the cursor to the beginning of the screen line.
|
||||
moveToBeginningOfLine: ->
|
||||
@setBufferPosition([@getBufferRow(), 0])
|
||||
@setScreenPosition([@getScreenRow(), 0])
|
||||
|
||||
# Moves the cursor to the beginning of the first character in the line.
|
||||
moveToFirstCharacterOfLine: ->
|
||||
position = @getBufferPosition()
|
||||
scanRange = @getCurrentLineBufferRange()
|
||||
newPosition = null
|
||||
@editSession.scanInBufferRange /^\s*/, scanRange, ({range}) =>
|
||||
newPosition = range.end
|
||||
return unless newPosition
|
||||
newPosition = [position.row, 0] if newPosition.isEqual(position)
|
||||
@setBufferPosition(newPosition)
|
||||
{row, column} = @getScreenPosition()
|
||||
screenline = @editSession.lineForScreenRow(row)
|
||||
|
||||
goalColumn = screenline.text.search(/\S/)
|
||||
return if goalColumn == -1
|
||||
|
||||
goalColumn = 0 if goalColumn == column
|
||||
@setScreenPosition([row, goalColumn])
|
||||
|
||||
# Moves the cursor to the beginning of the buffer line, skipping all whitespace.
|
||||
skipLeadingWhitespace: ->
|
||||
@@ -228,7 +257,7 @@ class Cursor
|
||||
|
||||
# Moves the cursor to the end of the buffer line.
|
||||
moveToEndOfLine: ->
|
||||
@setBufferPosition([@getBufferRow(), Infinity])
|
||||
@setScreenPosition([@getScreenRow(), Infinity])
|
||||
|
||||
# Moves the cursor to the beginning of the word.
|
||||
moveToBeginningOfWord: ->
|
||||
@@ -244,6 +273,16 @@ class Cursor
|
||||
if position = @getBeginningOfNextWordBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
# Moves the cursor to the previous word boundary.
|
||||
moveToPreviousWordBoundary: ->
|
||||
if position = @getPreviousWordBoundaryBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
# Moves the cursor to the next word boundary.
|
||||
moveToNextWordBoundary: ->
|
||||
if position = @getMoveNextWordBoundaryBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
# Retrieves the buffer position of where the current word starts.
|
||||
#
|
||||
# options - A hash with one option:
|
||||
@@ -265,6 +304,49 @@ class Cursor
|
||||
|
||||
beginningOfWordPosition or currentBufferPosition
|
||||
|
||||
# Retrieves buffer position of previous word boiundry. It might be on the
|
||||
# current word, or the previous word.
|
||||
getPreviousWordBoundaryBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
previousNonBlankRow = @editSession.buffer.previousNonBlankRow(currentBufferPosition.row)
|
||||
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
|
||||
|
||||
beginningOfWordPosition = null
|
||||
@editSession.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
if range.start.row < currentBufferPosition.row and currentBufferPosition.column > 0
|
||||
# force it to stop at the beginning of each line
|
||||
beginningOfWordPosition = new Point(currentBufferPosition.row, 0)
|
||||
else if range.end.isLessThan(currentBufferPosition)
|
||||
beginningOfWordPosition = range.end
|
||||
else
|
||||
beginningOfWordPosition = range.start
|
||||
|
||||
if not beginningOfWordPosition?.isEqual(currentBufferPosition)
|
||||
stop()
|
||||
|
||||
beginningOfWordPosition or currentBufferPosition
|
||||
|
||||
# Retrieves buffer position of previous word boiundry. It might be on the
|
||||
# current word, or the previous word.
|
||||
getMoveNextWordBoundaryBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
scanRange = [currentBufferPosition, @editSession.getEofBufferPosition()]
|
||||
|
||||
endOfWordPosition = null
|
||||
@editSession.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
if range.start.row > currentBufferPosition.row
|
||||
# force it to stop at the beginning of each line
|
||||
endOfWordPosition = new Point(range.start.row, 0)
|
||||
else if range.start.isGreaterThan(currentBufferPosition)
|
||||
endOfWordPosition = range.start
|
||||
else
|
||||
endOfWordPosition = range.end
|
||||
|
||||
if not endOfWordPosition?.isEqual(currentBufferPosition)
|
||||
stop()
|
||||
|
||||
endOfWordPosition or currentBufferPosition
|
||||
|
||||
# Retrieves the buffer position of where the current word ends.
|
||||
#
|
||||
# options - A hash with one option:
|
||||
@@ -293,7 +375,7 @@ class Cursor
|
||||
# Returns a {Range}.
|
||||
getBeginningOfNextWordBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
start = if @isSurroundedByWhitespace() then currentBufferPosition else @getEndOfCurrentWordBufferPosition()
|
||||
start = if @isInsideWord() then @getEndOfCurrentWordBufferPosition() else currentBufferPosition
|
||||
scanRange = [start, @editSession.getEofBufferPosition()]
|
||||
|
||||
beginningOfNextWordPosition = null
|
||||
@@ -327,21 +409,7 @@ class Cursor
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getCurrentParagraphBufferRange: ->
|
||||
row = @getBufferRow()
|
||||
return unless /\w/.test(@editSession.lineForBufferRow(row))
|
||||
|
||||
startRow = row
|
||||
while startRow > 0
|
||||
break unless /\w/.test(@editSession.lineForBufferRow(startRow - 1))
|
||||
startRow--
|
||||
|
||||
endRow = row
|
||||
lastRow = @editSession.getLastBufferRow()
|
||||
while endRow < lastRow
|
||||
break unless /\w/.test(@editSession.lineForBufferRow(endRow + 1))
|
||||
endRow++
|
||||
|
||||
new Range([startRow, 0], [endRow, @editSession.lineLengthForBufferRow(endRow)])
|
||||
@editSession.languageMode.rowRangeForParagraphAtBufferRow(@getBufferRow())
|
||||
|
||||
# Retrieves the characters that constitute a word preceeding the current cursor position.
|
||||
#
|
||||
|
||||
@@ -282,6 +282,9 @@ class DisplayBuffer
|
||||
scopesForBufferPosition: (bufferPosition) ->
|
||||
@tokenizedBuffer.scopesForPosition(bufferPosition)
|
||||
|
||||
bufferRangeForScopeAtPosition: (selector, position) ->
|
||||
@tokenizedBuffer.bufferRangeForScopeAtPosition(selector, position)
|
||||
|
||||
# Retrieves the grammar's token for a buffer position.
|
||||
#
|
||||
# bufferPosition - A {Point} in the {Buffer}.
|
||||
|
||||
@@ -36,13 +36,13 @@ class EditSession
|
||||
|
||||
constructor: (optionsOrState) ->
|
||||
if optionsOrState instanceof telepath.Document
|
||||
project.editSessions.push(this)
|
||||
@state = optionsOrState
|
||||
{tabLength, softTabs, @softWrap} = @state.toObject()
|
||||
@buffer = deserialize(@state.get('buffer'))
|
||||
@setScrollTop(@state.get('scrollTop'))
|
||||
@setScrollLeft(@state.get('scrollLeft'))
|
||||
cursorScreenPosition = @state.getObject('cursorScreenPosition')
|
||||
registerEditSession = true
|
||||
else
|
||||
{@buffer, tabLength, softTabs, @softWrap} = optionsOrState
|
||||
@state = telepath.Document.create
|
||||
@@ -82,6 +82,8 @@ class EditSession
|
||||
when 'scrollLeft'
|
||||
@trigger 'scroll-left-changed', newValue
|
||||
|
||||
project.addEditSession(this) if registerEditSession
|
||||
|
||||
getViewClass: ->
|
||||
require 'editor'
|
||||
|
||||
@@ -383,9 +385,12 @@ class EditSession
|
||||
# {Delegates to: DisplayBuffer.bufferRowsForScreenRows}
|
||||
bufferRowsForScreenRows: (startRow, endRow) -> @displayBuffer.bufferRowsForScreenRows(startRow, endRow)
|
||||
|
||||
# {Delegates to: DisplayBuffer.bufferRowsForScreenRows}
|
||||
# {Delegates to: DisplayBuffer.scopesForBufferPosition}
|
||||
scopesForBufferPosition: (bufferPosition) -> @displayBuffer.scopesForBufferPosition(bufferPosition)
|
||||
|
||||
bufferRangeForScopeAtCursor: (selector) ->
|
||||
@displayBuffer.bufferRangeForScopeAtPosition(selector, @getCursorBufferPosition())
|
||||
|
||||
# {Delegates to: DisplayBuffer.tokenForBufferPosition}
|
||||
tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition)
|
||||
|
||||
@@ -531,6 +536,9 @@ class EditSession
|
||||
unfoldAll: ->
|
||||
@languageMode.unfoldAll()
|
||||
|
||||
foldAllAtIndentLevel: (indentLevel) ->
|
||||
@languageMode.foldAllAtIndentLevel(indentLevel)
|
||||
|
||||
# Folds the current row.
|
||||
foldCurrentRow: ->
|
||||
bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
|
||||
@@ -1037,6 +1045,8 @@ class EditSession
|
||||
getTextInBufferRange: (range) ->
|
||||
@buffer.getTextInRange(range)
|
||||
|
||||
setTextInBufferRange: (range, text) -> @getBuffer().change(range, text)
|
||||
|
||||
# Retrieves the range for the current paragraph.
|
||||
#
|
||||
# A paragraph is defined as a block of text surrounded by empty lines.
|
||||
@@ -1055,19 +1065,19 @@ class EditSession
|
||||
|
||||
# Moves every cursor up one row.
|
||||
moveCursorUp: (lineCount) ->
|
||||
@moveCursors (cursor) -> cursor.moveUp(lineCount)
|
||||
@moveCursors (cursor) -> cursor.moveUp(lineCount, moveToEndOfSelection: true)
|
||||
|
||||
# Moves every cursor down one row.
|
||||
moveCursorDown: (lineCount) ->
|
||||
@moveCursors (cursor) -> cursor.moveDown(lineCount)
|
||||
@moveCursors (cursor) -> cursor.moveDown(lineCount, moveToEndOfSelection: true)
|
||||
|
||||
# Moves every cursor left one column.
|
||||
moveCursorLeft: ->
|
||||
@moveCursors (cursor) -> cursor.moveLeft()
|
||||
@moveCursors (cursor) -> cursor.moveLeft(moveToEndOfSelection: true)
|
||||
|
||||
# Moves every cursor right one column.
|
||||
moveCursorRight: ->
|
||||
@moveCursors (cursor) -> cursor.moveRight()
|
||||
@moveCursors (cursor) -> cursor.moveRight(moveToEndOfSelection: true)
|
||||
|
||||
# Moves every cursor to the top of the buffer.
|
||||
moveCursorToTop: ->
|
||||
@@ -1101,6 +1111,12 @@ class EditSession
|
||||
moveCursorToBeginningOfNextWord: ->
|
||||
@moveCursors (cursor) -> cursor.moveToBeginningOfNextWord()
|
||||
|
||||
moveCursorToPreviousWordBoundary: ->
|
||||
@moveCursors (cursor) -> cursor.moveToPreviousWordBoundary()
|
||||
|
||||
moveCursorToNextWordBoundary: ->
|
||||
@moveCursors (cursor) -> cursor.moveToNextWordBoundary()
|
||||
|
||||
# Internal:
|
||||
moveCursors: (fn) ->
|
||||
fn(cursor) for cursor in @getCursors()
|
||||
@@ -1154,6 +1170,12 @@ class EditSession
|
||||
selectToEndOfLine: ->
|
||||
@expandSelectionsForward (selection) => selection.selectToEndOfLine()
|
||||
|
||||
selectToPreviousWordBoundary: ->
|
||||
@expandSelectionsBackward (selection) => selection.selectToPreviousWordBoundary()
|
||||
|
||||
selectToNextWordBoundary: ->
|
||||
@expandSelectionsForward (selection) => selection.selectToNextWordBoundary()
|
||||
|
||||
# Selects the current line.
|
||||
selectLine: ->
|
||||
@expandSelectionsForward (selection) => selection.selectLine()
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
ConfigPanel = require 'config-panel'
|
||||
Editor = require 'editor'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class EditorConfigPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@form class: 'form-horizontal', =>
|
||||
@fieldset =>
|
||||
@legend "Editor Settings"
|
||||
|
||||
@div class: 'control-group', =>
|
||||
@label class: 'control-label', "Font Size:"
|
||||
@div class: 'controls', =>
|
||||
@subview "fontSizeEditor", new Editor(mini: true, attributes: {id: 'editor.fontSize', type: 'int', style: 'width: 4em'})
|
||||
|
||||
@div class: 'control-group', =>
|
||||
@label class: 'control-label', "Font Family:"
|
||||
@div class: 'controls', =>
|
||||
@subview "fontFamilyEditor", new Editor(mini: true, attributes: {id: 'editor.fontFamily', type: 'string'})
|
||||
|
||||
@div class: 'control-group', =>
|
||||
@div class: 'controls', =>
|
||||
@div class: 'checkbox', =>
|
||||
@label for: 'editor.autoIndent', =>
|
||||
@input id: 'editor.autoIndent', type: 'checkbox'
|
||||
@text 'Auto-Indent'
|
||||
|
||||
@div class: 'controls', =>
|
||||
@div class: 'checkbox', =>
|
||||
@label for: 'editor.autoIndentOnPaste', =>
|
||||
@input id: 'editor.autoIndentOnPaste', type: 'checkbox'
|
||||
@text 'Auto-Indent on Paste'
|
||||
|
||||
@div class: 'controls', =>
|
||||
@div class: 'checkbox', =>
|
||||
@label for: 'editor.normalizeIndentOnPaste', =>
|
||||
@input id: 'editor.normalizeIndentOnPaste', type: 'checkbox'
|
||||
@text 'Normalize Indent on Paste'
|
||||
|
||||
@div class: 'controls', =>
|
||||
@div class: 'checkbox', =>
|
||||
@label for: 'editor.showLineNumbers', =>
|
||||
@input id: 'editor.showLineNumbers', type: 'checkbox'
|
||||
@text 'Show Line Numbers'
|
||||
|
||||
@div class: 'controls', =>
|
||||
@div class: 'checkbox', =>
|
||||
@label for: 'editor.showInvisibles', =>
|
||||
@input id: 'editor.showInvisibles', type: 'checkbox'
|
||||
@text 'Show Invisible Characters'
|
||||
|
||||
@div class: 'controls', =>
|
||||
@div class: 'checkbox', =>
|
||||
@label for: 'editor.showIndentGuide', =>
|
||||
@input id: 'editor.showIndentGuide', type: 'checkbox'
|
||||
@text 'Show Indent Guide'
|
||||
|
||||
@div class: 'control-group', =>
|
||||
@label class: 'control-label', for: 'editor.preferredLineLength', "Preferred Line Length:"
|
||||
@div class: 'controls', =>
|
||||
@subview "preferredLineLengthEditor", new Editor(mini: true, attributes: {id: 'editor.preferredLineLength', type: 'int', style: 'width: 4em'})
|
||||
|
||||
@div class: 'control-group', =>
|
||||
@label class: 'control-label', for: 'editor.nonWordCharacters', "Non-Word Characters:"
|
||||
@div class: 'controls', =>
|
||||
@subview "nonWordCharactersEditor", new Editor(mini: true, attributes: {id: 'editor.nonWordCharacters', type: 'string'})
|
||||
+36
-2
@@ -141,11 +141,15 @@ class Editor extends View
|
||||
'editor:move-to-beginning-of-word': @moveCursorToBeginningOfWord
|
||||
'editor:move-to-end-of-word': @moveCursorToEndOfWord
|
||||
'editor:move-to-beginning-of-next-word': @moveCursorToBeginningOfNextWord
|
||||
'editor:move-to-previous-word-boundary': @moveCursorToPreviousWordBoundary
|
||||
'editor:move-to-next-word-boundary': @moveCursorToNextWordBoundary
|
||||
'editor:select-to-end-of-line': @selectToEndOfLine
|
||||
'editor:select-to-beginning-of-line': @selectToBeginningOfLine
|
||||
'editor:select-to-end-of-word': @selectToEndOfWord
|
||||
'editor:select-to-beginning-of-word': @selectToBeginningOfWord
|
||||
'editor:select-to-beginning-of-next-word': @selectToBeginningOfNextWord
|
||||
'editor:select-to-next-word-boundary': @selectToNextWordBoundary
|
||||
'editor:select-to-previous-word-boundary': @selectToPreviousWordBoundary
|
||||
'editor:select-to-first-character-of-line': @selectToFirstCharacterOfLine
|
||||
'editor:add-selection-below': @addSelectionBelow
|
||||
'editor:add-selection-above': @addSelectionAbove
|
||||
@@ -175,6 +179,15 @@ class Editor extends View
|
||||
'editor:fold-current-row': @foldCurrentRow
|
||||
'editor:unfold-current-row': @unfoldCurrentRow
|
||||
'editor:fold-selection': @foldSelection
|
||||
'editor:fold-at-indent-level-1': => @foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': => @foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': => @foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': => @foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': => @foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': => @foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': => @foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': => @foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': => @foldAllAtIndentLevel(8)
|
||||
'editor:toggle-line-comments': @toggleLineCommentsInSelection
|
||||
'editor:log-cursor-scope': @logCursorScope
|
||||
'editor:checkout-head-revision': @checkoutHead
|
||||
@@ -238,6 +251,12 @@ class Editor extends View
|
||||
# {Delegates to: EditSession.moveCursorToFirstCharacterOfLine}
|
||||
moveCursorToFirstCharacterOfLine: -> @activeEditSession.moveCursorToFirstCharacterOfLine()
|
||||
|
||||
# {Delegates to: EditSession.moveCursorToPreviousWordBoundary}
|
||||
moveCursorToPreviousWordBoundary: -> @activeEditSession.moveCursorToPreviousWordBoundary()
|
||||
|
||||
# {Delegates to: EditSession.moveCursorToNextWordBoundary}
|
||||
moveCursorToNextWordBoundary: -> @activeEditSession.moveCursorToNextWordBoundary()
|
||||
|
||||
# {Delegates to: EditSession.moveCursorToEndOfLine}
|
||||
moveCursorToEndOfLine: -> @activeEditSession.moveCursorToEndOfLine()
|
||||
|
||||
@@ -334,6 +353,12 @@ class Editor extends View
|
||||
# {Delegates to: EditSession.selectToEndOfLine}
|
||||
selectToEndOfLine: -> @activeEditSession.selectToEndOfLine()
|
||||
|
||||
# {Delegates to: EditSession.selectToPreviousWordBoundary}
|
||||
selectToPreviousWordBoundary: -> @activeEditSession.selectToPreviousWordBoundary()
|
||||
|
||||
# {Delegates to: EditSession.selectToNextWordBoundary}
|
||||
selectToNextWordBoundary: -> @activeEditSession.selectToNextWordBoundary()
|
||||
|
||||
# {Delegates to: EditSession.addSelectionBelow}
|
||||
addSelectionBelow: -> @activeEditSession.addSelectionBelow()
|
||||
|
||||
@@ -460,6 +485,8 @@ class Editor extends View
|
||||
# {Delegates to: EditSession.isFoldedAtCursorRow}
|
||||
isFoldedAtCursorRow: -> @activeEditSession.isFoldedAtCursorRow()
|
||||
|
||||
foldAllAtIndentLevel: (indentLevel) -> @activeEditSession.foldAllAtIndentLevel(indentLevel)
|
||||
|
||||
# {Delegates to: EditSession.lineForScreenRow}
|
||||
lineForScreenRow: (screenRow) -> @activeEditSession.lineForScreenRow(screenRow)
|
||||
|
||||
@@ -622,7 +649,10 @@ class Editor extends View
|
||||
false if @isFocused
|
||||
|
||||
@renderedLines.on 'mousedown', '.fold.line', (e) =>
|
||||
@activeEditSession.destroyFoldWithId($(e.currentTarget).attr('fold-id'))
|
||||
id = $(e.currentTarget).attr('fold-id')
|
||||
marker = @activeEditSession.displayBuffer.getMarker(id)
|
||||
@activeEditSession.setCursorBufferPosition(marker.getBufferRange().start)
|
||||
@activeEditSession.destroyFoldWithId(id)
|
||||
false
|
||||
|
||||
@renderedLines.on 'mousedown', (e) =>
|
||||
@@ -1469,7 +1499,7 @@ class Editor extends View
|
||||
range = document.createRange()
|
||||
range.setEnd(textNode, offset)
|
||||
range.collapse()
|
||||
leftPixels = range.getClientRects()[0].left - @scrollView.offset().left + @scrollLeft()
|
||||
leftPixels = range.getClientRects()[0].left - Math.floor(@scrollView.offset().left) + Math.floor(@scrollLeft())
|
||||
range.detach()
|
||||
leftPixels
|
||||
|
||||
@@ -1530,6 +1560,10 @@ class Editor extends View
|
||||
reloadGrammar: ->
|
||||
@activeEditSession.reloadGrammar()
|
||||
|
||||
# {Delegates to: EditSession.scopesForBufferPosition}
|
||||
scopesForBufferPosition: (bufferPosition) ->
|
||||
@activeEditSession.scopesForBufferPosition(bufferPosition)
|
||||
|
||||
# Copies the current file path to the native clipboard.
|
||||
copyPathToPasteboard: ->
|
||||
path = @getPath()
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
ConfigPanel = require 'config-panel'
|
||||
{$$} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class GeneralConfigPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@form id: 'general-config-panel', class: 'form-horizontal', =>
|
||||
@fieldset =>
|
||||
@legend "General Settings"
|
||||
|
||||
@div class: 'control-group', =>
|
||||
@div class: 'checkbox', =>
|
||||
@label for: 'core.hideGitIgnoredFiles', =>
|
||||
@input id: 'core.hideGitIgnoredFiles', type: 'checkbox'
|
||||
@text 'Hide Git-Ignored Files'
|
||||
|
||||
@div class: 'checkbox', =>
|
||||
@label for: 'core.autosave', =>
|
||||
@input id: 'core.autosave', type: 'checkbox'
|
||||
@text 'Auto-Save on Focus Change'
|
||||
@@ -1,44 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
ConfigPanel = require 'config-panel'
|
||||
PackageConfigView = require 'package-config-view'
|
||||
packageManager = require 'package-manager'
|
||||
|
||||
### Internal ###
|
||||
module.exports =
|
||||
class InstalledPackagesConfigPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@div class: 'installed-packages', =>
|
||||
@div outlet: 'loadingArea', class: 'alert alert-info loading-area', =>
|
||||
@span 'Loading installed packages\u2026'
|
||||
@div outlet: 'packagesArea'
|
||||
|
||||
initialize: (@packageEventEmitter) ->
|
||||
@packages = _.sortBy(atom.getAvailablePackageMetadata(), 'name')
|
||||
packageManager.renderMarkdownInMetadata @packages, =>
|
||||
@loadingArea.hide()
|
||||
for pack in @packages
|
||||
@packagesArea.append(new PackageConfigView(pack, @packageEventEmitter))
|
||||
@packageEventEmitter.trigger 'installed-packages-loaded', [@packages]
|
||||
|
||||
@packageEventEmitter.on 'package-installed', (error, pack) =>
|
||||
@addPackage(pack) unless error?
|
||||
@packageEventEmitter.on 'package-uninstalled', (error, pack) =>
|
||||
@removePackage(pack) unless error?
|
||||
|
||||
removePackage: ({name}) ->
|
||||
@packages = _.reject @packages, (pack) -> pack.name is name
|
||||
@packagesArea.children("[name=#{name}]").remove()
|
||||
|
||||
addPackage: (pack) ->
|
||||
@packages.push(pack)
|
||||
@packages = _.sortBy(@packages, 'name')
|
||||
index = @packages.indexOf(pack)
|
||||
view = new PackageConfigView(pack, @packageEventEmitter)
|
||||
if index is 0
|
||||
@packagesArea.prepend(view)
|
||||
else if index is @packages.length - 1
|
||||
@packagesArea.append(view)
|
||||
else
|
||||
@packagesArea.children(":eq(#{index})").before(view)
|
||||
|
||||
getPackageCount: -> @packages.length
|
||||
@@ -31,7 +31,6 @@ class Keymap
|
||||
|
||||
bindDefaultKeys: ->
|
||||
$(document).command 'new-window', => atom.newWindow()
|
||||
$(document).command 'open-user-configuration', => atom.openConfig()
|
||||
$(document).command 'open', => atom.open()
|
||||
$(document).command 'open-dev', => atom.openDev()
|
||||
$(document).command 'toggle-dev-tools', => atom.toggleDevTools()
|
||||
|
||||
@@ -5,10 +5,21 @@
|
||||
'tab': 'editor:indent'
|
||||
'meta-=': 'editor:auto-indent'
|
||||
'meta-d': 'editor:delete-line'
|
||||
|
||||
'ctrl-[': 'editor:fold-current-row'
|
||||
'ctrl-]': 'editor:unfold-current-row'
|
||||
'ctrl-{': 'editor:fold-all'
|
||||
'ctrl-}': 'editor:unfold-all'
|
||||
'ctrl-meta-1': 'editor:fold-at-indent-level-1'
|
||||
'ctrl-meta-2': 'editor:fold-at-indent-level-2'
|
||||
'ctrl-meta-3': 'editor:fold-at-indent-level-3'
|
||||
'ctrl-meta-4': 'editor:fold-at-indent-level-4'
|
||||
'ctrl-meta-5': 'editor:fold-at-indent-level-5'
|
||||
'ctrl-meta-6': 'editor:fold-at-indent-level-6'
|
||||
'ctrl-meta-7': 'editor:fold-at-indent-level-7'
|
||||
'ctrl-meta-8': 'editor:fold-at-indent-level-8'
|
||||
'ctrl-meta-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
'alt-shift-down': 'editor:add-selection-below'
|
||||
'alt-shift-up': 'editor:add-selection-above'
|
||||
'alt-meta-ctrl-f': 'editor:fold-selection'
|
||||
|
||||
@@ -91,7 +91,6 @@ class LanguageMode
|
||||
for currentRow in [0..@buffer.getLastRow()]
|
||||
[startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
continue unless startRow?
|
||||
|
||||
@editSession.createFold(startRow, endRow)
|
||||
|
||||
# Unfolds all the foldable lines in the buffer.
|
||||
@@ -99,6 +98,18 @@ class LanguageMode
|
||||
for row in [@buffer.getLastRow()..0]
|
||||
fold.destroy() for fold in @editSession.displayBuffer.foldsStartingAtBufferRow(row)
|
||||
|
||||
# Fold all comment and code blocks at a given indentLevel
|
||||
#
|
||||
# indentLevel - A {Number} indicating indentLevel; 0 based.
|
||||
foldAllAtIndentLevel: (indentLevel) ->
|
||||
for currentRow in [0..@buffer.getLastRow()]
|
||||
[startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
continue unless startRow?
|
||||
|
||||
# assumption: startRow will always be the min indent level for the entire range
|
||||
if @editSession.indentationForBufferRow(startRow) == indentLevel
|
||||
@editSession.createFold(startRow, endRow)
|
||||
|
||||
# Given a buffer row, creates a fold at it.
|
||||
#
|
||||
# bufferRow - A {Number} indicating the buffer row
|
||||
@@ -106,9 +117,7 @@ class LanguageMode
|
||||
# Returns the new {Fold}.
|
||||
foldBufferRow: (bufferRow) ->
|
||||
for currentRow in [bufferRow..0]
|
||||
rowRange = @rowRangeForCommentAtBufferRow(currentRow)
|
||||
rowRange ?= @rowRangeForFoldAtBufferRow(currentRow)
|
||||
[startRow, endRow] = rowRange ? []
|
||||
[startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
continue unless startRow? and startRow <= bufferRow <= endRow
|
||||
fold = @editSession.displayBuffer.largestFoldStartingAtBufferRow(startRow)
|
||||
return @editSession.createFold(startRow, endRow) unless fold
|
||||
@@ -119,13 +128,33 @@ class LanguageMode
|
||||
unfoldBufferRow: (bufferRow) ->
|
||||
@editSession.displayBuffer.largestFoldContainingBufferRow(bufferRow)?.destroy()
|
||||
|
||||
doesBufferRowStartFold: (bufferRow) ->
|
||||
return false if @editSession.isBufferRowBlank(bufferRow)
|
||||
nextNonEmptyRow = @editSession.nextNonBlankBufferRow(bufferRow)
|
||||
return false unless nextNonEmptyRow?
|
||||
@editSession.indentationForBufferRow(nextNonEmptyRow) > @editSession.indentationForBufferRow(bufferRow)
|
||||
|
||||
# Find the row range for a fold at a given bufferRow. Will handle comments
|
||||
# and code.
|
||||
#
|
||||
# bufferRow - A {Number} indicating the buffer row
|
||||
#
|
||||
# Returns an {Array} of the [startRow, endRow]. Returns null if no range.
|
||||
rowRangeForFoldAtBufferRow: (bufferRow) ->
|
||||
rowRange = @rowRangeForCommentAtBufferRow(bufferRow)
|
||||
rowRange ?= @rowRangeForCodeFoldAtBufferRow(bufferRow)
|
||||
rowRange
|
||||
|
||||
rowRangeForCommentAtBufferRow: (bufferRow) ->
|
||||
return unless @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment()
|
||||
|
||||
startRow = bufferRow
|
||||
for currentRow in [bufferRow-1..0]
|
||||
break if @buffer.isRowBlank(currentRow)
|
||||
break unless @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
|
||||
startRow = currentRow
|
||||
endRow = bufferRow
|
||||
for currentRow in [bufferRow+1..@buffer.getLastRow()]
|
||||
break if @buffer.isRowBlank(currentRow)
|
||||
break unless @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
|
||||
endRow = currentRow
|
||||
return [startRow, endRow] if startRow isnt endRow
|
||||
|
||||
rowRangeForCodeFoldAtBufferRow: (bufferRow) ->
|
||||
return null unless @doesBufferRowStartFold(bufferRow)
|
||||
|
||||
startIndentLevel = @editSession.indentationForBufferRow(bufferRow)
|
||||
@@ -142,20 +171,43 @@ class LanguageMode
|
||||
|
||||
[bufferRow, foldEndRow]
|
||||
|
||||
rowRangeForCommentAtBufferRow: (row) ->
|
||||
return unless @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(row).isComment()
|
||||
doesBufferRowStartFold: (bufferRow) ->
|
||||
return false if @editSession.isBufferRowBlank(bufferRow)
|
||||
nextNonEmptyRow = @editSession.nextNonBlankBufferRow(bufferRow)
|
||||
return false unless nextNonEmptyRow?
|
||||
@editSession.indentationForBufferRow(nextNonEmptyRow) > @editSession.indentationForBufferRow(bufferRow)
|
||||
|
||||
startRow = row
|
||||
for currentRow in [row-1..0]
|
||||
break if @buffer.isRowBlank(currentRow)
|
||||
break unless @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
|
||||
startRow = currentRow
|
||||
endRow = row
|
||||
for currentRow in [row+1..@buffer.getLastRow()]
|
||||
break if @buffer.isRowBlank(currentRow)
|
||||
break unless @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
|
||||
endRow = currentRow
|
||||
return [startRow, endRow] if startRow isnt endRow
|
||||
# Find a row range for a 'paragraph' around specified bufferRow.
|
||||
# Right now, a paragraph is a block of text bounded by and empty line or a
|
||||
# block of text that is not the same type (comments next to source code).
|
||||
rowRangeForParagraphAtBufferRow: (bufferRow) ->
|
||||
return unless /\w/.test(@editSession.lineForBufferRow(bufferRow))
|
||||
|
||||
isRowComment = (row) =>
|
||||
@editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(row).isComment()
|
||||
|
||||
if isRowComment(bufferRow)
|
||||
isOriginalRowComment = true
|
||||
range = @rowRangeForCommentAtBufferRow(bufferRow)
|
||||
[firstRow, lastRow] = range or [bufferRow, bufferRow]
|
||||
else
|
||||
isOriginalRowComment = false
|
||||
[firstRow, lastRow] = [0, @editSession.getLastBufferRow()-1]
|
||||
|
||||
startRow = bufferRow
|
||||
while startRow > firstRow
|
||||
break if isRowComment(startRow - 1) != isOriginalRowComment
|
||||
break unless /\w/.test(@editSession.lineForBufferRow(startRow - 1))
|
||||
startRow--
|
||||
|
||||
endRow = bufferRow
|
||||
lastRow = @editSession.getLastBufferRow()
|
||||
while endRow < lastRow
|
||||
break if isRowComment(endRow + 1) != isOriginalRowComment
|
||||
break unless /\w/.test(@editSession.lineForBufferRow(endRow + 1))
|
||||
endRow++
|
||||
|
||||
new Range([startRow, 0], [endRow, @editSession.lineLengthForBufferRow(endRow)])
|
||||
|
||||
# Given a buffer row, this returns a suggested indentation level.
|
||||
#
|
||||
@@ -170,7 +222,7 @@ class LanguageMode
|
||||
return currentIndentLevel unless increaseIndentRegex = @increaseIndentRegexForScopes(scopes)
|
||||
|
||||
currentLine = @buffer.lineForRow(bufferRow)
|
||||
precedingRow = @buffer.previousNonBlankRow(bufferRow)
|
||||
precedingRow = if bufferRow > 0 then bufferRow - 1 else null
|
||||
return currentIndentLevel unless precedingRow?
|
||||
|
||||
precedingLine = @buffer.lineForRow(precedingRow)
|
||||
@@ -180,7 +232,7 @@ class LanguageMode
|
||||
return desiredIndentLevel unless decreaseIndentRegex = @decreaseIndentRegexForScopes(scopes)
|
||||
desiredIndentLevel -= 1 if decreaseIndentRegex.test(currentLine)
|
||||
|
||||
desiredIndentLevel
|
||||
Math.max(desiredIndentLevel, 0)
|
||||
|
||||
# Calculate a minimum indent level for a range of lines excluding empty lines.
|
||||
#
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
ConfigPanel = require 'config-panel'
|
||||
InstalledPackagesConfigPanel = require 'installed-packages-config-panel'
|
||||
AvailablePackagesConfigPanel = require 'available-packages-config-panel'
|
||||
_ = require 'underscore'
|
||||
EventEmitter = require 'event-emitter'
|
||||
|
||||
### Internal ###
|
||||
class PackageEventEmitter
|
||||
_.extend PackageEventEmitter.prototype, EventEmitter
|
||||
|
||||
module.exports =
|
||||
class PackageConfigPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@div class: 'package-panel', =>
|
||||
@legend 'Packages'
|
||||
@ul class: 'nav nav-tabs', =>
|
||||
@li class: 'active', outlet: 'installedLink', =>
|
||||
@a 'Installed', =>
|
||||
@span class: 'badge pull-right', outlet: 'installedCount'
|
||||
@li outlet: 'availableLink', =>
|
||||
@a 'Available', =>
|
||||
@span class: 'badge pull-right', outlet: 'availableCount'
|
||||
|
||||
initialize: ->
|
||||
@packageEventEmitter = new PackageEventEmitter()
|
||||
@installed = new InstalledPackagesConfigPanel(@packageEventEmitter)
|
||||
@available = new AvailablePackagesConfigPanel(@packageEventEmitter)
|
||||
@append(@installed, @available)
|
||||
|
||||
@available.hide()
|
||||
|
||||
@installedLink.on 'click', =>
|
||||
@availableLink.removeClass('active')
|
||||
@available.hide()
|
||||
@installedLink.addClass('active')
|
||||
@installed.show()
|
||||
|
||||
@availableLink.on 'click', =>
|
||||
@installedLink.removeClass('active')
|
||||
@installed.hide()
|
||||
@availableLink.addClass('active')
|
||||
@available.show()
|
||||
|
||||
@packageEventEmitter.on 'installed-packages-loaded package-installed package-uninstalled', =>
|
||||
@installedCount.text(@installed.getPackageCount())
|
||||
|
||||
@packageEventEmitter.on 'available-packages-loaded', =>
|
||||
@availableCount.text(@available.getPackageCount())
|
||||
@@ -77,9 +77,9 @@ class PaneContainer extends View
|
||||
newPane.focus()
|
||||
|
||||
itemDestroyed: (item) ->
|
||||
state = item.serialize?()
|
||||
state.uri ?= item.getUri?()
|
||||
@destroyedItemStates.push(state) if state?
|
||||
if state = item.serialize?()
|
||||
state.uri ?= item.getUri?()
|
||||
@destroyedItemStates.push(state)
|
||||
|
||||
itemAdded: (item) ->
|
||||
itemUri = item.getUri?()
|
||||
|
||||
@@ -179,6 +179,10 @@ class Project
|
||||
getEditSessions: ->
|
||||
new Array(@editSessions...)
|
||||
|
||||
addEditSession: (editSession) ->
|
||||
@editSessions.push editSession
|
||||
@trigger 'edit-session-created', editSession
|
||||
|
||||
### Public ###
|
||||
|
||||
# Removes an {EditSession} association from the project.
|
||||
@@ -297,8 +301,7 @@ class Project
|
||||
options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
|
||||
options.buffer = buffer
|
||||
editSession = new EditSession(options)
|
||||
@editSessions.push editSession
|
||||
@trigger 'edit-session-created', editSession
|
||||
@addEditSession(editSession)
|
||||
editSession
|
||||
|
||||
defaultEditSessionOptions: ->
|
||||
|
||||
@@ -31,9 +31,9 @@ class Range
|
||||
# columnDelta - A {Number} indicating how far from the starting {Point} the range's column should be
|
||||
#
|
||||
# Returns the new {Range}.
|
||||
@fromPointWithDelta: (point, rowDelta, columnDelta) ->
|
||||
pointA = Point.fromObject(point)
|
||||
pointB = new Point(point.row + rowDelta, point.column + columnDelta)
|
||||
@fromPointWithDelta: (pointA, rowDelta, columnDelta) ->
|
||||
pointA = Point.fromObject(pointA)
|
||||
pointB = new Point(pointA.row + rowDelta, pointA.column + columnDelta)
|
||||
new Range(pointA, pointB)
|
||||
|
||||
# Creates a new `Range` object based on two {Point}s.
|
||||
|
||||
@@ -21,7 +21,9 @@ class RootView extends View
|
||||
@version: 1
|
||||
|
||||
@configDefaults:
|
||||
autosave: false
|
||||
ignoredNames: [".git", ".svn", ".DS_Store"]
|
||||
excludeVcsIgnoredPaths: false
|
||||
disabledPackages: []
|
||||
themes: ['atom-dark-ui', 'atom-dark-syntax']
|
||||
|
||||
|
||||
@@ -218,6 +218,14 @@ class Selection
|
||||
selectToBeginningOfNextWord: ->
|
||||
@modifySelection => @cursor.moveToBeginningOfNextWord()
|
||||
|
||||
# Selects text to the previous word boundary.
|
||||
selectToPreviousWordBoundary: ->
|
||||
@modifySelection => @cursor.moveToPreviousWordBoundary()
|
||||
|
||||
# Selects text to the next word boundary.
|
||||
selectToNextWordBoundary: ->
|
||||
@modifySelection => @cursor.moveToNextWordBoundary()
|
||||
|
||||
# Moves the selection down one row.
|
||||
addSelectionBelow: ->
|
||||
range = (@goalBufferRange ? @getBufferRange()).copy()
|
||||
|
||||
@@ -5,6 +5,7 @@ Specificity = require 'specificity'
|
||||
fsUtils = require 'fs-utils'
|
||||
EventEmitter = require 'event-emitter'
|
||||
NullGrammar = require 'null-grammar'
|
||||
TextMateScopeSelector = require 'text-mate-scope-selector'
|
||||
|
||||
### Internal ###
|
||||
|
||||
@@ -131,12 +132,6 @@ class Syntax
|
||||
element[0]
|
||||
|
||||
cssSelectorFromScopeSelector: (scopeSelector) ->
|
||||
scopeSelector.split(', ').map((commaFragment) ->
|
||||
commaFragment.split(' ').map((spaceFragment) ->
|
||||
spaceFragment.split('.').map((dotFragment) ->
|
||||
'.' + dotFragment.replace(/\+/g, '\\+')
|
||||
).join('')
|
||||
).join(' ')
|
||||
).join(', ')
|
||||
new TextMateScopeSelector(scopeSelector).toCssSelector()
|
||||
|
||||
_.extend(Syntax.prototype, EventEmitter)
|
||||
|
||||
@@ -27,6 +27,8 @@ class TextMatePackage extends Package
|
||||
@metadata = {@name}
|
||||
|
||||
load: ({sync}={}) ->
|
||||
@metadata = Package.loadMetadata(@path, true)
|
||||
|
||||
if sync
|
||||
@loadGrammarsSync()
|
||||
@loadScopedPropertiesSync()
|
||||
@@ -100,8 +102,9 @@ class TextMatePackage extends Package
|
||||
selector = syntax.cssSelectorFromScopeSelector(scope) if scope?
|
||||
@scopedProperties.push({selector, properties})
|
||||
|
||||
for {selector, properties} in @scopedProperties
|
||||
syntax.addProperties(@path, selector, properties)
|
||||
if @isActive()
|
||||
for {selector, properties} in @scopedProperties
|
||||
syntax.addProperties(@path, selector, properties)
|
||||
|
||||
loadScopedProperties: (callback) ->
|
||||
scopedProperties = []
|
||||
@@ -157,5 +160,6 @@ class TextMatePackage extends Package
|
||||
increaseIndentPattern: textMateSettings.increaseIndentPattern
|
||||
decreaseIndentPattern: textMateSettings.decreaseIndentPattern
|
||||
foldEndPattern: textMateSettings.foldingStopMarker
|
||||
completions: textMateSettings.completions
|
||||
)
|
||||
{ editor: editorProperties } if _.size(editorProperties) > 0
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
_ = require 'underscore'
|
||||
|
||||
### Internal ###
|
||||
|
||||
class SegmentMatcher
|
||||
constructor: (segment) ->
|
||||
@segment = segment.join('')
|
||||
@segment = _.flatten(segment).join('')
|
||||
|
||||
matches: (scope) ->
|
||||
scope is @segment
|
||||
matches: (scope) -> scope is @segment
|
||||
|
||||
toCssSelector: ->
|
||||
@segment.split('.').map((dotFragment) ->
|
||||
'.' + dotFragment.replace(/\+/g, '\\+')
|
||||
).join('')
|
||||
|
||||
class TrueMatcher
|
||||
constructor: ->
|
||||
|
||||
matches: ->
|
||||
true
|
||||
matches: -> true
|
||||
|
||||
toCssSelector: -> '*'
|
||||
|
||||
class ScopeMatcher
|
||||
constructor: (first, others) ->
|
||||
@@ -27,6 +34,9 @@ class ScopeMatcher
|
||||
|
||||
true
|
||||
|
||||
toCssSelector: ->
|
||||
@segments.map((matcher) -> matcher.toCssSelector()).join('')
|
||||
|
||||
class PathMatcher
|
||||
constructor: (first, others) ->
|
||||
@matchers = [first]
|
||||
@@ -40,33 +50,44 @@ class PathMatcher
|
||||
return true unless matcher?
|
||||
false
|
||||
|
||||
toCssSelector: ->
|
||||
@matchers.map((matcher) -> matcher.toCssSelector()).join(' ')
|
||||
|
||||
class OrMatcher
|
||||
constructor: (@left, @right) ->
|
||||
|
||||
matches: (scopes) ->
|
||||
@left.matches(scopes) or @right.matches(scopes)
|
||||
matches: (scopes) -> @left.matches(scopes) or @right.matches(scopes)
|
||||
|
||||
toCssSelector: -> "#{@left.toCssSelector()}, #{@right.toCssSelector()}"
|
||||
|
||||
class AndMatcher
|
||||
constructor: (@left, @right) ->
|
||||
|
||||
matches: (scopes) ->
|
||||
@left.matches(scopes) and @right.matches(scopes)
|
||||
matches: (scopes) -> @left.matches(scopes) and @right.matches(scopes)
|
||||
|
||||
toCssSelector: ->
|
||||
if @right instanceof NegateMatcher
|
||||
"#{@left.toCssSelector()}#{@right.toCssSelector()}"
|
||||
else
|
||||
"#{@left.toCssSelector()} #{@right.toCssSelector()}"
|
||||
|
||||
class NegateMatcher
|
||||
constructor: (@left, @right) ->
|
||||
constructor: (@matcher) ->
|
||||
|
||||
matches: (scopes) ->
|
||||
@left.matches(scopes) and not @right.matches(scopes)
|
||||
matches: (scopes) -> not @matcher.matches(scopes)
|
||||
|
||||
toCssSelector: -> ":not(#{@matcher.toCssSelector()})"
|
||||
|
||||
class CompositeMatcher
|
||||
constructor: (left, operator, right) ->
|
||||
switch operator
|
||||
when '|' then @matcher = new OrMatcher(left, right)
|
||||
when '&' then @matcher = new AndMatcher(left, right)
|
||||
when '-' then @matcher = new NegateMatcher(left, right)
|
||||
when '-' then @matcher = new AndMatcher(left, new NegateMatcher(right))
|
||||
|
||||
matches: (scopes) ->
|
||||
@matcher.matches(scopes)
|
||||
matches: (scopes) -> @matcher.matches(scopes)
|
||||
|
||||
toCssSelector: -> @matcher.toCssSelector()
|
||||
|
||||
module.exports = {
|
||||
AndMatcher
|
||||
|
||||
@@ -7,7 +7,7 @@ start = _ selector:(selector) _ {
|
||||
}
|
||||
|
||||
segment
|
||||
= _ segment:[a-zA-Z0-9]+ _ {
|
||||
= _ segment:([a-zA-Z0-9+_]+[a-zA-Z0-9-+_]*) _ {
|
||||
return new matchers.SegmentMatcher(segment);
|
||||
}
|
||||
|
||||
@@ -25,23 +25,52 @@ path
|
||||
return new matchers.PathMatcher(first, others);
|
||||
}
|
||||
|
||||
expression
|
||||
= path
|
||||
|
||||
/ "(" _ selector:selector _ ")" {
|
||||
group
|
||||
= "(" _ selector:selector _ ")" {
|
||||
return selector;
|
||||
}
|
||||
|
||||
filter
|
||||
= prefix:([LRB]":") _ group:group {
|
||||
return group;
|
||||
}
|
||||
|
||||
/ prefix:([LRB]":") _ path:path {
|
||||
return path;
|
||||
}
|
||||
|
||||
expression
|
||||
= "-" _ filter:filter _ {
|
||||
return new matchers.NegateMatcher(filter);
|
||||
}
|
||||
|
||||
/ "-" _ group:group _ {
|
||||
return new matchers.NegateMatcher(group);
|
||||
}
|
||||
|
||||
/ "-" _ path:path _ {
|
||||
return new matchers.NegateMatcher(path);
|
||||
}
|
||||
|
||||
/ filter
|
||||
|
||||
/ group
|
||||
|
||||
/ path
|
||||
|
||||
composite
|
||||
= left:expression _ operator:[|&-] _ right:composite {
|
||||
= left:expression _ operator:[|&-] _ right:composite {
|
||||
return new matchers.CompositeMatcher(left, operator, right);
|
||||
}
|
||||
|
||||
/ expression
|
||||
|
||||
selector
|
||||
= left:composite _ "," _ right:selector {
|
||||
return new matchers.OrMatcher(left, right);
|
||||
= left:composite _ "," _ right:selector? {
|
||||
if (right)
|
||||
return new matchers.OrMatcher(left, right);
|
||||
else
|
||||
return left;
|
||||
}
|
||||
|
||||
/ composite
|
||||
|
||||
@@ -28,3 +28,5 @@ class TextMateScopeSelector
|
||||
# Return a {Boolean}.
|
||||
matches: (scopes) ->
|
||||
@matcher.matches(scopes)
|
||||
|
||||
toCssSelector: -> @matcher.toCssSelector()
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
fsUtils = require 'fs-utils'
|
||||
plist = require 'plist'
|
||||
Theme = require 'theme'
|
||||
|
||||
### Internal ###
|
||||
|
||||
module.exports =
|
||||
class TextMateTheme extends Theme
|
||||
@testPath: (path) ->
|
||||
/\.(tmTheme|plist)$/.test(path)
|
||||
|
||||
constructor: (@path) ->
|
||||
super
|
||||
@rulesets = []
|
||||
|
||||
load: ->
|
||||
@buildRulesets()
|
||||
@stylesheets[@path] = @getStylesheet()
|
||||
super
|
||||
|
||||
buildRulesets: ->
|
||||
{settings} = plist.parseFileSync(@path)
|
||||
@buildGlobalSettingsRulesets(settings[0])
|
||||
@buildScopeSelectorRulesets(settings[1..])
|
||||
|
||||
getStylesheet: ->
|
||||
lines = []
|
||||
for {selector, properties} in @getRulesets()
|
||||
lines.push("#{selector} {")
|
||||
for name, value of properties
|
||||
lines.push " #{name}: #{value};"
|
||||
lines.push("}\n")
|
||||
lines.join("\n")
|
||||
|
||||
getRulesets: -> @rulesets
|
||||
|
||||
buildGlobalSettingsRulesets: ({settings}) ->
|
||||
{ background, foreground, caret, selection, lineHighlight } = settings
|
||||
|
||||
@rulesets.push
|
||||
selector: '.editor, .editor .gutter'
|
||||
properties:
|
||||
'background-color': @translateColor(background)
|
||||
'color': @translateColor(foreground)
|
||||
|
||||
@rulesets.push
|
||||
selector: '.editor.is-focused .cursor'
|
||||
properties:
|
||||
'border-color': @translateColor(caret)
|
||||
|
||||
@rulesets.push
|
||||
selector: '.editor.is-focused .selection .region'
|
||||
properties:
|
||||
'background-color': @translateColor(selection)
|
||||
|
||||
@rulesets.push
|
||||
selector: '.editor.is-focused .line-number.cursor-line-no-selection, .editor.is-focused .line.cursor-line'
|
||||
properties:
|
||||
'background-color': @translateColor(lineHighlight)
|
||||
|
||||
buildScopeSelectorRulesets: (scopeSelectorSettings) ->
|
||||
for { name, scope, settings } in scopeSelectorSettings
|
||||
continue unless scope
|
||||
@rulesets.push
|
||||
comment: name
|
||||
selector: @translateScopeSelector(scope)
|
||||
properties: @translateScopeSelectorSettings(settings)
|
||||
|
||||
translateScopeSelector: (textmateScopeSelector) ->
|
||||
scopes = textmateScopeSelector.split(/\s+/).map (scope) -> '.' + scope
|
||||
scopes.join(' ')
|
||||
|
||||
translateScopeSelectorSettings: ({ foreground, background, fontStyle }) ->
|
||||
properties = {}
|
||||
|
||||
if fontStyle
|
||||
fontStyles = fontStyle.split(/\s+/)
|
||||
properties['font-weight'] = 'bold' if _.contains(fontStyles, 'bold')
|
||||
properties['font-style'] = 'italic' if _.contains(fontStyles, 'italic')
|
||||
properties['text-decoration'] = 'underline' if _.contains(fontStyles, 'underline')
|
||||
|
||||
properties['color'] = @translateColor(foreground) if foreground
|
||||
properties['background-color'] = @translateColor(background) if background
|
||||
properties
|
||||
|
||||
translateColor: (textmateColor) ->
|
||||
if textmateColor.length <= 7
|
||||
textmateColor
|
||||
else
|
||||
r = parseInt(textmateColor[1..2], 16)
|
||||
g = parseInt(textmateColor[3..4], 16)
|
||||
b = parseInt(textmateColor[5..6], 16)
|
||||
a = parseInt(textmateColor[7..8], 16)
|
||||
a = Math.round((a / 255.0) * 100) / 100
|
||||
|
||||
"rgba(#{r}, #{g}, #{b}, #{a})"
|
||||
@@ -0,0 +1,47 @@
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore'
|
||||
|
||||
fsUtils = require 'fs-utils'
|
||||
Theme = require 'theme'
|
||||
|
||||
module.exports =
|
||||
class ThemeManager
|
||||
constructor: ->
|
||||
@loadedThemes = []
|
||||
|
||||
getAvailablePaths: ->
|
||||
themePaths = []
|
||||
for themeDirPath in config.themeDirPaths
|
||||
themePaths.push(fsUtils.listSync(themeDirPath, ['', '.css', 'less'])...)
|
||||
_.uniq(themePaths)
|
||||
|
||||
getAvailableNames: ->
|
||||
path.basename(themePath).split('.')[0] for themePath in @getAvailablePaths()
|
||||
|
||||
load: ->
|
||||
config.observe 'core.themes', (themeNames) =>
|
||||
removeStylesheet(@userStylesheetPath) if @userStylesheetPath?
|
||||
theme.deactivate() while theme = @loadedThemes.pop()
|
||||
themeNames = [themeNames] unless _.isArray(themeNames)
|
||||
@loadTheme(themeName) for themeName in themeNames
|
||||
@loadUserStylesheet()
|
||||
|
||||
loadTheme: (name) ->
|
||||
try
|
||||
@loadedThemes.push(new Theme(name))
|
||||
catch error
|
||||
console.warn("Failed to load theme #{name}", error.stack ? error)
|
||||
|
||||
getUserStylesheetPath: ->
|
||||
stylesheetPath = fsUtils.resolve(path.join(config.configDirPath, 'user'), ['css', 'less'])
|
||||
if fsUtils.isFileSync(stylesheetPath)
|
||||
stylesheetPath
|
||||
else
|
||||
null
|
||||
|
||||
loadUserStylesheet: ->
|
||||
if userStylesheetPath = @getUserStylesheetPath()
|
||||
@userStylesheetPath = userStylesheetPath
|
||||
userStylesheetContents = loadStylesheet(userStylesheetPath)
|
||||
applyStylesheet(userStylesheetPath, userStylesheetContents, 'userTheme')
|
||||
+31
-23
@@ -1,38 +1,46 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
|
||||
### Internal ###
|
||||
|
||||
module.exports =
|
||||
class Theme
|
||||
@stylesheets: null
|
||||
|
||||
@load: (name) ->
|
||||
TextMateTheme = require 'text-mate-theme'
|
||||
AtomTheme = require 'atom-theme'
|
||||
stylesheetPath: null
|
||||
stylesheets: null
|
||||
|
||||
constructor: (name) ->
|
||||
@stylesheets = []
|
||||
if fsUtils.exists(name)
|
||||
path = name
|
||||
@stylesheetPath = name
|
||||
else
|
||||
path = fsUtils.resolve(config.themeDirPaths..., name, ['', '.tmTheme', '.css', 'less'])
|
||||
@stylesheetPath = fsUtils.resolve(config.themeDirPaths..., name, ['', '.css', 'less'])
|
||||
|
||||
throw new Error("No theme exists named '#{name}'") unless path
|
||||
throw new Error("No theme exists named '#{name}'") unless @stylesheetPath
|
||||
|
||||
theme =
|
||||
if TextMateTheme.testPath(path)
|
||||
new TextMateTheme(path)
|
||||
else
|
||||
new AtomTheme(path)
|
||||
|
||||
theme.load()
|
||||
theme
|
||||
|
||||
constructor: (@path) ->
|
||||
@stylesheets = {}
|
||||
@load()
|
||||
|
||||
# Loads the stylesheets found in a `package.cson` file.
|
||||
load: ->
|
||||
for stylesheetPath, stylesheetContent of @stylesheets
|
||||
applyStylesheet(stylesheetPath, stylesheetContent, 'userTheme')
|
||||
if path.extname(@stylesheetPath) in ['.css', '.less']
|
||||
@loadStylesheet(@stylesheetPath)
|
||||
else
|
||||
metadataPath = fsUtils.resolveExtension(path.join(@stylesheetPath, 'package'), ['cson', 'json'])
|
||||
if fsUtils.isFileSync(metadataPath)
|
||||
stylesheetNames = fsUtils.readObjectSync(metadataPath)?.stylesheets
|
||||
if stylesheetNames
|
||||
for name in stylesheetNames
|
||||
filename = fsUtils.resolveExtension(path.join(@stylesheetPath, name), ['.css', '.less', ''])
|
||||
@loadStylesheet(filename)
|
||||
else
|
||||
@loadStylesheet(stylesheetPath) for stylesheetPath in fsUtils.listSync(@stylesheetPath, ['.css', '.less'])
|
||||
|
||||
# Given a path, this loads it as a stylesheet.
|
||||
#
|
||||
# stylesheetPath - A {String} to a stylesheet
|
||||
loadStylesheet: (stylesheetPath) ->
|
||||
@stylesheets.push stylesheetPath
|
||||
content = window.loadStylesheet(stylesheetPath)
|
||||
window.applyStylesheet(stylesheetPath, content, 'userTheme')
|
||||
|
||||
deactivate: ->
|
||||
for stylesheetPath, stylesheetContent of @stylesheets
|
||||
removeStylesheet(stylesheetPath)
|
||||
window.removeStylesheet(stylesheetPath) for stylesheetPath in @stylesheets
|
||||
|
||||
@@ -109,6 +109,12 @@ class Token
|
||||
isOnlyWhitespace: ->
|
||||
not /\S/.test(@value)
|
||||
|
||||
matchesScopeSelector: (selector) ->
|
||||
targetClasses = selector.replace(/^\.?/, '').split('.')
|
||||
_.any @scopes, (scope) ->
|
||||
scopeClasses = scope.split('.')
|
||||
_.isSubset(targetClasses, scopeClasses)
|
||||
|
||||
getValueAsHtml: ({invisibles, hasLeadingWhitespace, hasTrailingWhitespace, hasIndentGuide})->
|
||||
invisibles ?= {}
|
||||
html = @value
|
||||
|
||||
@@ -183,9 +183,13 @@ class TokenizedBuffer
|
||||
{ tokens, ruleStack } = @grammar.tokenizeLine(line, ruleStack, row is 0)
|
||||
new TokenizedLine({tokens, ruleStack, @tabLength, lineEnding})
|
||||
|
||||
# FIXME: benogle says: These are actually buffer rows as all buffer rows are
|
||||
# accounted for in @tokenizedLines
|
||||
lineForScreenRow: (row) ->
|
||||
@linesForScreenRows(row, row)[0]
|
||||
|
||||
# FIXME: benogle says: These are actually buffer rows as all buffer rows are
|
||||
# accounted for in @tokenizedLines
|
||||
linesForScreenRows: (startRow, endRow) ->
|
||||
@tokenizedLines[startRow..endRow]
|
||||
|
||||
@@ -196,8 +200,34 @@ class TokenizedBuffer
|
||||
@tokenForPosition(position).scopes
|
||||
|
||||
tokenForPosition: (position) ->
|
||||
{row, column} = Point.fromObject(position)
|
||||
@tokenizedLines[row].tokenAtBufferColumn(column)
|
||||
|
||||
tokenStartPositionForPosition: (position) ->
|
||||
{row, column} = Point.fromObject(position)
|
||||
column = @tokenizedLines[row].tokenStartColumnForBufferColumn(column)
|
||||
new Point(row, column)
|
||||
|
||||
bufferRangeForScopeAtPosition: (selector, position) ->
|
||||
position = Point.fromObject(position)
|
||||
@tokenizedLines[position.row].tokenAtBufferColumn(position.column)
|
||||
tokenizedLine = @tokenizedLines[position.row]
|
||||
startIndex = tokenizedLine.tokenIndexAtBufferColumn(position.column)
|
||||
|
||||
for index in [startIndex..0]
|
||||
token = tokenizedLine.tokenAtIndex(index)
|
||||
break unless token.matchesScopeSelector(selector)
|
||||
firstToken = token
|
||||
|
||||
for index in [startIndex...tokenizedLine.getTokenCount()]
|
||||
token = tokenizedLine.tokenAtIndex(index)
|
||||
break unless token.matchesScopeSelector(selector)
|
||||
lastToken = token
|
||||
|
||||
return unless firstToken? and lastToken?
|
||||
|
||||
startColumn = tokenizedLine.bufferColumnForToken(firstToken)
|
||||
endColumn = tokenizedLine.bufferColumnForToken(lastToken) + lastToken.bufferDelta
|
||||
new Range([position.row, startColumn], [position.row, endColumn])
|
||||
|
||||
destroy: ->
|
||||
@unsubscribe()
|
||||
|
||||
@@ -90,11 +90,22 @@ class TokenizedLine
|
||||
@lineEnding is null
|
||||
|
||||
tokenAtBufferColumn: (bufferColumn) ->
|
||||
@tokens[@tokenIndexAtBufferColumn(bufferColumn)]
|
||||
|
||||
tokenIndexAtBufferColumn: (bufferColumn) ->
|
||||
delta = 0
|
||||
for token, index in @tokens
|
||||
delta += token.bufferDelta
|
||||
return index if delta > bufferColumn
|
||||
index - 1
|
||||
|
||||
tokenStartColumnForBufferColumn: (bufferColumn) ->
|
||||
delta = 0
|
||||
for token in @tokens
|
||||
delta += token.bufferDelta
|
||||
return token if delta > bufferColumn
|
||||
token
|
||||
nextDelta = delta + token.bufferDelta
|
||||
break if nextDelta > bufferColumn
|
||||
delta = nextDelta
|
||||
delta
|
||||
|
||||
breakOutAtomicTokens: (inputTokens, tabLength) ->
|
||||
outputTokens = []
|
||||
@@ -111,3 +122,16 @@ class TokenizedLine
|
||||
for scope in token.scopes
|
||||
return true if _.contains(scope.split('.'), 'comment')
|
||||
break
|
||||
false
|
||||
|
||||
tokenAtIndex: (index) ->
|
||||
@tokens[index]
|
||||
|
||||
getTokenCount: ->
|
||||
@tokens.length
|
||||
|
||||
bufferColumnForToken: (targetToken) ->
|
||||
column = 0
|
||||
for token in @tokens
|
||||
return column if token is targetToken
|
||||
column += token.bufferDelta
|
||||
|
||||
+5
-27
@@ -50,7 +50,7 @@ window.startEditorWindow = ->
|
||||
restoreDimensions()
|
||||
config.load()
|
||||
keymap.loadBundledKeymaps()
|
||||
atom.loadThemes()
|
||||
atom.themes.load()
|
||||
atom.loadPackages()
|
||||
deserializeEditorWindow()
|
||||
atom.activatePackages()
|
||||
@@ -60,20 +60,6 @@ window.startEditorWindow = ->
|
||||
atom.show()
|
||||
atom.focus()
|
||||
|
||||
window.startConfigWindow = ->
|
||||
restoreDimensions()
|
||||
windowEventHandler = new WindowEventHandler
|
||||
config.load()
|
||||
keymap.loadBundledKeymaps()
|
||||
atom.loadThemes()
|
||||
atom.loadPackages()
|
||||
deserializeConfigWindow()
|
||||
atom.activatePackageConfigs()
|
||||
keymap.loadUserKeymaps()
|
||||
$(window).on 'unload', -> unloadConfigWindow(); false
|
||||
atom.show()
|
||||
atom.focus()
|
||||
|
||||
window.unloadEditorWindow = ->
|
||||
return if not project and not rootView
|
||||
windowState = atom.getWindowState()
|
||||
@@ -98,13 +84,6 @@ window.installApmCommand = (callback) ->
|
||||
commandPath = path.join(window.resourcePath, 'node_modules', '.bin', 'apm')
|
||||
require('command-installer').install(commandPath, callback)
|
||||
|
||||
window.unloadConfigWindow = ->
|
||||
return if not configView
|
||||
atom.getWindowState().set('configView', configView.serialize())
|
||||
configView.remove()
|
||||
windowEventHandler?.unsubscribe()
|
||||
window.configView = null
|
||||
|
||||
window.onDrop = (e) ->
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@@ -140,11 +119,6 @@ window.deserializeEditorWindow = ->
|
||||
window.git?.destroy()
|
||||
window.git = Git.open(projectPath)
|
||||
|
||||
window.deserializeConfigWindow = ->
|
||||
ConfigView = require 'config-view'
|
||||
window.configView = deserialize(atom.getWindowState('configView')) ? new ConfigView()
|
||||
$(rootViewParentSelector).append(configView)
|
||||
|
||||
window.stylesheetElementForId = (id) ->
|
||||
$("""head style[id="#{id}"]""")
|
||||
|
||||
@@ -233,12 +207,15 @@ window.unregisterDeserializer = (klass) ->
|
||||
delete deserializers[klass.name]
|
||||
|
||||
window.deserialize = (state) ->
|
||||
return unless state?
|
||||
if deserializer = getDeserializer(state)
|
||||
stateVersion = state.get?('version') ? state.version
|
||||
return if deserializer.version? and deserializer.version isnt stateVersion
|
||||
if (state instanceof telepath.Document) and not deserializer.acceptsDocuments
|
||||
state = state.toObject()
|
||||
deserializer.deserialize(state)
|
||||
else
|
||||
console.warn "No deserializer found for", state
|
||||
|
||||
window.getDeserializer = (state) ->
|
||||
return unless state?
|
||||
@@ -247,6 +224,7 @@ window.getDeserializer = (state) ->
|
||||
if deferredDeserializers[name]
|
||||
deferredDeserializers[name]()
|
||||
delete deferredDeserializers[name]
|
||||
|
||||
deserializers[name]
|
||||
|
||||
window.requireWithGlobals = (id, globals={}) ->
|
||||
|
||||
@@ -32,7 +32,6 @@ class AtomApplication
|
||||
client.on 'error', createAtomApplication
|
||||
|
||||
windows: null
|
||||
configWindow: null
|
||||
menu: null
|
||||
resourcePath: null
|
||||
installUpdate: null
|
||||
@@ -80,10 +79,15 @@ class AtomApplication
|
||||
app.commandLine.appendSwitch 'js-flags', '--harmony_collections'
|
||||
|
||||
checkForUpdates: ->
|
||||
return if /\w{7}/.test @version # Don't check for updates if version is a short sha
|
||||
versionIsSha = /\w{7}/.test @version
|
||||
|
||||
autoUpdater.setAutomaticallyChecksForUpdates true
|
||||
autoUpdater.checkForUpdatesInBackground()
|
||||
if versionIsSha
|
||||
autoUpdater.setAutomaticallyDownloadsUpdates false
|
||||
autoUpdater.setAutomaticallyChecksForUpdates false
|
||||
else
|
||||
autoUpdater.setAutomaticallyDownloadsUpdates true
|
||||
autoUpdater.setAutomaticallyChecksForUpdates true
|
||||
autoUpdater.checkForUpdatesInBackground()
|
||||
|
||||
buildApplicationMenu: (version, continueUpdate) ->
|
||||
menus = []
|
||||
@@ -92,7 +96,7 @@ class AtomApplication
|
||||
submenu: [
|
||||
{ label: 'About Atom', selector: 'orderFrontStandardAboutPanel:' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Preferences...', accelerator: 'Command+,', click: => @openConfig() }
|
||||
{ label: 'Preferences...', accelerator: 'Command+,', click: => console.error("preferences not setup yet") }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Hide Atom', accelerator: 'Command+H', selector: 'hide:' }
|
||||
{ label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' }
|
||||
@@ -124,7 +128,7 @@ class AtomApplication
|
||||
menus.push
|
||||
label: 'File'
|
||||
submenu: [
|
||||
{ label: 'New Window', accelerator: 'Command+N', click: => @openPath() }
|
||||
{ label: 'New Window', accelerator: 'Command+Shift+N', click: => @openPath() }
|
||||
{ label: 'Open...', accelerator: 'Command+O', click: => @promptForPath() }
|
||||
{ label: 'Open In Dev Mode...', accelerator: 'Command+Shift+O', click: => @promptForPath(devMode: true) }
|
||||
]
|
||||
@@ -176,8 +180,10 @@ class AtomApplication
|
||||
@installUpdate = quitAndUpdate
|
||||
@buildApplicationMenu version, quitAndUpdate
|
||||
|
||||
ipc.on 'open-config', =>
|
||||
@openConfig()
|
||||
ipc.on 'close-without-confirm', (processId, routingId) ->
|
||||
window = BrowserWindow.fromProcessIdAndRoutingId processId, routingId
|
||||
window.removeAllListeners 'close'
|
||||
window.close()
|
||||
|
||||
ipc.on 'open', (processId, routingId, pathsToOpen) =>
|
||||
if pathsToOpen?.length > 0
|
||||
@@ -240,17 +246,6 @@ class AtomApplication
|
||||
console.log("Killing process #{pid} failed: #{error.code}")
|
||||
delete @pidsToOpenWindows[pid]
|
||||
|
||||
openConfig: ->
|
||||
if @configWindow
|
||||
@configWindow.focus()
|
||||
return
|
||||
|
||||
@configWindow = new AtomWindow
|
||||
bootstrapScript: 'config-bootstrap'
|
||||
resourcePath: @resourcePath
|
||||
@configWindow.browserWindow.on 'destroyed', =>
|
||||
@configWindow = null
|
||||
|
||||
runSpecs: ({exitWhenDone, resourcePath}) ->
|
||||
if resourcePath isnt @resourcePath and not fs.existsSync(resourcePath)
|
||||
resourcePath = @resourcePath
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
date = new Date().getTime()
|
||||
require 'atom'
|
||||
require 'window'
|
||||
|
||||
window.setUpEnvironment('config')
|
||||
window.startConfigWindow()
|
||||
console.log "Load time: #{new Date().getTime() - date}"
|
||||
+1
-2
@@ -52,7 +52,6 @@ setupCrashReporter = ->
|
||||
|
||||
setupAutoUpdater = ->
|
||||
autoUpdater.setFeedUrl 'https://speakeasy.githubapp.com/apps/27/appcast.xml'
|
||||
autoUpdater.setAutomaticallyDownloadsUpdates true
|
||||
|
||||
parseCommandLine = ->
|
||||
version = app.getVersion()
|
||||
@@ -60,7 +59,7 @@ parseCommandLine = ->
|
||||
options.usage """
|
||||
Atom #{version}
|
||||
|
||||
Usage: atom [options] [file ..]
|
||||
Usage: atom [options] [file ...]
|
||||
"""
|
||||
options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.')
|
||||
options.alias('h', 'help').boolean('h').describe('h', 'Print this usage message.')
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
.loading-message {
|
||||
margin: 5px;
|
||||
|
||||
.mini-icon(hourglass);
|
||||
.octicon(hourglass);
|
||||
|
||||
&:before {
|
||||
font-size: 16px;
|
||||
@@ -45,10 +45,10 @@
|
||||
padding-right: @icon-margin;
|
||||
}
|
||||
|
||||
.mini-icon(text-file);
|
||||
.octicon(file-text);
|
||||
|
||||
&.symlink {
|
||||
.mini-icon(symlink);
|
||||
.octicon(file-symlink-file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
margin-right: @icon-margin;
|
||||
}
|
||||
|
||||
.mini-icon(directory);
|
||||
.octicon(file-directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
$ = require 'jquery'
|
||||
{$$} = require 'space-pen'
|
||||
_ = require 'underscore'
|
||||
Range = require 'range'
|
||||
SelectList = require 'select-list'
|
||||
|
||||
@@ -59,10 +60,17 @@ class AutocompleteView extends SelectList
|
||||
|
||||
false
|
||||
|
||||
getCompletionsForCursorScope: ->
|
||||
cursorScope = @editor.scopesForBufferPosition(@editor.getCursorBufferPosition())
|
||||
completions = syntax.propertiesForScope(cursorScope, 'editor.completions')
|
||||
completions = completions.map (properties) -> _.valueForKeyPath(properties, 'editor.completions')
|
||||
_.uniq(_.flatten(completions))
|
||||
|
||||
buildWordList: ->
|
||||
wordHash = {}
|
||||
matches = @currentBuffer.getText().match(@wordRegex)
|
||||
wordHash[word] ?= true for word in (matches or [])
|
||||
wordHash[word] ?= true for word in matches ? []
|
||||
wordHash[word] ?= true for word in @getCompletionsForCursorScope()
|
||||
|
||||
@wordList = Object.keys(wordHash).sort (word1, word2) ->
|
||||
word1 = word1.toLowerCase()
|
||||
|
||||
@@ -433,7 +433,24 @@ describe "AutocompleteView", ->
|
||||
editor.attachToDom()
|
||||
editor.insertText('thisIsAReallyReallyReallyLongCompletion ')
|
||||
editor.moveCursorToBottom()
|
||||
editor.insertNewline
|
||||
editor.insertNewline()
|
||||
editor.insertText('t')
|
||||
autocomplete.attach()
|
||||
expect(autocomplete.list.prop('scrollWidth')).toBe autocomplete.list.width()
|
||||
|
||||
it "includes completions for the scope's completion preferences", ->
|
||||
atom.activatePackage('css-tmbundle', sync: true)
|
||||
cssEditor = new Editor(editSession: project.open('css.css'))
|
||||
autocomplete = new AutocompleteView(cssEditor)
|
||||
|
||||
cssEditor.attachToDom()
|
||||
cssEditor.moveCursorToEndOfLine()
|
||||
cssEditor.insertText(' out')
|
||||
cssEditor.moveCursorToEndOfLine()
|
||||
|
||||
autocomplete.attach()
|
||||
expect(autocomplete.list.find('li').length).toBe 4
|
||||
expect(autocomplete.list.find('li:eq(0)')).toHaveText 'outline'
|
||||
expect(autocomplete.list.find('li:eq(1)')).toHaveText 'outline-color'
|
||||
expect(autocomplete.list.find('li:eq(2)')).toHaveText 'outline-style'
|
||||
expect(autocomplete.list.find('li:eq(3)')).toHaveText 'outline-width'
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
module.exports =
|
||||
activate: ->
|
||||
rootView.command 'autoflow:reflow-paragraph', '.editor', (e) =>
|
||||
@reflowParagraph(e.currentTargetView())
|
||||
|
||||
reflowParagraph: (editor) ->
|
||||
if range = editor.getCurrentParagraphBufferRange()
|
||||
editor.getBuffer().change(range, @reflow(editor.getTextInRange(range)))
|
||||
|
||||
reflow: (text) ->
|
||||
wrapColumn = config.getPositiveInt('editor.preferredLineLength', 80)
|
||||
lines = []
|
||||
|
||||
currentLine = []
|
||||
currentLineLength = 0
|
||||
for segment in @segmentText(text.replace(/\n/g, ' '))
|
||||
if @wrapSegment(segment, currentLineLength, wrapColumn)
|
||||
lines.push(currentLine.join(''))
|
||||
currentLine = []
|
||||
currentLineLength = 0
|
||||
currentLine.push(segment)
|
||||
currentLineLength += segment.length
|
||||
lines.push(currentLine.join(''))
|
||||
|
||||
lines.join('\n').replace(/\s+\n/g, '\n')
|
||||
|
||||
wrapSegment: (segment, currentLineLength, wrapColumn) ->
|
||||
/\w/.test(segment) and
|
||||
(currentLineLength + segment.length > wrapColumn) and
|
||||
(currentLineLength > 0 or segment.length < wrapColumn)
|
||||
|
||||
segmentText: (text) ->
|
||||
segments = []
|
||||
re = /[\s]+|[^\s]+/g
|
||||
segments.push(match[0]) while match = re.exec(text)
|
||||
segments
|
||||
@@ -0,0 +1,2 @@
|
||||
'.editor':
|
||||
'alt-meta-q': 'autoflow:reflow-paragraph'
|
||||
@@ -0,0 +1,50 @@
|
||||
module.exports =
|
||||
activate: ->
|
||||
rootView.eachEditor (editor) =>
|
||||
return unless editor.attached and editor.getPane()?
|
||||
editor.command 'autoflow:reflow-paragraph', (e) =>
|
||||
@reflowParagraph(e.currentTargetView())
|
||||
|
||||
reflowParagraph: (editor) ->
|
||||
if range = editor.getCurrentParagraphBufferRange()
|
||||
wrapColumn = config.getPositiveInt('editor.preferredLineLength', 80)
|
||||
editor.getBuffer().change(range, @reflow(editor.getTextInRange(range), {wrapColumn}))
|
||||
|
||||
reflow: (text, {wrapColumn}) ->
|
||||
paragraphs = []
|
||||
paragraphBlocks = text.split(/\n\s*\n/g)
|
||||
|
||||
for block in paragraphBlocks
|
||||
|
||||
# TODO: this could be more language specific. Use the actual comment char.
|
||||
linePrefix = block.match(/^\s*[\/#*-]*\s*/g)[0]
|
||||
blockLines = block.split('\n')
|
||||
blockLines = (blockLine.replace(new RegExp('^' + linePrefix.replace('*', '\\*')), '') for blockLine in blockLines) if linePrefix
|
||||
|
||||
lines = []
|
||||
currentLine = []
|
||||
currentLineLength = linePrefix.length
|
||||
|
||||
for segment in @segmentText(blockLines.join(' '))
|
||||
if @wrapSegment(segment, currentLineLength, wrapColumn)
|
||||
lines.push(linePrefix + currentLine.join(''))
|
||||
currentLine = []
|
||||
currentLineLength = linePrefix.length
|
||||
currentLine.push(segment)
|
||||
currentLineLength += segment.length
|
||||
lines.push(linePrefix + currentLine.join(''))
|
||||
|
||||
paragraphs.push(lines.join('\n').replace(/\s+\n/g, '\n'))
|
||||
|
||||
paragraphs.join('\n\n')
|
||||
|
||||
wrapSegment: (segment, currentLineLength, wrapColumn) ->
|
||||
/\w/.test(segment) and
|
||||
(currentLineLength + segment.length > wrapColumn) and
|
||||
(currentLineLength > 0 or segment.length < wrapColumn)
|
||||
|
||||
segmentText: (text) ->
|
||||
segments = []
|
||||
re = /[\s]+|[^\s]+/g
|
||||
segments.push(match[0]) while match = re.exec(text)
|
||||
segments
|
||||
@@ -1,2 +1,4 @@
|
||||
'main': 'autoflow'
|
||||
'main': './lib/autoflow'
|
||||
'description': 'Format the current paragraph to have lines no longer than 80 characters.\n\nThis packages uses the config value of `editor.preferredLineLength` when set.'
|
||||
'activationEvents':
|
||||
'autoflow:reflow-paragraph': '.editor'
|
||||
|
||||
@@ -2,15 +2,18 @@ RootView = require 'root-view'
|
||||
|
||||
describe "Autoflow package", ->
|
||||
editor = null
|
||||
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.open()
|
||||
atom.activatePackage('autoflow')
|
||||
editor = rootView.getActiveView()
|
||||
config.set('editor.preferredLineLength', 30)
|
||||
autoflow = null
|
||||
|
||||
describe "autoflow:reflow-paragraph", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.open()
|
||||
atom.activatePackage('autoflow')
|
||||
rootView.attachToDom()
|
||||
editor = rootView.getActiveView()
|
||||
|
||||
config.set('editor.preferredLineLength', 30)
|
||||
|
||||
it "rearranges line breaks in the current paragraph to ensure lines are shorter than config.editor.preferredLineLength", ->
|
||||
editor.setText """
|
||||
This is a preceding paragraph, which shouldn't be modified by a reflow of the following paragraph.
|
||||
@@ -52,3 +55,112 @@ describe "Autoflow package", ->
|
||||
and these are some smaller
|
||||
words
|
||||
"""
|
||||
|
||||
describe "reflowing text", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
autoflow = atom.activatePackage('autoflow', immediate: true).mainModule
|
||||
|
||||
it 'respects current paragraphs', ->
|
||||
text = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh id magna ullamcorper sagittis. Maecenas
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Phasellus gravida
|
||||
nibh id magna ullamcorper
|
||||
tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum.
|
||||
'''
|
||||
|
||||
res = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper sagittis. Maecenas et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum.
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'respects indentation', ->
|
||||
text = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh id magna ullamcorper sagittis. Maecenas
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Phasellus gravida
|
||||
nibh id magna ullamcorper
|
||||
tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum
|
||||
'''
|
||||
|
||||
res = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper sagittis. Maecenas et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
nibh id magna ullamcorper tincidunt adipiscing lacinia a dui. Etiam quis
|
||||
erat dolor. rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis
|
||||
fermentum
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'respects prefixed text (comments!)', ->
|
||||
text = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh id magna ullamcorper sagittis. Maecenas
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
# Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
# Phasellus gravida
|
||||
# nibh id magna ullamcorper
|
||||
# tincidunt adipiscing lacinia a dui. Etiam quis erat dolor.
|
||||
# rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis fermentum
|
||||
'''
|
||||
|
||||
res = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida nibh
|
||||
id magna ullamcorper sagittis. Maecenas et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
# nibh id magna ullamcorper tincidunt adipiscing lacinia a dui. Etiam quis
|
||||
# erat dolor. rutrum nisl fermentum rhoncus. Duis blandit ligula facilisis
|
||||
# fermentum
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'respects multiple prefixes (js/c comments)', ->
|
||||
text = '''
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
'''
|
||||
|
||||
res = '''
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida et
|
||||
// enim eu orci tincidunt adipiscing aliquam ligula.
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
it 'properly handles * prefix', ->
|
||||
text = '''
|
||||
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida
|
||||
et enim eu orci tincidunt adipiscing
|
||||
aliquam ligula.
|
||||
|
||||
* soidjfiojsoidj foi
|
||||
'''
|
||||
|
||||
res = '''
|
||||
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus gravida et
|
||||
* enim eu orci tincidunt adipiscing aliquam ligula.
|
||||
|
||||
* soidjfiojsoidj foi
|
||||
'''
|
||||
expect(autoflow.reflow(text, wrapColumn: 80)).toEqual res
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
'.editor':
|
||||
'meta-shift-f2': 'bookmarks:view-all'
|
||||
'meta-f2': 'bookmarks:toggle-bookmark'
|
||||
'f2': 'bookmarks:jump-to-next-bookmark'
|
||||
'shift-f2': 'bookmarks:jump-to-previous-bookmark'
|
||||
@@ -0,0 +1,77 @@
|
||||
path = require 'path'
|
||||
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
SelectList = require 'select-list'
|
||||
|
||||
module.exports =
|
||||
class BookmarksView extends SelectList
|
||||
@viewClass: -> "#{super} bookmarks-view overlay from-top"
|
||||
|
||||
filterKey: 'bookmarkFilterText'
|
||||
|
||||
initialize: ->
|
||||
super
|
||||
|
||||
toggle: ->
|
||||
if @hasParent()
|
||||
@cancel()
|
||||
else
|
||||
@populateBookmarks()
|
||||
@attach()
|
||||
|
||||
getFilterText: (bookmark) ->
|
||||
segments = []
|
||||
bookmarkRow = bookmark.getStartPosition().row
|
||||
segments.push(bookmarkRow)
|
||||
if bufferPath = bookmark.buffer.getPath()
|
||||
segments.push(bufferPath)
|
||||
if lineText = @getLineText(bookmark)
|
||||
segments.push(lineText)
|
||||
segments.join(' ')
|
||||
|
||||
getLineText: (bookmark) ->
|
||||
bookmark.buffer.lineForRow(bookmark.getStartPosition().row)?.trim()
|
||||
|
||||
populateBookmarks: ->
|
||||
markers = []
|
||||
attributes = class: 'bookmark'
|
||||
for buffer in project.getBuffers()
|
||||
for marker in buffer.findMarkers(attributes)
|
||||
marker.bookmarkFilterText = @getFilterText(marker)
|
||||
markers.push(marker)
|
||||
@setArray(markers)
|
||||
|
||||
itemForElement: (bookmark) ->
|
||||
bookmarkRow = bookmark.getStartPosition().row
|
||||
if filePath = bookmark.buffer.getPath()
|
||||
bookmarkLocation = "#{path.basename(filePath)}:#{bookmarkRow + 1}"
|
||||
else
|
||||
bookmarkLocation = "untitled:#{bookmarkRow + 1}"
|
||||
lineText = @getLineText(bookmark)
|
||||
|
||||
$$ ->
|
||||
if lineText
|
||||
@li class: 'bookmark two-lines', =>
|
||||
@div bookmarkLocation, class: 'primary-line'
|
||||
@div lineText, class: 'secondary-line line-text'
|
||||
else
|
||||
@li class: 'bookmark', =>
|
||||
@div bookmarkLocation, class: 'primary-line'
|
||||
|
||||
getEmptyMessage: (itemCount) ->
|
||||
if itemCount is 0
|
||||
'No bookmarks found'
|
||||
else
|
||||
super
|
||||
|
||||
confirmed : (bookmark) ->
|
||||
for editor in rootView.getEditors()
|
||||
if editor.getBuffer() is bookmark.buffer
|
||||
editor.activeEditSession.setSelectedBufferRange(bookmark.getRange(), autoscroll: true)
|
||||
|
||||
attach: ->
|
||||
super
|
||||
|
||||
rootView.append(this)
|
||||
@miniEditor.focus()
|
||||
@@ -0,0 +1,108 @@
|
||||
_ = require 'underscore'
|
||||
shell = require 'shell'
|
||||
|
||||
module.exports =
|
||||
class Bookmarks
|
||||
@activate: ->
|
||||
bookmarksList = null
|
||||
|
||||
rootView.command 'bookmarks:view-all', ->
|
||||
unless bookmarksList?
|
||||
BookmarksListView = require './bookmarks-view'
|
||||
bookmarksList = new BookmarksListView()
|
||||
bookmarksList.toggle()
|
||||
|
||||
rootView.eachEditor (editor) ->
|
||||
new Bookmarks(editor) if editor.attached and editor.getPane()?
|
||||
|
||||
editor: null
|
||||
|
||||
constructor: (@editor) ->
|
||||
@gutter = @editor.gutter
|
||||
@editor.on 'editor:display-updated', @renderBookmarkMarkers
|
||||
|
||||
@editor.command 'bookmarks:toggle-bookmark', @toggleBookmark
|
||||
@editor.command 'bookmarks:jump-to-next-bookmark', @jumpToNextBookmark
|
||||
@editor.command 'bookmarks:jump-to-previous-bookmark', @jumpToPreviousBookmark
|
||||
|
||||
toggleBookmark: =>
|
||||
cursors = @editor.getCursors()
|
||||
for cursor in cursors
|
||||
position = cursor.getBufferPosition()
|
||||
bookmarks = @findBookmarkMarkers(startBufferRow: position.row)
|
||||
|
||||
if bookmarks and bookmarks.length
|
||||
bookmark.destroy() for bookmark in bookmarks
|
||||
else
|
||||
newmark = @createBookmarkMarker(position.row)
|
||||
|
||||
@renderBookmarkMarkers()
|
||||
|
||||
jumpToNextBookmark: =>
|
||||
@jumpToBookmark('getNextBookmark')
|
||||
|
||||
jumpToPreviousBookmark: =>
|
||||
@jumpToBookmark('getPreviousBookmark')
|
||||
|
||||
renderBookmarkMarkers: =>
|
||||
return unless @gutter.isVisible()
|
||||
|
||||
@gutter.find(".line-number.bookmarked").removeClass('bookmarked')
|
||||
|
||||
markers = @findBookmarkMarkers()
|
||||
for marker in markers
|
||||
row = marker.getBufferRange().start.row
|
||||
@gutter.find(".line-number[lineNumber=#{row}]").addClass('bookmarked')
|
||||
|
||||
### Internal ###
|
||||
|
||||
jumpToBookmark: (getBookmarkFunction) =>
|
||||
cursor = @editor.getCursor()
|
||||
position = cursor.getBufferPosition()
|
||||
bookmarkMarker = @[getBookmarkFunction](position.row)
|
||||
|
||||
if bookmarkMarker
|
||||
@editor.activeEditSession.setSelectedBufferRange(bookmarkMarker.getBufferRange(), autoscroll: true)
|
||||
else
|
||||
shell.beep()
|
||||
|
||||
getPreviousBookmark: (bufferRow) ->
|
||||
markers = @findBookmarkMarkers()
|
||||
return null unless markers.length
|
||||
return markers[0] if markers.length == 1
|
||||
|
||||
bookmarkIndex = _.sortedIndex markers, bufferRow, (marker) ->
|
||||
if marker.getBufferRange then marker.getBufferRange().start.row else marker
|
||||
|
||||
bookmarkIndex--
|
||||
bookmarkIndex = markers.length - 1 if bookmarkIndex < 0
|
||||
|
||||
markers[bookmarkIndex]
|
||||
|
||||
getNextBookmark: (bufferRow) ->
|
||||
markers = @findBookmarkMarkers()
|
||||
return null unless markers.length
|
||||
return markers[0] if markers.length == 1
|
||||
|
||||
bookmarkIndex = _.sortedIndex markers, bufferRow, (marker) ->
|
||||
if marker.getBufferRange then marker.getBufferRange().start.row else marker
|
||||
|
||||
bookmarkIndex++ if markers[bookmarkIndex] and markers[bookmarkIndex].getBufferRange().start.row == bufferRow
|
||||
bookmarkIndex = 0 if bookmarkIndex >= markers.length
|
||||
|
||||
markers[bookmarkIndex]
|
||||
|
||||
createBookmarkMarker: (bufferRow) ->
|
||||
range = [[bufferRow, 0], [bufferRow, 0]]
|
||||
|
||||
# TODO: use the 'surround' strategy when collaboration is merged in
|
||||
@displayBuffer().markBufferRange(range, @bookmarkMarkerAttributes(invalidationStrategy: 'never'))
|
||||
|
||||
findBookmarkMarkers: (attributes={}) ->
|
||||
@displayBuffer().findMarkers(@bookmarkMarkerAttributes(attributes))
|
||||
|
||||
bookmarkMarkerAttributes: (attributes={}) ->
|
||||
_.extend(attributes, class: 'bookmark', displayBufferId: @displayBuffer().id)
|
||||
|
||||
displayBuffer: ->
|
||||
@editor.activeEditSession.displayBuffer
|
||||
@@ -0,0 +1,2 @@
|
||||
'main': './lib/bookmarks'
|
||||
'description': 'Can mark lines, then jump back to them'
|
||||
@@ -0,0 +1,173 @@
|
||||
RootView = require 'root-view'
|
||||
_ = require 'underscore'
|
||||
shell = require 'shell'
|
||||
|
||||
describe "Bookmarks package", ->
|
||||
[editor, editSession, displayBuffer] = []
|
||||
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
rootView.enableKeymap()
|
||||
atom.activatePackage('bookmarks', immediate: true)
|
||||
rootView.attachToDom()
|
||||
editor = rootView.getActiveView()
|
||||
editSession = editor.activeEditSession
|
||||
displayBuffer = editSession.displayBuffer
|
||||
spyOn(shell, 'beep')
|
||||
|
||||
describe "toggling bookmarks", ->
|
||||
it "creates a marker when toggled", ->
|
||||
editSession.setCursorBufferPosition([3, 10])
|
||||
expect(displayBuffer.findMarkers(class: 'bookmark').length).toEqual 0
|
||||
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
|
||||
markers = displayBuffer.findMarkers(class: 'bookmark')
|
||||
expect(markers.length).toEqual 1
|
||||
expect(markers[0].getBufferRange()).toEqual [[3, 0], [3, 0]]
|
||||
|
||||
it "removes marker when toggled", ->
|
||||
editSession.setCursorBufferPosition([3, 10])
|
||||
expect(displayBuffer.findMarkers(class: 'bookmark').length).toEqual 0
|
||||
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
expect(displayBuffer.findMarkers(class: 'bookmark').length).toEqual 1
|
||||
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
expect(displayBuffer.findMarkers(class: 'bookmark').length).toEqual 0
|
||||
|
||||
it "toggles proper classes on proper gutter row", ->
|
||||
editSession.setCursorBufferPosition([3, 10])
|
||||
expect(editor.find('.bookmarked').length).toEqual 0
|
||||
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
|
||||
lines = editor.find('.bookmarked')
|
||||
expect(lines.length).toEqual 1
|
||||
expect(lines.attr('linenumber')).toEqual '3'
|
||||
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
expect(editor.find('.bookmarked').length).toEqual 0
|
||||
|
||||
describe "jumping between bookmarks", ->
|
||||
|
||||
it "doesnt die when no bookmarks", ->
|
||||
editSession.setCursorBufferPosition([5, 10])
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-next-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [5, 10]
|
||||
expect(shell.beep.callCount).toBe 1
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-previous-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [5, 10]
|
||||
expect(shell.beep.callCount).toBe 2
|
||||
|
||||
describe "with one bookmark", ->
|
||||
beforeEach ->
|
||||
editSession.setCursorBufferPosition([2, 0])
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
|
||||
it "jump-to-next-bookmark jumps to the right place", ->
|
||||
editSession.setCursorBufferPosition([0, 0])
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-next-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-next-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
editSession.setCursorBufferPosition([5, 0])
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-next-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
it "jump-to-previous-bookmark jumps to the right place", ->
|
||||
editSession.setCursorBufferPosition([0, 0])
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-previous-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-previous-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
editSession.setCursorBufferPosition([5, 0])
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-previous-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
describe "with bookmarks", ->
|
||||
beforeEach ->
|
||||
editSession.setCursorBufferPosition([2, 0])
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
|
||||
editSession.setCursorBufferPosition([10, 0])
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
|
||||
it "jump-to-next-bookmark finds next bookmark", ->
|
||||
editSession.setCursorBufferPosition([0, 0])
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-next-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-next-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [10, 0]
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-next-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
editSession.setCursorBufferPosition([11, 0])
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-next-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
it "jump-to-previous-bookmark finds previous bookmark", ->
|
||||
editSession.setCursorBufferPosition([0, 0])
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-previous-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [10, 0]
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-previous-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [2, 0]
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-previous-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [10, 0]
|
||||
|
||||
editSession.setCursorBufferPosition([11, 0])
|
||||
|
||||
editor.trigger 'bookmarks:jump-to-previous-bookmark'
|
||||
expect(editSession.getCursor().getBufferPosition()).toEqual [10, 0]
|
||||
|
||||
describe "browsing bookmarks", ->
|
||||
it "displays a select list of all bookmarks", ->
|
||||
editSession.setCursorBufferPosition([0])
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
editSession.setCursorBufferPosition([2])
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
editSession.setCursorBufferPosition([4])
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
|
||||
rootView.trigger 'bookmarks:view-all'
|
||||
|
||||
bookmarks = rootView.find('.bookmarks-view')
|
||||
expect(bookmarks).toExist()
|
||||
expect(bookmarks.find('.bookmark').length).toBe 3
|
||||
expect(bookmarks.find('.bookmark:eq(0)').find('.primary-line').text()).toBe 'sample.js:1'
|
||||
expect(bookmarks.find('.bookmark:eq(0)').find('.secondary-line').text()).toBe 'var quicksort = function () {'
|
||||
expect(bookmarks.find('.bookmark:eq(1)').find('.primary-line').text()).toBe 'sample.js:3'
|
||||
expect(bookmarks.find('.bookmark:eq(1)').find('.secondary-line').text()).toBe 'if (items.length <= 1) return items;'
|
||||
expect(bookmarks.find('.bookmark:eq(2)').find('.primary-line').text()).toBe 'sample.js:5'
|
||||
expect(bookmarks.find('.bookmark:eq(2)').find('.secondary-line').text()).toBe 'while(items.length > 0) {'
|
||||
|
||||
describe "when a bookmark is selected", ->
|
||||
it "sets the cursor to the location the bookmark", ->
|
||||
editSession.setCursorBufferPosition([8])
|
||||
editor.trigger 'bookmarks:toggle-bookmark'
|
||||
editSession.setCursorBufferPosition([0])
|
||||
|
||||
rootView.trigger 'bookmarks:view-all'
|
||||
|
||||
bookmarks = rootView.find('.bookmarks-view')
|
||||
expect(bookmarks).toExist()
|
||||
bookmarks.find('.bookmark').mousedown().mouseup()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [8, 0]
|
||||
@@ -0,0 +1,15 @@
|
||||
@import "octicon-utf-codes.less";
|
||||
|
||||
.editor .gutter .line-number.bookmarked:after {
|
||||
color: #09C;
|
||||
content: @bookmark;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.bookmarks-view {
|
||||
.bookmark {
|
||||
.line-text {
|
||||
font-family: monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,7 +453,7 @@ describe "CommandPanel", ->
|
||||
|
||||
it "displays the number of files and operations", ->
|
||||
rootView.attachToDom()
|
||||
expect(commandPanel.previewCount.text()).toBe '17 matches in 4 files'
|
||||
expect(commandPanel.previewCount.text()).toBe '22 matches in 5 files'
|
||||
|
||||
describe "when move-down and move-up are triggered on the preview list", ->
|
||||
it "selects the next/previous operation (if there is one), and scrolls the list if needed", ->
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
@import "octicon-utf-codes.less";
|
||||
@import "octicon-mixins.less";
|
||||
|
||||
.command-panel {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
@@ -15,14 +18,10 @@
|
||||
border-top: 1px solid rgba(0,0,0,1);
|
||||
border-left: 1px solid rgba(0,0,0,1);
|
||||
|
||||
.octicon(hourglass, 1.1em);
|
||||
|
||||
&:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 1.1em;
|
||||
width: 1.1em;
|
||||
height: 1.1em;
|
||||
margin-right: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: '\f09e';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,37 +45,33 @@
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.path-details:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 12px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: "\f05b";
|
||||
position: relative;
|
||||
top: 0;
|
||||
.path-details {
|
||||
.octicon(chevron-down, 12px);
|
||||
|
||||
&:before {
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.is-collapsed .path-details:before {
|
||||
content: "\f05a";
|
||||
content: @chevron-right;
|
||||
}
|
||||
|
||||
.path-name:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: "\f011";
|
||||
position: relative;
|
||||
top: 1px;
|
||||
.path-name {
|
||||
.octicon(file-text);
|
||||
|
||||
&:before {
|
||||
margin-right: 5px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.path.readme .path-name:before {
|
||||
content: "\f007";
|
||||
content: @book;
|
||||
}
|
||||
|
||||
.operation {
|
||||
|
||||
@@ -98,13 +98,13 @@ class FuzzyFinderView extends SelectList
|
||||
confirmed : ({filePath}) ->
|
||||
return unless filePath
|
||||
|
||||
if fsUtils.isFileSync(filePath)
|
||||
if fsUtils.isDirectorySync(filePath)
|
||||
@setError('Selected path is a directory')
|
||||
setTimeout((=> @setError()), 2000)
|
||||
else
|
||||
lineNumber = @getLineNumber()
|
||||
@cancel()
|
||||
@openPath(filePath, lineNumber)
|
||||
else
|
||||
@setError('Selected path does not exist')
|
||||
setTimeout((=> @setError()), 2000)
|
||||
|
||||
toggleFileFinder: ->
|
||||
@finderMode = 'file'
|
||||
|
||||
@@ -109,12 +109,12 @@ describe 'FuzzyFinder', ->
|
||||
expect(editor2.getPath()).toBe expectedPath
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
|
||||
describe "when the selected path isn't a file that exists", ->
|
||||
describe "when the selected path is a directory", ->
|
||||
it "leaves the the tree view open, doesn't open the path in the editor, and displays an error", ->
|
||||
rootView.attachToDom()
|
||||
editorPath = rootView.getActiveView().getPath()
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
finderView.confirmed({filePath: 'dir/this/is/not/a/file.txt'})
|
||||
finderView.confirmed({filePath: project.resolve('dir')})
|
||||
expect(finderView.hasParent()).toBeTruthy()
|
||||
expect(rootView.getActiveView().getPath()).toBe editorPath
|
||||
expect(finderView.error.text().length).toBeGreaterThan 0
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
@import "octicon-utf-codes.less";
|
||||
@import "octicon-mixins.less";
|
||||
|
||||
.fuzzy-finder {
|
||||
|
||||
&.select-list li {
|
||||
@@ -11,62 +14,54 @@
|
||||
}
|
||||
|
||||
.status {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
.icon(16px);
|
||||
margin-left: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: #9d9d9d;
|
||||
float: right;
|
||||
|
||||
&new:before {
|
||||
&.new:before {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
content: "\f06b";
|
||||
content: @diff-added;
|
||||
}
|
||||
|
||||
&:before {
|
||||
&.modified:before {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
content: "\f06d";
|
||||
content: @diff-modified;
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
&:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
.icon(16px);
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: #9d9d9d;
|
||||
}
|
||||
|
||||
&.text-name:before {
|
||||
content: "\f011";
|
||||
content: @file-text;
|
||||
}
|
||||
|
||||
&.image-name:before {
|
||||
content: "\f012";
|
||||
content: @file-media;
|
||||
}
|
||||
|
||||
&.compressed-name:before {
|
||||
content: "\f013";
|
||||
content: @file-zip;
|
||||
}
|
||||
|
||||
&.pdf-name:before {
|
||||
content: "\f014";
|
||||
content: @file-pdf;
|
||||
}
|
||||
|
||||
&.readme-name:before {
|
||||
content: "\f007";
|
||||
content: @book;
|
||||
}
|
||||
|
||||
&.binary-name:before {
|
||||
content: "\f094";
|
||||
content: @file-binary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
'name': 'comment.hr.gfm'
|
||||
}
|
||||
{
|
||||
'begin': '^`{3,}\\s*coffee(script)?$'
|
||||
'begin': '^`{3,}\\s*coffee-?(script)?$'
|
||||
'beginCaptures':
|
||||
'0': 'name': 'support.gfm'
|
||||
'end': '^`{3,}$'
|
||||
|
||||
@@ -28,7 +28,7 @@ class Gists
|
||||
pasteboard.write(response.html_url)
|
||||
notification = $$ ->
|
||||
@div class: 'notification', =>
|
||||
@span class: 'icon icon-gist mega-icon'
|
||||
@span class: 'icon icon-gist mega-octicon'
|
||||
@div class: 'content', =>
|
||||
@h3 "Gist #{response.id} created", class: 'title'
|
||||
@p "The url is on your clipboard", class: 'message'
|
||||
|
||||
@@ -9,6 +9,8 @@ fenceNameToExtension =
|
||||
'bash': 'sh'
|
||||
'coffee': 'coffee'
|
||||
'coffeescript': 'coffee'
|
||||
'coffee-script': 'coffee'
|
||||
'css': 'css'
|
||||
'go': 'go'
|
||||
'java': 'java'
|
||||
'javascript': 'js'
|
||||
@@ -35,6 +37,7 @@ class MarkdownPreviewView extends ScrollView
|
||||
super
|
||||
|
||||
@renderMarkdown()
|
||||
@subscribe syntax, 'grammar-added grammar-updated', _.debounce((=> @renderMarkdown()), 250)
|
||||
@on 'core:move-up', => @scrollUp()
|
||||
@on 'core:move-down', => @scrollDown()
|
||||
|
||||
@@ -52,7 +55,7 @@ class MarkdownPreviewView extends ScrollView
|
||||
path: @buffer.getPath()
|
||||
|
||||
getTitle: ->
|
||||
"Markdown Preview – #{@buffer.getBaseName()}"
|
||||
"#{@buffer.getBaseName()} Preview"
|
||||
|
||||
getUri: ->
|
||||
"markdown-preview:#{@buffer.getPath()}"
|
||||
@@ -70,7 +73,6 @@ class MarkdownPreviewView extends ScrollView
|
||||
setLoading: ->
|
||||
@html($$$ -> @div class: 'markdown-spinner', 'Loading Markdown...')
|
||||
|
||||
|
||||
tokenizeCodeBlocks: (html) =>
|
||||
html = $(html)
|
||||
preList = $(html.filter("pre"))
|
||||
|
||||
@@ -2,7 +2,7 @@ RootView = require 'root-view'
|
||||
MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
describe "MarkdownPreview package", ->
|
||||
describe "Markdown preview package", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('gfm')
|
||||
project.setPath(project.resolve('markdown'))
|
||||
@@ -122,3 +122,14 @@ describe "MarkdownPreview package", ->
|
||||
pane1.activeItem.buffer.trigger 'saved'
|
||||
expect(preview.renderMarkdown).toHaveBeenCalled()
|
||||
expect(pane2.activeItem).toBe preview
|
||||
|
||||
describe "when a new grammar is loaded", ->
|
||||
it "reloads the view to colorize any fenced code blocks matching the newly loaded grammar", ->
|
||||
rootView.getActiveView().trigger 'markdown-preview:show'
|
||||
[pane1, pane2] = rootView.getPanes()
|
||||
preview = pane2.activeItem
|
||||
preview.renderMarkdown.reset()
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
|
||||
atom.activatePackage('javascript-tmbundle', sync: true)
|
||||
waitsFor -> preview.renderMarkdown.callCount > 0
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
cursor: text;
|
||||
position: relative;
|
||||
|
||||
.mini-icon-link {
|
||||
.octicon-link {
|
||||
display: none;
|
||||
color: #000;
|
||||
}
|
||||
@@ -91,7 +91,7 @@
|
||||
margin-left: -22px;
|
||||
top: 15%;
|
||||
|
||||
.mini-icon-link {
|
||||
.octicon-link {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
'body':
|
||||
'meta-,': 'settings-view:toggle'
|
||||
@@ -0,0 +1,134 @@
|
||||
{$$, View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
async = require 'async'
|
||||
AtomPackage = require 'atom-package'
|
||||
Editor = require 'editor'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class GeneralPanel extends View
|
||||
@content: ->
|
||||
@form id: 'general-panel', class: 'form-horizontal', =>
|
||||
@div outlet: "loadingElement", class: 'alert alert-info loading-area', "Loading settings"
|
||||
|
||||
initialize: ->
|
||||
window.setTimeout (=> @activatePackages => @showSettings()), 1
|
||||
|
||||
showSettings: ->
|
||||
@loadingElement.hide()
|
||||
@appendSettings(name, settings) for name, settings of config.getSettings()
|
||||
@bindFormFields()
|
||||
@bindEditors()
|
||||
|
||||
activatePackages: (finishedCallback) ->
|
||||
iterator = (pack, callback) ->
|
||||
try
|
||||
if pack instanceof AtomPackage and not pack.isActive()
|
||||
pack.activate({immediate: true})
|
||||
catch e
|
||||
console.error e
|
||||
finally
|
||||
callback()
|
||||
|
||||
async.each atom.getLoadedPackages(), iterator, finishedCallback
|
||||
|
||||
appendSettings: (namespace, settings) ->
|
||||
return if _.isEmpty(settings)
|
||||
|
||||
@append $$ ->
|
||||
@fieldset =>
|
||||
@legend "#{_.uncamelcase(namespace)} settings"
|
||||
appendSetting.call(this, namespace, name, value) for name, value of settings
|
||||
|
||||
bindFormFields: ->
|
||||
for input in @find('input[id]').toArray()
|
||||
do (input) =>
|
||||
input = $(input)
|
||||
name = input.attr('id')
|
||||
type = input.attr('type')
|
||||
|
||||
@observeConfig name, (value) ->
|
||||
if type is 'checkbox'
|
||||
input.attr('checked', value)
|
||||
else
|
||||
input.val(value) if value
|
||||
|
||||
input.on 'change', =>
|
||||
value = input.val()
|
||||
if type == 'checkbox'
|
||||
value = !!input.attr('checked')
|
||||
else
|
||||
value = @parseValue(type, value)
|
||||
config.set(name, value)
|
||||
|
||||
bindEditors: ->
|
||||
for editor in @find('.editor[id]').views()
|
||||
do (editor) =>
|
||||
name = editor.attr('id')
|
||||
type = editor.attr('type')
|
||||
|
||||
@observeConfig name, (value) ->
|
||||
return if value?.toString() == editor.getText()
|
||||
value ?= ""
|
||||
editor.setText(value.toString())
|
||||
|
||||
editor.getBuffer().on 'contents-modified', =>
|
||||
config.set(name, @parseValue(type, editor.getText()))
|
||||
|
||||
parseValue: (type, value) ->
|
||||
if value == ''
|
||||
value = undefined
|
||||
else if type == 'int'
|
||||
intValue = parseInt(value)
|
||||
value = intValue unless isNaN(intValue)
|
||||
else if type == 'float'
|
||||
floatValue = parseFloat(value)
|
||||
value = floatValue unless isNaN(floatValue)
|
||||
|
||||
value
|
||||
|
||||
###
|
||||
# Space Pen Helpers
|
||||
###
|
||||
|
||||
appendSetting = (namespace, name, value) ->
|
||||
return if namespace is 'core' and name is 'themes' # Handled in the Themes panel
|
||||
|
||||
@div class: 'control-group', =>
|
||||
@div class: 'controls', =>
|
||||
if _.isBoolean(value)
|
||||
appendCheckbox.call(this, namespace, name, value)
|
||||
else if _.isArray(value)
|
||||
appendArray.call(this, namespace, name, value)
|
||||
else
|
||||
appendEditor.call(this, namespace, name, value)
|
||||
|
||||
appendCheckbox = (namespace, name, value) ->
|
||||
englishName = _.uncamelcase(name)
|
||||
keyPath = "#{namespace}.#{name}"
|
||||
@div class: 'checkbox', =>
|
||||
@label for: keyPath, =>
|
||||
@input id: keyPath, type: 'checkbox'
|
||||
@text englishName
|
||||
|
||||
appendEditor = (namespace, name, value) ->
|
||||
englishName = _.uncamelcase(name)
|
||||
keyPath = "#{namespace}.#{name}"
|
||||
if _.isNumber(value)
|
||||
type = if value % 1 == 0 then 'int' else 'float'
|
||||
else
|
||||
type = 'string'
|
||||
|
||||
@label class: 'control-label', englishName
|
||||
@div class: 'controls', =>
|
||||
@subview keyPath.replace('.', ''), new Editor(mini: true, attributes: {id: keyPath, type: type})
|
||||
|
||||
appendArray = (namespace, name, value) ->
|
||||
englishName = _.uncamelcase(name)
|
||||
@label class: 'control-label', englishName
|
||||
@div class: 'controls', =>
|
||||
@text "readOnly: " + value.join(", ")
|
||||
@@ -0,0 +1,127 @@
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
{View, $$} = require 'space-pen'
|
||||
EventEmitter = require 'event-emitter'
|
||||
Editor = require 'editor'
|
||||
PackageView = require './package-view'
|
||||
packageManager = require './package-manager'
|
||||
stringScore = require 'stringscore'
|
||||
|
||||
|
||||
### Internal ###
|
||||
class PackageEventEmitter
|
||||
_.extend PackageEventEmitter.prototype, EventEmitter
|
||||
|
||||
module.exports =
|
||||
class PackagePanel extends View
|
||||
@content: ->
|
||||
@div class: 'package-panel', =>
|
||||
@legend 'Packages'
|
||||
@ul class: 'nav nav-tabs', =>
|
||||
@li class: 'active', =>
|
||||
@a 'Installed', =>
|
||||
@span class: 'badge pull-right', outlet: 'installedCount'
|
||||
@li =>
|
||||
@a 'Available', =>
|
||||
@span class: 'badge pull-right', outlet: 'availableCount'
|
||||
|
||||
@subview 'packageFilter', new Editor(mini: true, attributes: {id: 'package-filter'})
|
||||
@div outlet: 'installedPackages'
|
||||
@div outlet: 'availablePackages'
|
||||
|
||||
initialize: ->
|
||||
@packageEventEmitter = new PackageEventEmitter()
|
||||
|
||||
@availablePackages.hide()
|
||||
@loadInstalledViews()
|
||||
@loadAvailableViews()
|
||||
|
||||
@find('.nav-tabs li').on 'click', (event) =>
|
||||
return if $(event.currentTarget).hasClass('active')
|
||||
@find('.nav-tabs li').toggleClass('active')
|
||||
@availablePackages.toggle()
|
||||
@installedPackages.toggle()
|
||||
|
||||
@packageEventEmitter.on 'package-installed', (error, pack) =>
|
||||
@addInstalledPackage(pack) unless error?
|
||||
|
||||
@packageEventEmitter.on 'package-uninstalled', (error, pack) =>
|
||||
@removeInstalledPackage(pack) unless error?
|
||||
|
||||
@packageFilter.getBuffer().on 'contents-modified', =>
|
||||
@filterPackages(@packageFilter.getText())
|
||||
|
||||
loadInstalledViews: ->
|
||||
@installedPackages.empty()
|
||||
@installedPackages.append @createLoadingView('Loading installed packages\u2026')
|
||||
|
||||
packages = _.sortBy(atom.getAvailablePackageMetadata(), 'name')
|
||||
packageManager.renderMarkdownInMetadata packages, =>
|
||||
@installedPackages.empty()
|
||||
for pack in packages
|
||||
view = new PackageView(pack, @packageEventEmitter)
|
||||
@installedPackages.append(view)
|
||||
|
||||
@updateInstalledCount()
|
||||
|
||||
loadAvailableViews: ->
|
||||
@availablePackages.empty()
|
||||
@availablePackages.append @createLoadingView('Loading available packages\u2026')
|
||||
|
||||
packageManager.getAvailable (error, @packages=[]) =>
|
||||
@availablePackages.empty()
|
||||
if error?
|
||||
errorView = @createErrorView('Error fetching available packages.')
|
||||
errorView.on 'click', => @loadAvailableViews()
|
||||
@availablePackages.append errorView
|
||||
console.error(error.stack ? error)
|
||||
else
|
||||
for pack in @packages
|
||||
view = new PackageView(pack, @packageEventEmitter)
|
||||
@availablePackages.append(view)
|
||||
|
||||
@updateAvailableCount()
|
||||
|
||||
createLoadingView: (text) ->
|
||||
$$ ->
|
||||
@div class: 'alert alert-info loading-area', text
|
||||
|
||||
createErrorView: (text) ->
|
||||
$$ ->
|
||||
@div class: 'alert alert-error', =>
|
||||
@span text
|
||||
@button class: 'btn btn-mini btn-retry', 'Retry'
|
||||
|
||||
updateInstalledCount: ->
|
||||
@installedCount.text(@installedPackages.children().length)
|
||||
|
||||
updateAvailableCount: ->
|
||||
@availableCount.text(@availablePackages.children().length)
|
||||
|
||||
removeInstalledPackage: ({name}) ->
|
||||
@installedPackages.children("[name=#{name}]").remove()
|
||||
@updateInstalledCount()
|
||||
|
||||
addInstalledPackage: (pack) ->
|
||||
packageNames = [pack.name]
|
||||
@installedPackages.children().each (index, el) -> packageNames.push(el.getAttribute('name'))
|
||||
packageNames.sort()
|
||||
insertAfterIndex = packageNames.indexOf(pack.name) - 1
|
||||
|
||||
view = new PackageView(pack, @packageEventEmitter)
|
||||
if insertAfterIndex < 0
|
||||
@installedPackages.prepend(view)
|
||||
else
|
||||
@installedPackages.children(":eq(#{insertAfterIndex})").after(view)
|
||||
|
||||
@updateInstalledCount()
|
||||
|
||||
filterPackages: (filterString) ->
|
||||
for children in [@installedPackages.children(), @availablePackages.children()]
|
||||
for packageView in children
|
||||
name = packageView.getAttribute('name')
|
||||
continue unless name
|
||||
if /^\s*$/.test(filterString) or stringScore(name, filterString)
|
||||
$(packageView).show()
|
||||
else
|
||||
$(packageView).hide()
|
||||
+42
-34
@@ -1,13 +1,13 @@
|
||||
Package = require 'package'
|
||||
semver = require 'semver'
|
||||
packageManager = require 'package-manager'
|
||||
packageManager = require './package-manager'
|
||||
_ = require 'underscore'
|
||||
{$$, View} = require 'space-pen'
|
||||
requireWithGlobals 'bootstrap/js/bootstrap-dropdown', jQuery: require 'jquery'
|
||||
|
||||
### Internal ###
|
||||
module.exports =
|
||||
class PackageConfigView extends View
|
||||
class PackageView extends View
|
||||
@content: ->
|
||||
@div class: 'panel', =>
|
||||
@div outlet: 'heading', class: 'panel-heading', =>
|
||||
@@ -29,33 +29,41 @@ class PackageConfigView extends View
|
||||
@a 'Show README', outlet: 'readmeLink'
|
||||
@div class: 'readme', outlet: 'readme'
|
||||
|
||||
pack: null
|
||||
metadata: null
|
||||
installed: false
|
||||
disabled: false
|
||||
bundled: false
|
||||
updateAvailable: false
|
||||
|
||||
initialize: (@pack, @packageEventEmitter) ->
|
||||
initialize: (pack, @packageEventEmitter) ->
|
||||
if pack instanceof Package
|
||||
@pack = pack
|
||||
@metadata = @pack.metadata
|
||||
else
|
||||
@metadata = pack
|
||||
|
||||
@updatePackageState()
|
||||
|
||||
@attr('name', @pack.name)
|
||||
@name.text(@pack.name)
|
||||
if version = semver.valid(@pack.version)
|
||||
@attr('name', @metadata.name)
|
||||
@name.text(@metadata.name)
|
||||
if version = semver.valid(@metadata.version)
|
||||
@version.text(version)
|
||||
else
|
||||
@version.hide()
|
||||
|
||||
if @pack.descriptionHtml
|
||||
@description.html(@pack.descriptionHtml)
|
||||
else if @pack.description
|
||||
@description.text(@pack.description)
|
||||
if @metadata.descriptionHtml
|
||||
@description.html(@metadata.descriptionHtml)
|
||||
else if @metadata.description
|
||||
@description.text(@metadata.description)
|
||||
else
|
||||
@description.text('No further description available.')
|
||||
|
||||
@readme.hide()
|
||||
if @pack.readmeHtml
|
||||
@readme.html(pack.readmeHtml)
|
||||
else if @pack.readme
|
||||
@readme.text(@pack.readme)
|
||||
if @metadata.readmeHtml
|
||||
@readme.html(@metadata.readmeHtml)
|
||||
else if @metadata.readme
|
||||
@readme.text(@metadata.readme)
|
||||
else
|
||||
@readmeArea.hide()
|
||||
|
||||
@@ -67,12 +75,12 @@ class PackageConfigView extends View
|
||||
@readme.show()
|
||||
@readmeLink.text('Hide README')
|
||||
|
||||
homepage = @pack.homepage
|
||||
homepage = @metadata.homepage
|
||||
unless homepage
|
||||
if _.isString(@pack.repository)
|
||||
repoUrl = @pack.repository
|
||||
if _.isString(@metadata.repository)
|
||||
repoUrl = @metadata.repository
|
||||
else
|
||||
repoUrl = @pack.repository?.url
|
||||
repoUrl = @metadata.repository?.url
|
||||
if repoUrl
|
||||
repoUrl = repoUrl.replace(/.git$/, '')
|
||||
homepage = repoUrl if require('url').parse(repoUrl).host is 'github.com'
|
||||
@@ -81,7 +89,7 @@ class PackageConfigView extends View
|
||||
else
|
||||
@homepage.hide()
|
||||
|
||||
if issues = @pack.bugs?.url
|
||||
if issues = @metadata.bugs?.url
|
||||
@issues.find('a').attr('href', issues)
|
||||
else
|
||||
@issues.hide()
|
||||
@@ -96,16 +104,16 @@ class PackageConfigView extends View
|
||||
if @installed
|
||||
if @updateAvailable
|
||||
@defaultAction.text('Upgrading\u2026')
|
||||
packageManager.install @pack, (error) =>
|
||||
@packageEventEmitter.trigger('package-upgraded', error, @pack)
|
||||
packageManager.install @metadata, (error) =>
|
||||
@packageEventEmitter.trigger('package-upgraded', error, @metadata)
|
||||
else
|
||||
@defaultAction.text('Uninstalling\u2026')
|
||||
packageManager.uninstall @pack, (error) =>
|
||||
@packageEventEmitter.trigger('package-uninstalled', error, @pack)
|
||||
packageManager.uninstall @metadata, (error) =>
|
||||
@packageEventEmitter.trigger('package-uninstalled', error, @metadata)
|
||||
else
|
||||
@defaultAction.text('Installing\u2026')
|
||||
packageManager.install @pack, (error) =>
|
||||
@packageEventEmitter.trigger('package-installed', error, @pack)
|
||||
packageManager.install @metadata, (error) =>
|
||||
@packageEventEmitter.trigger('package-installed', error, @metadata)
|
||||
|
||||
@updateDefaultAction()
|
||||
|
||||
@@ -116,24 +124,24 @@ class PackageConfigView extends View
|
||||
@updateDefaultAction()
|
||||
@updateEnabledState()
|
||||
|
||||
@packageEventEmitter.on 'package-installed package-uninstalled package-upgraded', (error, pack) =>
|
||||
if pack?.name is @pack.name
|
||||
@packageEventEmitter.on 'package-installed package-uninstalled package-upgraded', (error, metadata) =>
|
||||
if metadata?.name is @metadata.name
|
||||
@defaultAction.enable()
|
||||
@updatePackageState()
|
||||
@updateDefaultAction()
|
||||
|
||||
togglePackageEnablement: ->
|
||||
if @disabled
|
||||
config.removeAtKeyPath('core.disabledPackages', @pack.name)
|
||||
config.removeAtKeyPath('core.disabledPackages', @metadata.name)
|
||||
else
|
||||
config.pushAtKeyPath('core.disabledPackages', @pack.name)
|
||||
config.pushAtKeyPath('core.disabledPackages', @metadata.name)
|
||||
|
||||
updatePackageState: ->
|
||||
@disabled = atom.isPackageDisabled(@pack.name)
|
||||
@disabled = atom.isPackageDisabled(@metadata.name)
|
||||
@updateAvailable = false
|
||||
@bundled = false
|
||||
loadedPackage = atom.getLoadedPackage(@pack.name)
|
||||
packagePath = loadedPackage?.path ? atom.resolvePackagePath(@pack.name)
|
||||
loadedPackage = atom.getLoadedPackage(@metadata.name)
|
||||
packagePath = loadedPackage?.path ? atom.resolvePackagePath(@metadata.name)
|
||||
@installed = packagePath?
|
||||
if @installed
|
||||
for packageDirPath in config.bundledPackageDirPaths
|
||||
@@ -144,8 +152,8 @@ class PackageConfigView extends View
|
||||
version = loadedPackage?.metadata.version
|
||||
unless version
|
||||
try
|
||||
version = Package.loadMetadata(@pack.name).version
|
||||
@updateAvailable = semver.gt(@pack.version, version)
|
||||
version = Package.loadMetadata(@metadata.name).version
|
||||
@updateAvailable = semver.gt(@metadata.version, version)
|
||||
|
||||
if @updateAvailable
|
||||
@update.show()
|
||||
+41
-21
@@ -1,44 +1,59 @@
|
||||
{View, $$} = require 'space-pen'
|
||||
{$$} = require 'space-pen'
|
||||
ScrollView = require 'scroll-view'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
GeneralConfigPanel = require 'general-config-panel'
|
||||
EditorConfigPanel = require 'editor-config-panel'
|
||||
ThemeConfigPanel = require 'theme-config-panel'
|
||||
PackageConfigPanel = require 'package-config-panel'
|
||||
Pane = require 'pane'
|
||||
GeneralPanel = require './general-panel'
|
||||
ThemePanel = require './theme-panel'
|
||||
PackagePanel = require './package-panel'
|
||||
Project = require 'project'
|
||||
|
||||
configUri = "atom://config"
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class ConfigView extends View
|
||||
class SettingsView extends ScrollView
|
||||
registerDeserializer(this)
|
||||
|
||||
@deserialize: ({activePanelName}) ->
|
||||
view = new ConfigView()
|
||||
view.showPanel(activePanelName)
|
||||
view
|
||||
@activate: (state) ->
|
||||
Project.registerOpener (filePath) ->
|
||||
new SettingsView() if filePath is configUri
|
||||
|
||||
rootView.command 'settings-view:toggle', ->
|
||||
rootView.open(configUri)
|
||||
|
||||
@deserialize: ({activePanelName}={}) ->
|
||||
new SettingsView(activePanelName)
|
||||
|
||||
@content: ->
|
||||
@div id: 'config-view', =>
|
||||
@div id: 'settings-view', tabindex: -1, =>
|
||||
@div id: 'config-menu', =>
|
||||
@ul id: 'panels-menu', class: 'nav nav-pills nav-stacked', outlet: 'panelMenu'
|
||||
@button "open .atom", id: 'open-dot-atom', class: 'btn btn-default btn-small'
|
||||
@button "Open ~/.atom", id: 'open-dot-atom', class: 'btn btn-default btn-small'
|
||||
@div id: 'panels', outlet: 'panels'
|
||||
|
||||
initialize: ->
|
||||
activePanelName: null
|
||||
|
||||
initialize: (activePanelName) ->
|
||||
super
|
||||
@panelsByName = {}
|
||||
document.title = "Atom Configuration"
|
||||
@on 'click', '#panels-menu li a', (e) =>
|
||||
@showPanel($(e.target).closest('li').attr('name'))
|
||||
|
||||
@on 'click', '#open-dot-atom', ->
|
||||
atom.open(config.configDirPath)
|
||||
|
||||
@addPanel('General', new GeneralConfigPanel)
|
||||
@addPanel('Editor', new EditorConfigPanel)
|
||||
@addPanel('Themes', new ThemeConfigPanel)
|
||||
@addPanel('Packages', new PackageConfigPanel)
|
||||
@addPanel('General', new GeneralPanel)
|
||||
@addPanel('Themes', new ThemePanel)
|
||||
@addPanel('Packages', new PackagePanel)
|
||||
@showPanel(activePanelName) if activePanelName
|
||||
|
||||
serialize: ->
|
||||
deserializer: 'SettingsView'
|
||||
activePanelName: @activePanelName
|
||||
|
||||
addPanel: (name, panel) ->
|
||||
panelItem = $$ -> @li name: name, => @a name
|
||||
@@ -64,6 +79,11 @@ class ConfigView extends View
|
||||
else
|
||||
@panelToShow = name
|
||||
|
||||
serialize: ->
|
||||
deserializer: @constructor.name
|
||||
activePanelName: @activePanelName
|
||||
getTitle: ->
|
||||
"Settings"
|
||||
|
||||
getUri: ->
|
||||
configUri
|
||||
|
||||
isEqual: (other) ->
|
||||
other instanceof SettingsView
|
||||
+3
-4
@@ -1,5 +1,4 @@
|
||||
ConfigPanel = require 'config-panel'
|
||||
{$$} = require 'space-pen'
|
||||
{View, $$} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
|
||||
@@ -16,7 +15,7 @@ require 'jqueryui-browser/ui/jquery.ui.draggable'
|
||||
delete window.jQuery
|
||||
|
||||
module.exports =
|
||||
class ThemeConfigPanel extends ConfigPanel
|
||||
class ThemeConfigPanel extends View
|
||||
@content: ->
|
||||
@div id: 'themes-config', =>
|
||||
@legend "Themes"
|
||||
@@ -30,7 +29,7 @@ class ThemeConfigPanel extends ConfigPanel
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
for name in atom.getAvailableThemeNames()
|
||||
for name in atom.themes.getAvailableNames()
|
||||
@availableThemes.append(@buildThemeLi(name, draggable: true))
|
||||
|
||||
@observeConfig "core.themes", (enabledThemes) =>
|
||||
@@ -0,0 +1,3 @@
|
||||
'main': './lib/settings-view'
|
||||
'deferredDeserializers': ['SettingsView']
|
||||
'description': 'GUI pane for Atom settings'
|
||||
+26
-27
@@ -1,8 +1,8 @@
|
||||
PackageConfigPanel = require 'package-config-panel'
|
||||
packageManager = require 'package-manager'
|
||||
PackagePanel = require '../lib/package-panel'
|
||||
packageManager = require '../lib/package-manager'
|
||||
_ = require 'underscore'
|
||||
|
||||
describe "PackageConfigPanel", ->
|
||||
describe "PackagePanel", ->
|
||||
[panel, configObserver] = []
|
||||
|
||||
beforeEach ->
|
||||
@@ -58,67 +58,66 @@ describe "PackageConfigPanel", ->
|
||||
config.set('core.disabledPackages', ['p1', 'p3'])
|
||||
configObserver.reset()
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
panel = new PackageConfigPanel
|
||||
panel = new PackagePanel
|
||||
|
||||
installedCallback = jasmine.createSpy("installed packages callback")
|
||||
panel.packageEventEmitter.on("installed-packages-loaded", installedCallback)
|
||||
waitsFor -> installedCallback.callCount > 0
|
||||
waitsFor ->
|
||||
panel.installedPackages.children().length == 3
|
||||
|
||||
describe 'Installed tab', ->
|
||||
it "lists all installed packages with a link to enable or disable the package", ->
|
||||
p1View = panel.installed.find("[name='p1']").view()
|
||||
p1View = panel.installedPackages.find("[name='p1']").view()
|
||||
expect(p1View).toExist()
|
||||
expect(p1View.enableToggle.find('a').text()).toBe 'Enable'
|
||||
|
||||
p2View = panel.installed.find("[name='p2']").view()
|
||||
p2View = panel.installedPackages.find("[name='p2']").view()
|
||||
expect(p2View).toExist()
|
||||
expect(p2View.enableToggle.find('a').text()).toBe 'Disable'
|
||||
|
||||
p3View = panel.installed.find("[name='p3']").view()
|
||||
p3View = panel.installedPackages.find("[name='p3']").view()
|
||||
expect(p3View).toExist()
|
||||
expect(p3View.enableToggle.find('a').text()).toBe 'Enable'
|
||||
|
||||
describe "when the core.disabledPackages array changes", ->
|
||||
it "updates the checkboxes for newly disabled / enabled packages", ->
|
||||
config.set('core.disabledPackages', ['p2'])
|
||||
p1View = panel.installed.find("[name='p1']").view()
|
||||
p1View = panel.installedPackages.find("[name='p1']").view()
|
||||
expect(p1View.enableToggle.find('a').text()).toBe 'Disable'
|
||||
|
||||
p2View = panel.installed.find("[name='p2']").view()
|
||||
p2View = panel.installedPackages.find("[name='p2']").view()
|
||||
expect(p2View.enableToggle.find('a').text()).toBe 'Enable'
|
||||
|
||||
p3View = panel.installed.find("[name='p3']").view()
|
||||
p3View = panel.installedPackages.find("[name='p3']").view()
|
||||
expect(p3View.enableToggle.find('a').text()).toBe 'Disable'
|
||||
|
||||
describe "when the disable link is clicked", ->
|
||||
it "adds the package name to the disabled packages array", ->
|
||||
p2View = panel.installed.find("[name='p2']").view()
|
||||
p2View = panel.installedPackages.find("[name='p2']").view()
|
||||
p2View.enableToggle.find('a').click()
|
||||
expect(configObserver).toHaveBeenCalledWith(['p1', 'p3', 'p2'])
|
||||
|
||||
describe "when the enable link is clicked", ->
|
||||
it "removes the package name from the disabled packages array", ->
|
||||
p3View = panel.installed.find("[name='p3']").view()
|
||||
p3View = panel.installedPackages.find("[name='p3']").view()
|
||||
p3View.enableToggle.find('a').click()
|
||||
expect(configObserver).toHaveBeenCalledWith(['p1'])
|
||||
|
||||
describe "when Uninstall is clicked", ->
|
||||
it "removes the package from the tab", ->
|
||||
expect(panel.installed.find("[name='p1']")).toExist()
|
||||
p1View = panel.installed.find("[name='p1']").view()
|
||||
expect(panel.installedPackages.find("[name='p1']")).toExist()
|
||||
p1View = panel.installedPackages.find("[name='p1']").view()
|
||||
expect(p1View.defaultAction.text()).toBe 'Uninstall'
|
||||
p1View.defaultAction.click()
|
||||
expect(panel.installed.find("[name='p1']")).not.toExist()
|
||||
expect(panel.installedPackages.find("[name='p1']")).not.toExist()
|
||||
|
||||
describe 'Available tab', ->
|
||||
it 'lists all available packages', ->
|
||||
panel.availableLink.click()
|
||||
panel.find("li a:contains(Available)").click()
|
||||
panel.attachToDom()
|
||||
|
||||
expect(panel.available.packagesArea.children('.panel').length).toBe 3
|
||||
p4View = panel.available.packagesArea.children('.panel:eq(0)').view()
|
||||
p5View = panel.available.packagesArea.children('.panel:eq(1)').view()
|
||||
p6View = panel.available.packagesArea.children('.panel:eq(2)').view()
|
||||
expect(panel.availablePackages.children('.panel').length).toBe 3
|
||||
p4View = panel.availablePackages.children('.panel:eq(0)').view()
|
||||
p5View = panel.availablePackages.children('.panel:eq(1)').view()
|
||||
p6View = panel.availablePackages.children('.panel:eq(2)').view()
|
||||
|
||||
expect(p4View.name.text()).toBe 'p4'
|
||||
expect(p5View.name.text()).toBe 'p5'
|
||||
@@ -145,10 +144,10 @@ describe "PackageConfigPanel", ->
|
||||
|
||||
describe "when Install is clicked", ->
|
||||
it "adds the package to the Installed tab", ->
|
||||
expect(panel.installed.find("[name='p4']")).not.toExist()
|
||||
expect(panel.available.find("[name='p4']")).toExist()
|
||||
p4View = panel.available.find("[name='p4']").view()
|
||||
expect(panel.installedPackages.find("[name='p4']")).not.toExist()
|
||||
expect(panel.availablePackages.find("[name='p4']")).toExist()
|
||||
p4View = panel.availablePackages.find("[name='p4']").view()
|
||||
expect(p4View.defaultAction.text()).toBe 'Install'
|
||||
p4View.defaultAction.click()
|
||||
expect(panel.installed.find("[name='p4']")).toExist()
|
||||
expect(panel.installedPackages.find("[name='p4']")).toExist()
|
||||
expect(p4View.defaultAction.text()).toBe 'Uninstall'
|
||||
@@ -0,0 +1,80 @@
|
||||
GeneralPanel = require '../lib/general-panel'
|
||||
Editor = require 'editor'
|
||||
|
||||
describe "GeneralPanel", ->
|
||||
panel = null
|
||||
|
||||
getValueForId = (id) ->
|
||||
element = panel.find("##{id.replace('.', '\\.')}")
|
||||
if element.is("input")
|
||||
element.attr('checked')?
|
||||
else
|
||||
element.view().getText()
|
||||
|
||||
setValueForId = (id, value) ->
|
||||
element = panel.find("##{id.replace('.', '\\.')}")
|
||||
if element.is("input")
|
||||
element.attr('checked', if value then 'checked' else '')
|
||||
element.change()
|
||||
else
|
||||
element.view().setText(value?.toString())
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
|
||||
|
||||
beforeEach ->
|
||||
config.set('foo.int', 22)
|
||||
config.set('foo.float', 0.1)
|
||||
config.set('foo.boolean', true)
|
||||
config.set('foo.string', 'hey')
|
||||
|
||||
panel = new GeneralPanel()
|
||||
spyOn(panel, "showSettings").andCallThrough()
|
||||
window.advanceClock(10000)
|
||||
waitsFor ->
|
||||
panel.showSettings.callCount > 0
|
||||
|
||||
it "automatically binds named fields to their corresponding config keys", ->
|
||||
expect(getValueForId('foo.int')).toBe '22'
|
||||
expect(getValueForId('foo.float')).toBe '0.1'
|
||||
expect(getValueForId('foo.boolean')).toBeTruthy()
|
||||
expect(getValueForId('foo.string')).toBe 'hey'
|
||||
|
||||
config.set('foo.int', 222)
|
||||
config.set('foo.float', 0.11)
|
||||
config.set('foo.boolean', false)
|
||||
config.set('foo.string', 'hey again')
|
||||
expect(getValueForId('foo.int')).toBe '222'
|
||||
expect(getValueForId('foo.float')).toBe '0.11'
|
||||
expect(getValueForId('foo.boolean')).toBeFalsy()
|
||||
expect(getValueForId('foo.string')).toBe 'hey again'
|
||||
|
||||
setValueForId('foo.int', 90)
|
||||
setValueForId('foo.float', 89.2)
|
||||
setValueForId('foo.string', "oh hi")
|
||||
setValueForId('foo.boolean', true)
|
||||
expect(config.get('foo.int')).toBe 90
|
||||
expect(config.get('foo.float')).toBe 89.2
|
||||
expect(config.get('foo.boolean')).toBe true
|
||||
expect(config.get('foo.string')).toBe 'oh hi'
|
||||
|
||||
setValueForId('foo.int', '')
|
||||
setValueForId('foo.float', '')
|
||||
setValueForId('foo.string', '')
|
||||
expect(config.get('foo.int')).toBeUndefined()
|
||||
expect(config.get('foo.float')).toBeUndefined()
|
||||
expect(config.get('foo.string')).toBeUndefined()
|
||||
|
||||
it "does not save the config value until it has been changed to a new value", ->
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
config.observe "foo.int", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
setValueForId('foo.int', 2)
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
observeHandler.reset()
|
||||
|
||||
setValueForId('foo.int', 2)
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
@@ -0,0 +1,46 @@
|
||||
SettingsView = require '../lib/settings-view'
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
describe "SettingsView", ->
|
||||
settingsView = null
|
||||
|
||||
beforeEach ->
|
||||
settingsView = new SettingsView
|
||||
|
||||
describe "serialization", ->
|
||||
it "remembers which panel was visible", ->
|
||||
settingsView.showPanel('Packages')
|
||||
newSettingsView = deserialize(settingsView.serialize())
|
||||
settingsView.remove()
|
||||
newSettingsView.attachToDom()
|
||||
expect(newSettingsView.activePanelName).toBe 'Packages'
|
||||
|
||||
it "shows the previously active panel if it is added after deserialization", ->
|
||||
settingsView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
settingsView.showPanel('Panel 1')
|
||||
newSettingsView = deserialize(settingsView.serialize())
|
||||
settingsView.remove()
|
||||
newSettingsView.attachToDom()
|
||||
newSettingsView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
expect(newSettingsView.activePanelName).toBe 'Panel 1'
|
||||
|
||||
describe ".addPanel(name, view)", ->
|
||||
it "adds a menu entry to the left and a panel that can be activated by clicking it", ->
|
||||
settingsView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
settingsView.addPanel('Panel 2', $$ -> @div id: 'panel-2')
|
||||
|
||||
expect(settingsView.panelMenu.find('li a:contains(Panel 1)')).toExist()
|
||||
expect(settingsView.panelMenu.find('li a:contains(Panel 2)')).toExist()
|
||||
expect(settingsView.panelMenu.children(':first')).toHaveClass 'active'
|
||||
|
||||
settingsView.attachToDom()
|
||||
settingsView.panelMenu.find('li a:contains(Panel 1)').click()
|
||||
expect(settingsView.panelMenu.children('.active').length).toBe 1
|
||||
expect(settingsView.panelMenu.find('li:contains(Panel 1)')).toHaveClass('active')
|
||||
expect(settingsView.panels.find('#panel-1')).toBeVisible()
|
||||
expect(settingsView.panels.find('#panel-2')).toBeHidden()
|
||||
settingsView.panelMenu.find('li a:contains(Panel 2)').click()
|
||||
expect(settingsView.panelMenu.children('.active').length).toBe 1
|
||||
expect(settingsView.panelMenu.find('li:contains(Panel 2)')).toHaveClass('active')
|
||||
expect(settingsView.panels.find('#panel-1')).toBeHidden()
|
||||
expect(settingsView.panels.find('#panel-2')).toBeVisible()
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
$ = require 'jquery'
|
||||
ThemeConfigPanel = require 'theme-config-panel'
|
||||
ThemePanel = require '../lib/theme-panel'
|
||||
|
||||
describe "ThemeConfigPanel", ->
|
||||
describe "ThemePanel", ->
|
||||
panel = null
|
||||
|
||||
beforeEach ->
|
||||
config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax'])
|
||||
panel = new ThemeConfigPanel
|
||||
panel = new ThemePanel
|
||||
|
||||
describe "when an enabled theme is reloced in the themes list", ->
|
||||
it "updates the 'core.themes' config key to reflect the new order", ->
|
||||
@@ -1,9 +1,7 @@
|
||||
@import "bootstrap/less/variables.less";
|
||||
@import "octicon-mixins.less";
|
||||
|
||||
#config-view {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
#settings-view {
|
||||
background: white;
|
||||
display: -webkit-flex;
|
||||
|
||||
@@ -86,7 +84,7 @@
|
||||
|
||||
#enabled-themes {
|
||||
li:hover .disable-theme {
|
||||
.mini-icon(x);
|
||||
.octicon(x);
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
&:hover { color: #000; }
|
||||
@@ -102,37 +100,6 @@
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
/* li.ui-draggable-dragging, li.ui-sortable-helper {
|
||||
width: 300px;
|
||||
background: white;
|
||||
border: 1px solid #eee;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
ol {
|
||||
box-sizing: border-box;
|
||||
height: 400px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #ccc;
|
||||
border-top-width: 0;
|
||||
background: #fafafa;
|
||||
|
||||
li {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: white;
|
||||
|
||||
input[type='checkbox'] {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
#packages {
|
||||
@@ -143,6 +110,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
#package-filter {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.btn-retry {
|
||||
margin-left: 10px;
|
||||
}
|
||||
@@ -157,7 +128,7 @@
|
||||
|
||||
.loading-area {
|
||||
span {
|
||||
.mini-icon(hourglass);
|
||||
.octicon(hourglass);
|
||||
|
||||
&:before {
|
||||
font-size: 1.1em;
|
||||
@@ -110,6 +110,10 @@ module.exports =
|
||||
|
||||
enableSnippetsInEditor: (editor) ->
|
||||
editor.command 'snippets:expand', (e) =>
|
||||
unless editor.getSelection().isEmpty()
|
||||
e.abortKeyBinding()
|
||||
return
|
||||
|
||||
editSession = editor.activeEditSession
|
||||
prefix = editSession.getCursor().getCurrentWordPrefix()
|
||||
if snippet = syntax.getProperty(editSession.getCursorScopes(), "snippets.#{prefix}")
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário