Comparar commits
243 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 7ba57df6f7 | |||
| 5c2f4d939a | |||
| 6c9c7b6d92 | |||
| 210dd61d2a | |||
| aa302cb935 | |||
| 994e856ccd | |||
| 808f7e6ed3 | |||
| 22f7bc8f26 | |||
| ab0b2cf12e | |||
| 599a2ad021 | |||
| db649798ef | |||
| 4f8f8c43a4 | |||
| 4491a9302b | |||
| ce138b56c0 | |||
| 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 | |||
| bc74be531d | |||
| 592b0886b2 | |||
| 1685d62cae | |||
| e705e3e045 | |||
| 0bb7d3ba09 | |||
| 8e92499ebb | |||
| 62e0274ff6 | |||
| fe4c4e9751 | |||
| baa03f5587 | |||
| 214d73d812 | |||
| 8c6fe7a540 | |||
| 164d5591a0 | |||
| 85cb7c88fa | |||
| 4ccbd03daf | |||
| 3df33a7367 | |||
| 13b592d1f3 | |||
| ab8df8dcde | |||
| 2b25d433c7 | |||
| 1ba8218743 | |||
| 17cf2bd1c8 | |||
| 1746bc8797 | |||
| 635f09b741 | |||
| ee0443e40c | |||
| d02e9172da | |||
| bc55ee6bd2 | |||
| 76be95bd6c | |||
| e43e8d156e | |||
| 2aca31988f | |||
| c937827582 | |||
| c52cf836cd | |||
| 0924ad586f | |||
| abdce2df04 | |||
| 3f8c120ad0 | |||
| 365e5e8413 | |||
| 90e53ad3e2 | |||
| 4a67bfcf15 | |||
| 2410ea0d5b | |||
| 17cecda23e | |||
| 9d733a2da9 | |||
| 6bbcc58542 | |||
| 1a76e3dc9d | |||
| ff70ae633d | |||
| d1f372e439 | |||
| 041f52aaaa | |||
| 270d17814e | |||
| 16095c8086 | |||
| 94e2dbbc2c |
+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
|
||||
|
||||
@@ -7,6 +7,9 @@ pairs:
|
||||
bl: Brian Lopez; brian
|
||||
jp: Justin Palmer; justin
|
||||
gt: Garen Torikian; garen
|
||||
mc: Matt Colyer; mcolyer
|
||||
jr: Jason Rudolph; jasonrudolph
|
||||
jl: Jessica Lord; jlord
|
||||
email:
|
||||
domain: github.com
|
||||
#global: true
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
* Added: Terminal package now bundled by default, open with ctrl-`
|
||||
* Fixed: Fuzzy finder not showing results for files at a certain depth
|
||||
* Fixed: Atom > Preferences... menu not opening settings in focused window
|
||||
|
||||
* 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
|
||||
* Fixed: Using toggle comment shortcut respects indentation level
|
||||
|
||||
* Fixed: Search never completing in the command panel
|
||||
|
||||
* Fixed: cmd-n now works when no windows are open
|
||||
|
||||
* Fixed: Error selecting a grammar for an untitled editor
|
||||
|
||||
* Added: j/k now can be used to navigate the tree view and archive editor
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# :rotating_light: Contributing to Atom :rotating_light:
|
||||
|
||||
|
||||
## Issues
|
||||
* Include screenshots and animated GIFs whenever possible, they are immensely
|
||||
helpful
|
||||
@@ -26,3 +27,35 @@
|
||||
* Class methods (methods starting with a `@`)
|
||||
* Instance variables
|
||||
* Instance methods
|
||||
|
||||
## Philosophy
|
||||
|
||||
### Write Beautiful Code
|
||||
Once you get something working, take the time to consider whether you can achieve it in a more elegant way. We're planning on open-sourcing Atom, so let's put our best foot forward.
|
||||
|
||||
### When in doubt, pair-up
|
||||
Pairing can be an effective and fun way to pass on culture, knowledge, and taste. If you can find the time, we encourage you to work synchronously with other community members of all experience levels to help the knowledge-mulching process. It doesn't have to be all the time; a little pairing goes a long way.
|
||||
|
||||
### Write tests, and write them first
|
||||
The test suite keeps protects our codebase from the ravages of entropy, but it only works when we have thorough coverage. Before you write implementation code, write a failing test proving that it's needed.
|
||||
|
||||
### Leave the test suite better than you found it
|
||||
Consider how the specs you are adding fit into the spec-file as a whole. Is this the right place for your spec? Does the spec need to be reorganized now that you're adding this extra dimension? Specs are only as useful as the next person's ability to understand them.
|
||||
|
||||
### Solve today's problem
|
||||
Avoid adding flexibility that isn't needed *today*. Nothing is ever set in stone, and we can always go back and add flexibility later. Adding it early just means we have to pay for complexity that we might not end up using.
|
||||
|
||||
### Favor clarity over brevity or cleverness.
|
||||
Three lines that someone else can read are better than one line that's tricky.
|
||||
|
||||
### Don't be defensive
|
||||
Only catch exceptions that are truly exceptional. Assume that components we control will honor their contracts. If they don't, the solution is to find and fix the problem in code rather than cluttering the code with attempts to foresee all potential issues at runtime.
|
||||
|
||||
### Don't be afraid to add classes and methods
|
||||
Code rarely suffers from too many methods and classes, and often suffers from too few. Err on the side of numerous short, well-named methods. Pull out classes with well-defined roles.
|
||||
|
||||
### Rip shit out
|
||||
Don't be afraid to delete code. Don't be afraid to rewrite something that needs to be refreshed. If it's in version control, we can always resurrect it.
|
||||
|
||||
### Maintain a consistent level of abstraction
|
||||
Every line in a method should read at the same basic level of abstraction. If there's a section of a method that goes into a lot more detail than the rest of the method, consider extracting a new method and giving it a clear name.
|
||||
|
||||
+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:
|
||||
|
||||
@@ -29,7 +29,7 @@ on creating your first package.
|
||||
|
||||
## package.json
|
||||
|
||||
Similar to [npm packages](http://en.wikipedia.org/wiki/Npm_(software)), Atom packages
|
||||
Similar to [npm packages](http://en.wikipedia.org/wiki/Npm_(software\)), Atom packages
|
||||
can contain a _package.json_ file in their top-level directory. This file contains metadata
|
||||
about the package, such as the path to its "main" module, library dependencies,
|
||||
and manifests specifying the order in which its resources should be loaded.
|
||||
|
||||
+9
-6
@@ -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",
|
||||
@@ -35,9 +35,11 @@
|
||||
"semver": "1.1.4",
|
||||
"guid": "0.0.10",
|
||||
"tantamount": "0.3.0",
|
||||
"nslog": "0.1.0",
|
||||
"coffeestack": "0.4.0",
|
||||
"first-mate": "0.1.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",
|
||||
@@ -67,7 +69,7 @@
|
||||
"todo-tmbundle": "1.0.0",
|
||||
"xml-tmbundle": "1.0.0",
|
||||
"yaml-tmbundle": "1.0.0",
|
||||
"nslog": "0.1.0"
|
||||
"terminal": "0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"biscotto": "0.0.12",
|
||||
@@ -78,9 +80,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()
|
||||
+233
-182
@@ -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", ->
|
||||
@@ -703,6 +851,31 @@ describe "EditSession", ->
|
||||
editSession.selectWord()
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[12, 2], [12, 6]]
|
||||
|
||||
describe ".selectToFirstCharacterOfLine()", ->
|
||||
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.selectToFirstCharacterOfLine()
|
||||
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,2]
|
||||
|
||||
expect(editSession.getSelections().length).toBe 2
|
||||
[selection1, selection2] = editSession.getSelections()
|
||||
expect(selection1.getBufferRange()).toEqual [[0,0], [0,5]]
|
||||
expect(selection1.isReversed()).toBeTruthy()
|
||||
expect(selection2.getBufferRange()).toEqual [[1,2], [1,7]]
|
||||
expect(selection2.isReversed()).toBeTruthy()
|
||||
|
||||
editSession.selectToFirstCharacterOfLine()
|
||||
[selection1, selection2] = editSession.getSelections()
|
||||
expect(selection1.getBufferRange()).toEqual [[0,0], [0,5]]
|
||||
expect(selection1.isReversed()).toBeTruthy()
|
||||
expect(selection2.getBufferRange()).toEqual [[1,0], [1,7]]
|
||||
expect(selection2.isReversed()).toBeTruthy()
|
||||
|
||||
describe ".setSelectedBufferRanges(ranges)", ->
|
||||
it "clears existing selections and creates selections for each of the given ranges", ->
|
||||
editSession.setSelectedBufferRanges([[[2, 2], [3, 3]], [[4, 4], [5, 5]]])
|
||||
@@ -1559,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", ->
|
||||
@@ -1717,10 +1898,10 @@ describe "EditSession", ->
|
||||
editSession.setSelectedBufferRange([[4, 5], [7, 5]])
|
||||
editSession.toggleLineCommentsInSelection()
|
||||
|
||||
expect(buffer.lineForRow(4)).toBe "// while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe "// current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " // current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe " // }"
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[4, 8], [7, 8]]
|
||||
|
||||
editSession.toggleLineCommentsInSelection()
|
||||
@@ -1732,9 +1913,9 @@ describe "EditSession", ->
|
||||
it "does not comment the last line of a non-empty selection if it ends at column 0", ->
|
||||
editSession.setSelectedBufferRange([[4, 5], [7, 0]])
|
||||
editSession.toggleLineCommentsInSelection()
|
||||
expect(buffer.lineForRow(4)).toBe "// while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe "// current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " // current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe " }"
|
||||
|
||||
it "uncomments lines if all lines match the comment regex", ->
|
||||
@@ -1942,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()
|
||||
@@ -2334,55 +2430,10 @@ describe "EditSession", ->
|
||||
editSession.insertText('foo')
|
||||
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
|
||||
|
||||
describe "editor.autoIndentOnPaste", ->
|
||||
describe "when the text contains multiple lines", ->
|
||||
beforeEach ->
|
||||
copyText("function() {\ninside=true\n}\n i=1\n")
|
||||
editSession.setCursorBufferPosition([2, 0])
|
||||
|
||||
it "does not auto-indent pasted text by default", ->
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(2)).toBe "function() {"
|
||||
expect(editSession.lineForBufferRow(3)).toBe "inside=true"
|
||||
expect(editSession.lineForBufferRow(4)).toBe "}"
|
||||
expect(editSession.lineForBufferRow(5)).toBe " i=1"
|
||||
|
||||
it "auto-indents pasted text when editor.autoIndentOnPaste is true", ->
|
||||
config.set("editor.autoIndentOnPaste", true)
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(2)).toBe " function() {"
|
||||
expect(editSession.lineForBufferRow(3)).toBe " inside=true"
|
||||
expect(editSession.lineForBufferRow(4)).toBe " }"
|
||||
expect(editSession.lineForBufferRow(5)).toBe " i=1"
|
||||
|
||||
describe "when the text contains no newlines", ->
|
||||
it "increaseses indent of pasted text when editor.autoIndentOnPaste is true", ->
|
||||
copyText("var number")
|
||||
editSession.setCursorBufferPosition([10, 0])
|
||||
config.set("editor.autoIndentOnPaste", true)
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(10)).toBe " var number"
|
||||
|
||||
it "decreaseses indent of pasted text when editor.autoIndentOnPaste is true", ->
|
||||
copyText(" var number")
|
||||
editSession.setCursorBufferPosition([10, 0])
|
||||
config.set("editor.autoIndentOnPaste", true)
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(10)).toBe " var number"
|
||||
|
||||
describe "editor.normalizeIndentOnPaste", ->
|
||||
beforeEach ->
|
||||
config.set('editor.normalizeIndentOnPaste', true)
|
||||
|
||||
it "does not normalize the indentation level of the text when editor.autoIndentOnPaste is true", ->
|
||||
copyText(" function() {\nvar cool = 1;\n }\n")
|
||||
config.set('editor.autoIndentOnPaste', true)
|
||||
editSession.setCursorBufferPosition([5, ])
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(5)).toBe " function() {"
|
||||
expect(editSession.lineForBufferRow(6)).toBe " var cool = 1;"
|
||||
expect(editSession.lineForBufferRow(7)).toBe " }"
|
||||
|
||||
it "does not normalize the indentation level of the text when editor.normalizeIndentOnPaste is false", ->
|
||||
copyText(" function() {\nvar cool = 1;\n }\n")
|
||||
config.set('editor.normalizeIndentOnPaste', false)
|
||||
|
||||
@@ -14,19 +14,27 @@ describe "LanguageMode", ->
|
||||
editSession = project.open('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe ".minIndentLevelForRowRange(startRow, endRow)", ->
|
||||
it "returns the minimum indent level for the given row range", ->
|
||||
expect(languageMode.minIndentLevelForRowRange(4, 7)).toBe 2
|
||||
expect(languageMode.minIndentLevelForRowRange(5, 7)).toBe 2
|
||||
expect(languageMode.minIndentLevelForRowRange(5, 6)).toBe 3
|
||||
expect(languageMode.minIndentLevelForRowRange(9, 11)).toBe 1
|
||||
expect(languageMode.minIndentLevelForRowRange(10, 10)).toBe 0
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 7)
|
||||
expect(buffer.lineForRow(4)).toBe "// while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe "// current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " // current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe " // }"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 5)
|
||||
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe " // }"
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".doesBufferRowStartFold(bufferRow)", ->
|
||||
@@ -36,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", ->
|
||||
@@ -50,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)
|
||||
@@ -58,17 +107,28 @@ describe "LanguageMode", ->
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 7)
|
||||
expect(buffer.lineForRow(4)).toBe "# pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe "# left = []"
|
||||
expect(buffer.lineForRow(6)).toBe "# right = []"
|
||||
expect(buffer.lineForRow(7)).toBe "# "
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 6)
|
||||
expect(buffer.lineForRow(4)).toBe " # pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " # left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " # right = []"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 5)
|
||||
expect(buffer.lineForRow(4)).toBe " pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " left = []"
|
||||
expect(buffer.lineForRow(6)).toBe "# right = []"
|
||||
expect(buffer.lineForRow(7)).toBe "# "
|
||||
expect(buffer.lineForRow(6)).toBe " # right = []"
|
||||
|
||||
it "comments/uncomments lines when empty line", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 7)
|
||||
expect(buffer.lineForRow(4)).toBe " # pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " # left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " # right = []"
|
||||
expect(buffer.lineForRow(7)).toBe " # "
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 5)
|
||||
expect(buffer.lineForRow(4)).toBe " pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " # right = []"
|
||||
expect(buffer.lineForRow(7)).toBe " # "
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".doesBufferRowStartFold(bufferRow)", ->
|
||||
@@ -79,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 ->
|
||||
@@ -139,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
|
||||
|
||||
@@ -2,6 +2,7 @@ PaneContainer = require 'pane-container'
|
||||
Pane = require 'pane'
|
||||
{View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
{dirname} = require 'path'
|
||||
|
||||
describe "Pane", ->
|
||||
[container, view1, view2, editSession1, editSession2, pane] = []
|
||||
@@ -373,7 +374,7 @@ describe "Pane", ->
|
||||
|
||||
pane.trigger 'core:save-as'
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(dirname(editSession2.getPath()))
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
|
||||
@@ -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,18 @@ 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: 'coreArea', =>
|
||||
@div outlet: 'coreHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'coreSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'internalArea', =>
|
||||
@div outlet: 'internalHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'internalSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'bundledArea', =>
|
||||
@div outlet: 'bundledHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'bundledSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'userArea', =>
|
||||
@div outlet: 'userHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'userSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: "status", class: 'status', =>
|
||||
@div outlet: "time", class: 'time'
|
||||
@div outlet: "specCount", class: 'spec-count'
|
||||
@@ -118,9 +129,42 @@ class AtomReporter extends View
|
||||
@time.text "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
addSpecs: (specs) ->
|
||||
coreSpecs = 0
|
||||
internalPackageSpecs = 0
|
||||
bundledPackageSpecs = 0
|
||||
userPackageSpecs = 0
|
||||
for spec in specs
|
||||
symbol = $$ -> @li class: "spec-summary pending spec-summary-#{spec.id}"
|
||||
@symbolSummary.append symbol
|
||||
switch spec.specType
|
||||
when 'core'
|
||||
coreSpecs++
|
||||
@coreSummary.append symbol
|
||||
when 'internal'
|
||||
internalPackageSpecs++
|
||||
@internalSummary.append symbol
|
||||
when 'bundled'
|
||||
bundledPackageSpecs++
|
||||
@bundledSummary.append symbol
|
||||
when 'user'
|
||||
userPackageSpecs++
|
||||
@userSummary.append symbol
|
||||
|
||||
if coreSpecs > 0
|
||||
@coreHeader.text("Core Specs (#{coreSpecs}):")
|
||||
else
|
||||
@coreArea.hide()
|
||||
if internalPackageSpecs > 0
|
||||
@internalHeader.text("Internal Package Specs (#{internalPackageSpecs}):")
|
||||
else
|
||||
@internalArea.hide()
|
||||
if bundledPackageSpecs > 0
|
||||
@bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs}):")
|
||||
else
|
||||
@bundledArea.hide()
|
||||
if userPackageSpecs > 0
|
||||
@userHeader.text("User Package Specs (#{userPackageSpecs}):")
|
||||
else
|
||||
@userArea.hide()
|
||||
|
||||
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()
|
||||
|
||||
@@ -5,12 +5,23 @@ measure 'spec suite require time', ->
|
||||
path = require 'path'
|
||||
require 'spec-helper'
|
||||
|
||||
# Run core specs
|
||||
for specPath in fsUtils.listTreeSync(fsUtils.resolveOnLoadPath("spec")) when /-spec\.coffee$/.test specPath
|
||||
require specPath
|
||||
requireSpecs = (directoryPath, specType) ->
|
||||
for specPath in fsUtils.listTreeSync(path.join(directoryPath, 'spec')) when /-spec\.coffee$/.test specPath
|
||||
require specPath
|
||||
for spec in jasmine.getEnv().currentRunner().specs() when not spec.specType?
|
||||
spec.specType = specType
|
||||
|
||||
# Run extension specs
|
||||
for packageDirPath in config.packageDirPaths
|
||||
for packagePath in fsUtils.listSync(packageDirPath)
|
||||
for specPath in fsUtils.listTreeSync(path.join(packagePath, "spec")) when /-spec\.coffee$/.test specPath
|
||||
require specPath
|
||||
# Run core specs
|
||||
requireSpecs(window.resourcePath, 'core')
|
||||
|
||||
# Run internal package specs
|
||||
for packagePath in fsUtils.listTreeSync(config.bundledPackagesDirPath)
|
||||
requireSpecs(packagePath, 'internal')
|
||||
|
||||
# Run bundled package specs
|
||||
for packagePath in fsUtils.listTreeSync(config.nodeModulesDirPath) when atom.isInternalPackage(packagePath)
|
||||
requireSpecs(packagePath, 'bundled')
|
||||
|
||||
# Run user package specs
|
||||
for packagePath in fsUtils.listTreeSync(config.userPackagesDirPath)
|
||||
requireSpecs(packagePath, 'user')
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "fsUtils", ->
|
||||
describe ".read(path)", ->
|
||||
@@ -82,6 +83,14 @@ describe "fsUtils", ->
|
||||
|
||||
expect(symlinkPaths).toEqual(paths)
|
||||
|
||||
it "ignores missing symlinks", ->
|
||||
directory = temp.mkdirSync('symlink-in-here')
|
||||
paths = []
|
||||
onPath = (childPath) -> paths.push(childPath)
|
||||
fs.symlinkSync(path.join(directory, 'source'), path.join(directory, 'destination'))
|
||||
fsUtils.traverseTreeSync(directory, onPath)
|
||||
expect(paths.length).toBe 0
|
||||
|
||||
describe ".md5ForPath(path)", ->
|
||||
it "returns the MD5 hash of the file at the given path", ->
|
||||
expect(fsUtils.md5ForPath(require.resolve('fixtures/sample.js'))).toBe 'dd38087d0d7e3e4802a6d3f9b9745f2b'
|
||||
|
||||
@@ -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
|
||||
+6
-45
@@ -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)
|
||||
|
||||
@@ -199,9 +159,10 @@ window.atom =
|
||||
showSaveDialog: (callback) ->
|
||||
callback(showSaveDialogSync())
|
||||
|
||||
showSaveDialogSync: ->
|
||||
showSaveDialogSync: (defaultPath) ->
|
||||
defaultPath ?= project?.getPath()
|
||||
currentWindow = remote.getCurrentWindow()
|
||||
dialog.showSaveDialog currentWindow, title: 'Save File'
|
||||
dialog.showSaveDialog currentWindow, {title: 'Save File', defaultPath}
|
||||
|
||||
openDevTools: ->
|
||||
remote.getCurrentWindow().openDevTools()
|
||||
@@ -236,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,11 @@ 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]
|
||||
bundledPackagesDirPath: bundledPackagesDirPath
|
||||
nodeModulesDirPath: nodeModulesDirPath
|
||||
packageDirPaths: [userPackagesDirPath, bundledPackagesDirPath]
|
||||
userPackagesDirPath: userPackagesDirPath
|
||||
userStoragePath: userStoragePath
|
||||
lessSearchPaths: [path.join(resourcePath, 'static'), path.join(resourcePath, 'vendor')]
|
||||
@@ -100,6 +100,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 +119,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)
|
||||
|
||||
@@ -510,7 +515,6 @@ class EditSession
|
||||
pasteText: (options={}) ->
|
||||
[text, metadata] = pasteboard.read()
|
||||
|
||||
options.autoIndent ?= @shouldAutoIndentPastedText()
|
||||
if config.get('editor.normalizeIndentOnPaste') and metadata
|
||||
options.indentBasis ?= metadata.indentBasis
|
||||
|
||||
@@ -532,6 +536,9 @@ class EditSession
|
||||
unfoldAll: ->
|
||||
@languageMode.unfoldAll()
|
||||
|
||||
foldAllAtIndentLevel: (indentLevel) ->
|
||||
@languageMode.foldAllAtIndentLevel(indentLevel)
|
||||
|
||||
# Folds the current row.
|
||||
foldCurrentRow: ->
|
||||
bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
|
||||
@@ -1038,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.
|
||||
@@ -1056,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: ->
|
||||
@@ -1102,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()
|
||||
@@ -1147,10 +1162,20 @@ class EditSession
|
||||
selectToBeginningOfLine: ->
|
||||
@expandSelectionsBackward (selection) => selection.selectToBeginningOfLine()
|
||||
|
||||
# Selects to the first non-whitespace character of the line.
|
||||
selectToFirstCharacterOfLine: ->
|
||||
@expandSelectionsBackward (selection) => selection.selectToFirstCharacterOfLine()
|
||||
|
||||
# Selects all the text from the current cursor position to the end of the line.
|
||||
selectToEndOfLine: ->
|
||||
@expandSelectionsForward (selection) => selection.selectToEndOfLine()
|
||||
|
||||
selectToPreviousWordBoundary: ->
|
||||
@expandSelectionsBackward (selection) => selection.selectToPreviousWordBoundary()
|
||||
|
||||
selectToNextWordBoundary: ->
|
||||
@expandSelectionsForward (selection) => selection.selectToNextWordBoundary()
|
||||
|
||||
# Selects the current line.
|
||||
selectLine: ->
|
||||
@expandSelectionsForward (selection) => selection.selectLine()
|
||||
@@ -1288,9 +1313,6 @@ class EditSession
|
||||
shouldAutoIndent: ->
|
||||
config.get("editor.autoIndent")
|
||||
|
||||
shouldAutoIndentPastedText: ->
|
||||
config.get("editor.autoIndentOnPaste")
|
||||
|
||||
transact: (fn) -> @buffer.transact(fn)
|
||||
|
||||
commit: -> @buffer.commit()
|
||||
|
||||
@@ -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'})
|
||||
+40
-3
@@ -21,7 +21,6 @@ class Editor extends View
|
||||
showIndentGuide: false
|
||||
showLineNumbers: true
|
||||
autoIndent: true
|
||||
autoIndentOnPaste: false
|
||||
normalizeIndentOnPaste: true
|
||||
nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`~?-"
|
||||
preferredLineLength: 80
|
||||
@@ -142,11 +141,16 @@ 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
|
||||
'editor:select-line': @selectLine
|
||||
@@ -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()
|
||||
|
||||
@@ -328,9 +347,18 @@ class Editor extends View
|
||||
# {Delegates to: EditSession.selectToBeginningOfLine}
|
||||
selectToBeginningOfLine: -> @activeEditSession.selectToBeginningOfLine()
|
||||
|
||||
# {Delegates to: EditSession.selectToFirstCharacterOfLine}
|
||||
selectToFirstCharacterOfLine: -> @activeEditSession.selectToFirstCharacterOfLine()
|
||||
|
||||
# {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()
|
||||
|
||||
@@ -457,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)
|
||||
|
||||
@@ -619,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) =>
|
||||
@@ -1466,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
|
||||
|
||||
@@ -1527,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()
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
'alt-meta-w': 'pane:close-other-items'
|
||||
'meta-P': 'pane:close'
|
||||
|
||||
'meta-n': 'new-window'
|
||||
'meta-N': 'new-editor'
|
||||
'meta-n': 'new-editor'
|
||||
'meta-N': 'new-window'
|
||||
'meta-,': 'open-user-configuration'
|
||||
'meta-o': 'open'
|
||||
'meta-O': 'open-dev'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -81,15 +81,16 @@ class LanguageMode
|
||||
columnEnd = columnStart + match[2].length
|
||||
buffer.change([[row, columnStart], [row, columnEnd]], "")
|
||||
else
|
||||
indent = @minIndentLevelForRowRange(start, end)
|
||||
indentString = @editSession.buildIndentString(indent)
|
||||
for row in [start..end]
|
||||
buffer.insert([row, 0], commentStartString)
|
||||
buffer.change([[row, 0], [row, indentString.length]], indentString + commentStartString)
|
||||
|
||||
# Folds all the foldable lines in the buffer.
|
||||
foldAll: ->
|
||||
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.
|
||||
@@ -97,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
|
||||
@@ -104,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
|
||||
@@ -117,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)
|
||||
@@ -140,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.
|
||||
#
|
||||
@@ -168,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)
|
||||
@@ -178,7 +232,18 @@ 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.
|
||||
#
|
||||
# startRow - The row {Number} to start at
|
||||
# endRow - The row {Number} to end at
|
||||
#
|
||||
# Returns a {Number} of the indent level of the block of lines.
|
||||
minIndentLevelForRowRange: (startRow, endRow) ->
|
||||
indents = (@editSession.indentationForBufferRow(row) for row in [startRow..endRow] when not @editSession.isBufferRowBlank(row))
|
||||
indents = [0] unless indents.length
|
||||
Math.min(indents...)
|
||||
|
||||
# Indents all the rows between two buffer row numbers.
|
||||
#
|
||||
|
||||
@@ -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?()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{dirname} = require 'path'
|
||||
{View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
@@ -222,7 +223,10 @@ class Pane extends View
|
||||
|
||||
saveItemAs: (item, nextAction) ->
|
||||
return unless item.saveAs?
|
||||
path = atom.showSaveDialogSync()
|
||||
|
||||
itemPath = item.getUri?()
|
||||
itemPath = dirname(itemPath) if itemPath
|
||||
path = atom.showSaveDialogSync(itemPath)
|
||||
if path
|
||||
item.saveAs(path)
|
||||
nextAction?()
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -71,9 +73,6 @@ class RootView extends View
|
||||
@command 'window:toggle-auto-indent', =>
|
||||
config.set("editor.autoIndent", !config.get("editor.autoIndent"))
|
||||
|
||||
@command 'window:toggle-auto-indent-on-paste', =>
|
||||
config.set("editor.autoIndentOnPaste", !config.get("editor.autoIndentOnPaste"))
|
||||
|
||||
@command 'pane:reopen-closed-item', =>
|
||||
@panes.reopenItem()
|
||||
|
||||
|
||||
@@ -198,6 +198,10 @@ class Selection
|
||||
selectToBeginningOfLine: ->
|
||||
@modifySelection => @cursor.moveToBeginningOfLine()
|
||||
|
||||
# Selects all the text from the current cursor position to the first character of the line.
|
||||
selectToFirstCharacterOfLine: ->
|
||||
@modifySelection => @cursor.moveToFirstCharacterOfLine()
|
||||
|
||||
# Selects all the text from the current cursor position to the end of the line.
|
||||
selectToEndOfLine: ->
|
||||
@modifySelection => @cursor.moveToEndOfLine()
|
||||
@@ -214,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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
ipc = require 'ipc'
|
||||
remote = require 'remote'
|
||||
Subscriber = require 'subscriber'
|
||||
fsUtils = require 'fs-utils'
|
||||
|
||||
@@ -32,6 +33,10 @@ class WindowEventHandler
|
||||
|
||||
@subscribe $(document), 'click', 'a', @openLink
|
||||
|
||||
@subscribe $(document), 'contextmenu', (e) ->
|
||||
e.preventDefault()
|
||||
remote.getCurrentWindow().emit('context-menu', e.pageX, e.pageY)
|
||||
|
||||
openLink: (event) =>
|
||||
location = $(event.target).attr('href')
|
||||
if location and location[0] isnt '#' and /^https?:\/\//.test(location)
|
||||
|
||||
+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: => @sendCommand('window:open-settings') }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Hide Atom', accelerator: 'Command+H', selector: 'hide:' }
|
||||
{ label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' }
|
||||
@@ -124,6 +128,7 @@ class AtomApplication
|
||||
menus.push
|
||||
label: 'File'
|
||||
submenu: [
|
||||
{ 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) }
|
||||
]
|
||||
@@ -175,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
|
||||
@@ -239,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,4 +1,6 @@
|
||||
BrowserWindow = require 'browser-window'
|
||||
Menu = require 'menu'
|
||||
MenuItem = require 'menu-item'
|
||||
app = require 'app'
|
||||
dialog = require 'dialog'
|
||||
ipc = require 'ipc'
|
||||
@@ -9,12 +11,15 @@ _ = require 'underscore'
|
||||
module.exports =
|
||||
class AtomWindow
|
||||
browserWindow: null
|
||||
contextMenu: null
|
||||
inspectElementMenuItem: null
|
||||
|
||||
constructor: (settings={}) ->
|
||||
{resourcePath, pathToOpen, isSpec} = settings
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@setupNodePath(resourcePath)
|
||||
@createContextMenu()
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom'
|
||||
@handleEvents(isSpec)
|
||||
|
||||
@@ -77,8 +82,7 @@ class AtomWindow
|
||||
buttons: ['Close', 'Keep Waiting']
|
||||
message: 'Editor is not responsing'
|
||||
detail: 'The editor is not responding. Would you like to force close it or just keep waiting?'
|
||||
if chosen is 0
|
||||
setImmediate => @browserWindow.destroy()
|
||||
@browserWindow.destroy() if chosen is 0
|
||||
|
||||
@browserWindow.on 'crashed', =>
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
@@ -87,9 +91,13 @@ class AtomWindow
|
||||
message: 'The editor has crashed'
|
||||
detail: 'Please report this issue to https://github.com/github/atom/issues'
|
||||
switch chosen
|
||||
when 0 then setImmediate => @browserWindow.destroy()
|
||||
when 0 then @browserWindow.destroy()
|
||||
when 1 then @browserWindow.restart()
|
||||
|
||||
@browserWindow.on 'context-menu', (x, y) =>
|
||||
@inspectElementMenuItem.click = => @browserWindow.inspectElement(x, y)
|
||||
@contextMenu.popup(@browserWindow)
|
||||
|
||||
if isSpec
|
||||
# Spec window's web view should always have focus
|
||||
@browserWindow.on 'blur', =>
|
||||
@@ -102,6 +110,11 @@ class AtomWindow
|
||||
else
|
||||
@browserWindow.once 'window:loaded', => @openPath(pathToOpen)
|
||||
|
||||
createContextMenu: ->
|
||||
@contextMenu = new Menu
|
||||
@inspectElementMenuItem = new MenuItem(label: 'Inspect Element')
|
||||
@contextMenu.append(@inspectElementMenuItem)
|
||||
|
||||
sendCommand: (command, args...) ->
|
||||
ipc.sendChannel @browserWindow.getProcessId(), @browserWindow.getRoutingId(), 'command', command, args...
|
||||
|
||||
|
||||
@@ -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()
|
||||
+44
-21
@@ -1,44 +1,62 @@
|
||||
{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)
|
||||
|
||||
$(window).on 'window:open-settings', ->
|
||||
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 +82,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) =>
|
||||
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