Compare commits
115 commits
fix-promis
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30ad82671b | ||
|
|
59cafd6b99 | ||
|
|
ef4bb8a6ac | ||
|
|
744ec7351e | ||
|
|
23dde8c7ee | ||
|
|
a887128b9f | ||
|
|
a33959f829 | ||
|
|
b9b3ba3e9f | ||
|
|
15038d7b0c | ||
|
|
2b7e6346a5 | ||
|
|
4f1ebf8a1a | ||
|
|
653d62ec15 | ||
|
|
93d0af041f | ||
|
|
146d832e14 | ||
|
|
14f0c83261 | ||
|
|
c75395174f | ||
|
|
d0059a7bb2 | ||
|
|
1a515fcb05 | ||
|
|
964813a0b0 | ||
|
|
2fa4584eb4 | ||
|
|
23be6cc862 | ||
|
|
195396b47e | ||
|
|
791c62a3ba | ||
|
|
d76940dabc | ||
|
|
4312777508 | ||
|
|
15296ff369 | ||
|
|
117d7439ad | ||
|
|
8590f5a678 | ||
|
|
3283b72394 | ||
|
|
91f748f85f | ||
|
|
5301f4a5d4 | ||
|
|
daddcf8d0f | ||
|
|
9f1a767fec | ||
|
|
afaf152432 | ||
|
|
26ac7c50b1 | ||
|
|
b5cb054b39 | ||
|
|
4747bcf5e8 | ||
|
|
eb7a23717a | ||
|
|
f2ee5516e0 | ||
|
|
3dbcf76ff7 | ||
|
|
d0e7afe164 | ||
|
|
78a479bf9b | ||
|
|
dfbbdadfbc | ||
|
|
516b722f38 | ||
|
|
abb5cd207f | ||
|
|
545f13294e | ||
|
|
9b62ba70ca | ||
|
|
def663b9cc | ||
|
|
546aa9f95c | ||
|
|
99dd953370 | ||
|
|
708aa94eb0 | ||
|
|
02ab74465c | ||
|
|
ed3417c842 | ||
|
|
daaf8b3a2d | ||
|
|
378cf6cff4 | ||
|
|
0abb61fcb8 | ||
|
|
50f1beb1e9 | ||
|
|
f4eb1aef7d | ||
|
|
28b451f33d | ||
|
|
cf3e250ea0 | ||
|
|
26781dc9a4 | ||
|
|
ccf6aa22f8 | ||
|
|
e2c42a6eeb | ||
|
|
ba01f8a1b1 | ||
|
|
cd80a163cb | ||
|
|
d3c3603eb4 | ||
|
|
02d62547c7 | ||
|
|
d5acbd3f53 | ||
|
|
12bb28ceda | ||
|
|
0441b24c21 | ||
|
|
fd0aa7a6c3 | ||
|
|
03a36d11cd | ||
|
|
17be8f025a | ||
|
|
8e13c77a1a | ||
|
|
0f91ab5ae0 | ||
|
|
f22ae13cfd | ||
|
|
31ac3a98ed | ||
|
|
3c952ccbfe | ||
|
|
1799706e95 | ||
|
|
c6efc0d46c | ||
|
|
6bb9c45793 | ||
|
|
6c22b3b701 | ||
|
|
a450f5853f | ||
|
|
773c3c733d | ||
|
|
5f56b62b7b | ||
|
|
1b8f6238c2 | ||
|
|
3a104fe061 | ||
|
|
aca7d3b990 | ||
|
|
683a592979 | ||
|
|
25ce6b59fd | ||
|
|
d609005810 | ||
|
|
70a1987cf7 | ||
|
|
7bec719a6f | ||
|
|
2b9b2f26e5 | ||
|
|
6f03cd8bc7 | ||
|
|
7e1e03284a | ||
|
|
a59a6f9364 | ||
|
|
74451db75f | ||
|
|
ccf4c99b2b | ||
|
|
b869a49e02 | ||
|
|
959ad08591 | ||
|
|
dfa44b5fa2 | ||
|
|
e4462b6584 | ||
|
|
1c9d6a2fea | ||
|
|
96bdf4143c | ||
|
|
4424eec4cc | ||
|
|
cd4fb6f359 | ||
|
|
a405147fc5 | ||
|
|
e6ab4167c9 | ||
|
|
bca120d98d | ||
|
|
bad84d8c51 | ||
|
|
286db320a8 | ||
|
|
b763104cb2 | ||
|
|
a6c979e0a4 | ||
|
|
fdd8b36e62 |
20 changed files with 1050 additions and 186 deletions
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{coffee,json}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
23
.github/CONTRIBUTING.md
vendored
Normal file
23
.github/CONTRIBUTING.md
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Ex-Mode Contributing Guidelines
|
||||||
|
|
||||||
|
Current Maintainers:
|
||||||
|
|
||||||
|
- [@jazzpi](https://github.com/jazzpi)
|
||||||
|
|
||||||
|
This project is accepting new maintainers. Interested parties should open a new issue titled `New Maintainer Request`.
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
|
||||||
|
- If the PR *fixes* or should result in the closure of any issues, use the `fixes #` or `closes #` syntax to ensure issue will
|
||||||
|
close when your PR is merged
|
||||||
|
- All pull-requests that fix a bug or add a new feature *must* have accompanying tests before they will be merged. If you want
|
||||||
|
to speed up the merge of your PR, please contribute these tests
|
||||||
|
- *note*: if you submit a PR but are unsure how to write tests, please begin your PR title with `[needs tests]`
|
||||||
|
- Please use the [pull request template](PULL_REQUEST_TEMPLATE.md) as a guide for submitting your PR.
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
- Be aware of the responsibilities of `ex-mode` vs `vim-mode`
|
||||||
|
- If you have identified a bug we would welcome any Pull Requests that either:
|
||||||
|
- Fix the issue
|
||||||
|
- Create failing tests to confirm the bug
|
||||||
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
Fixes # .
|
||||||
|
|
||||||
|
Changes Proposed in this Pull Request:
|
||||||
|
|
||||||
|
- foo
|
||||||
|
- bar
|
||||||
|
- baz
|
||||||
|
|
||||||
|
I have written tests for:
|
||||||
|
|
||||||
|
[](Remove the `[]()` to uncomment the appropriate lines)
|
||||||
|
|
||||||
|
[](- New features introduced)
|
||||||
|
[](- Bugs fixed)
|
||||||
|
[](- Neither (I'm just enhancing tests!))
|
||||||
38
.travis.yml
38
.travis.yml
|
|
@ -1,13 +1,45 @@
|
||||||
language: objective-c
|
## Project specific config ###
|
||||||
|
language: generic
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- APM_TEST_PACKAGES="vim-mode"
|
global:
|
||||||
|
- APM_TEST_PACKAGES="vim-mode-plus"
|
||||||
|
- ATOM_LINT_WITH_BUNDLED_NODE="true"
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
- ATOM_CHANNEL=stable
|
||||||
|
- ATOM_CHANNEL=beta
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
|
### Generic setup follows ###
|
||||||
|
script:
|
||||||
|
- curl -s -O https://raw.githubusercontent.com/atom/ci/master/build-package.sh
|
||||||
|
- chmod u+x build-package.sh
|
||||||
|
- ./build-package.sh
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
on_success: never
|
on_success: never
|
||||||
on_failure: change
|
on_failure: change
|
||||||
|
|
||||||
script: 'curl -s https://raw.githubusercontent.com/atom/ci/master/build-package.sh | sh'
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
|
||||||
git:
|
git:
|
||||||
depth: 10
|
depth: 10
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- build-essential
|
||||||
|
- git
|
||||||
|
- libgnome-keyring-dev
|
||||||
|
- libsecret-1-dev
|
||||||
|
- fakeroot
|
||||||
|
|
|
||||||
58
CHANGELOG.md
58
CHANGELOG.md
|
|
@ -1,3 +1,61 @@
|
||||||
|
## (unpublished)
|
||||||
|
|
||||||
|
* Fix `:enew` not working ([#215](https://github.com/lloeki/ex-mode/pull/215))
|
||||||
|
|
||||||
|
## 0.18.0
|
||||||
|
|
||||||
|
* Add Gdefault support ([#191](https://github.com/lloeki/ex-mode/pull/191))
|
||||||
|
* Add :sort command ([#190](https://github.com/lloeki/ex-mode/pull/190))
|
||||||
|
|
||||||
|
## 0.17.0
|
||||||
|
|
||||||
|
* Add support for Atom 1.19 ([#185](https://github.com/lloeki/ex-mode/pull/185))
|
||||||
|
* Added support for canceling ex-mode with Ctrl-C ([#186](https://github.com/lloeki/ex-mode/pull/186))
|
||||||
|
|
||||||
|
## 0.16.0
|
||||||
|
|
||||||
|
* Support for Atom 1.18 and 1.19 ([#184](https://github.com/lloeki/ex-mode/pull/184))
|
||||||
|
|
||||||
|
## 0.15.0
|
||||||
|
* `vim-mode-plus` support!
|
||||||
|
- Add keybinding (@jmarianer)
|
||||||
|
- Support `:substitute` command (@mkiken)
|
||||||
|
- Support marks
|
||||||
|
- Use `vim-mode-plus` in specs
|
||||||
|
|
||||||
|
## 0.14.0
|
||||||
|
* Support `:tabonly` (@jmarianer)
|
||||||
|
* Fix `:x` closing Atom instead of the current pane
|
||||||
|
|
||||||
|
## 0.13.1
|
||||||
|
* Limit addresses to the last line
|
||||||
|
* Fix autocompleting a non existent directory (@mcnicholls)
|
||||||
|
|
||||||
|
## 0.13.0
|
||||||
|
* Added basic support for visual marks (e.g. `:'<,'>s/foo/bar`)
|
||||||
|
* Added `smartcase` option to `:set`
|
||||||
|
* Fixed using marks as addresses (e.g. `:'a,.delete`)
|
||||||
|
* Fixed search not working without a closing delimiter (e.g. `:/foo`)
|
||||||
|
|
||||||
|
## 0.12.0
|
||||||
|
* Added file and command autocomplete (@stuartquin)
|
||||||
|
* Added `splitbelow` and `splitright` options to `:set`
|
||||||
|
* Fixed the editor not updating when saving a new file with `:w` or `:saveas`
|
||||||
|
|
||||||
|
## 0.11.0
|
||||||
|
|
||||||
|
* Stop using non-standard Promise.defer (fixes issue with `:w`) (@AsaAyers)
|
||||||
|
|
||||||
|
## 0.9.0
|
||||||
|
|
||||||
|
* Added support for yank commands, ex `:1,10y` (@posgarou)
|
||||||
|
* Added contributor guidelines, including a pull request template
|
||||||
|
* Added ability to control splitting with `splitright`, and `splitbelow` (@dragonxwang)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* delete commands now add text to clipboard, ex `:1,4d`
|
||||||
|
|
||||||
## 0.8.0
|
## 0.8.0
|
||||||
* Don't allow :s delimiters not allowed by vim (@jacwah)
|
* Don't allow :s delimiters not allowed by vim (@jacwah)
|
||||||
* Backspace over empty `:` now cancels ex-mode (@shamrin)
|
* Backspace over empty `:` now cancels ex-mode (@shamrin)
|
||||||
|
|
|
||||||
33
README.md
Normal file → Executable file
33
README.md
Normal file → Executable file
|
|
@ -4,7 +4,9 @@ ex-mode for Atom's vim-mode
|
||||||
|
|
||||||
## Use
|
## Use
|
||||||
|
|
||||||
Install both [vim-mode](https://github.com/atom/vim-mode) and ex-mode. Type `:` in command mode. Enter `w` or `write`.
|
Install both [vim-mode-plus](https://github.com/t9md/atom-vim-mode-plus) (or
|
||||||
|
the deprecated `vim-mode`) and ex-mode. Type `:` in command mode. Enter `w` or
|
||||||
|
`write`.
|
||||||
|
|
||||||
## Extend
|
## Extend
|
||||||
|
|
||||||
|
|
@ -28,7 +30,34 @@ atom.packages.onDidActivatePackage (pack) ->
|
||||||
Ex.registerAlias 'Wq', 'wq'
|
Ex.registerAlias 'Wq', 'wq'
|
||||||
```
|
```
|
||||||
|
|
||||||
See `lib/ex.coffee` for some examples commands. Contributions are very welcome!
|
## Existing commands
|
||||||
|
|
||||||
|
This is the baseline list of commands supported in `ex-mode`.
|
||||||
|
|
||||||
|
| Command | Operation |
|
||||||
|
| --------------------------------------- | ---------------------------------- |
|
||||||
|
| `q/quit/tabc/tabclose` | Close active tab |
|
||||||
|
| `qall/quitall` | Close all tabs |
|
||||||
|
| `tabe/tabedit/tabnew` | Open new tab |
|
||||||
|
| `e/edit/tabe/tabedit/tabnew <file>` | Edit given file |
|
||||||
|
| `tabn/tabnext` | Go to next tab |
|
||||||
|
| `tabp/tabprevious` | Go to previous tab |
|
||||||
|
| `tabo/tabonly` | Close other tabs |
|
||||||
|
| `w/write` | Save active tab |
|
||||||
|
| `w/write/saveas <file>` | Save as |
|
||||||
|
| `wall/wa` | Save all tabs |
|
||||||
|
| `sp/split` | Split window |
|
||||||
|
| `sp/split <file>` | Open file in split window |
|
||||||
|
| `s/substitute` | Substitute regular expression in active line |
|
||||||
|
| `vsp/vsplit` | Vertical split window |
|
||||||
|
| `vsp/vsplit <file>` | Open file in vertical split window |
|
||||||
|
| `delete` | Cut active line |
|
||||||
|
| `yank` | Copy active line |
|
||||||
|
| `set <options>` | Set options |
|
||||||
|
| `sort` | Sort all lines in file |
|
||||||
|
| `sort <line range>` | Sort lines in line range |
|
||||||
|
|
||||||
|
See `lib/ex.coffee` for the implementations of these commands. Contributions are very welcome!
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,10 @@
|
||||||
|
|
||||||
# For more detailed documentation see
|
# For more detailed documentation see
|
||||||
# https://atom.io/docs/latest/advanced/keymaps
|
# https://atom.io/docs/latest/advanced/keymaps
|
||||||
|
'atom-text-editor.vim-mode-plus:not(.insert-mode)':
|
||||||
|
':': 'ex-mode:open'
|
||||||
|
'atom-text-editor.ex-mode-editor':
|
||||||
|
'ctrl-c': 'ex-mode:close'
|
||||||
|
'ctrl-[': 'ex-mode:close'
|
||||||
'atom-text-editor.vim-mode:not(.insert-mode)':
|
'atom-text-editor.vim-mode:not(.insert-mode)':
|
||||||
':': 'ex-mode:open'
|
':': 'ex-mode:open'
|
||||||
|
|
|
||||||
80
lib/autocomplete.coffee
Normal file
80
lib/autocomplete.coffee
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
fs = require 'fs'
|
||||||
|
path = require 'path'
|
||||||
|
os = require 'os'
|
||||||
|
Ex = require './ex'
|
||||||
|
|
||||||
|
module.exports =
|
||||||
|
class AutoComplete
|
||||||
|
constructor: (commands) ->
|
||||||
|
@commands = commands
|
||||||
|
@resetCompletion()
|
||||||
|
|
||||||
|
resetCompletion: () ->
|
||||||
|
@autoCompleteIndex = 0
|
||||||
|
@autoCompleteText = null
|
||||||
|
@completions = []
|
||||||
|
|
||||||
|
expandTilde: (filePath) ->
|
||||||
|
if filePath.charAt(0) == '~'
|
||||||
|
return os.homedir() + filePath.slice(1)
|
||||||
|
else
|
||||||
|
return filePath
|
||||||
|
|
||||||
|
getAutocomplete: (text) ->
|
||||||
|
if !@autoCompleteText
|
||||||
|
@autoCompleteText = text
|
||||||
|
|
||||||
|
parts = @autoCompleteText.split(' ')
|
||||||
|
cmd = parts[0]
|
||||||
|
|
||||||
|
if parts.length > 1
|
||||||
|
filePath = parts.slice(1).join(' ')
|
||||||
|
return @getCompletion(() => @getFilePathCompletion(cmd, filePath))
|
||||||
|
else
|
||||||
|
return @getCompletion(() => @getCommandCompletion(cmd))
|
||||||
|
|
||||||
|
filterByPrefix: (commands, prefix) ->
|
||||||
|
commands.sort().filter((f) => f.startsWith(prefix))
|
||||||
|
|
||||||
|
getCompletion: (completeFunc) ->
|
||||||
|
if @completions.length == 0
|
||||||
|
@completions = completeFunc()
|
||||||
|
|
||||||
|
complete = ''
|
||||||
|
if @completions.length
|
||||||
|
complete = @completions[@autoCompleteIndex % @completions.length]
|
||||||
|
@autoCompleteIndex++
|
||||||
|
|
||||||
|
# Only one result so lets return this directory
|
||||||
|
if complete.endsWith('/') && @completions.length == 1
|
||||||
|
@resetCompletion()
|
||||||
|
|
||||||
|
return complete
|
||||||
|
|
||||||
|
getCommandCompletion: (command) ->
|
||||||
|
return @filterByPrefix(@commands, command)
|
||||||
|
|
||||||
|
getFilePathCompletion: (command, filePath) ->
|
||||||
|
filePath = @expandTilde(filePath)
|
||||||
|
|
||||||
|
if filePath.endsWith(path.sep)
|
||||||
|
basePath = path.dirname(filePath + '.')
|
||||||
|
baseName = ''
|
||||||
|
else
|
||||||
|
basePath = path.dirname(filePath)
|
||||||
|
baseName = path.basename(filePath)
|
||||||
|
|
||||||
|
try
|
||||||
|
basePathStat = fs.statSync(basePath)
|
||||||
|
if basePathStat.isDirectory()
|
||||||
|
files = fs.readdirSync(basePath)
|
||||||
|
return @filterByPrefix(files, baseName).map((f) =>
|
||||||
|
filePath = path.join(basePath, f)
|
||||||
|
if fs.lstatSync(filePath).isDirectory()
|
||||||
|
return command + ' ' + filePath + path.sep
|
||||||
|
else
|
||||||
|
return command + ' ' + filePath
|
||||||
|
)
|
||||||
|
return []
|
||||||
|
catch err
|
||||||
|
return []
|
||||||
|
|
@ -5,33 +5,47 @@ CommandError = require './command-error'
|
||||||
|
|
||||||
class Command
|
class Command
|
||||||
constructor: (@editor, @exState) ->
|
constructor: (@editor, @exState) ->
|
||||||
@viewModel = new ExViewModel(@)
|
@selections = @exState.getSelections()
|
||||||
|
@viewModel = new ExViewModel(@, Object.keys(@selections).length > 0)
|
||||||
|
|
||||||
parseAddr: (str, curPos) ->
|
parseAddr: (str, cursor) ->
|
||||||
|
row = cursor.getBufferRow()
|
||||||
if str is '.'
|
if str is '.'
|
||||||
addr = curPos.row
|
addr = row
|
||||||
else if str is '$'
|
else if str is '$'
|
||||||
# Lines are 0-indexed in Atom, but 1-indexed in vim.
|
# Lines are 0-indexed in Atom, but 1-indexed in vim.
|
||||||
addr = @editor.getBuffer().lines.length - 1
|
# The two ways of getting length let us support Atom 1.19's new buffer
|
||||||
|
# implementation (https://github.com/atom/atom/pull/14435) and still
|
||||||
|
# support 1.18 and below
|
||||||
|
buffer = @editor.getBuffer()
|
||||||
|
addr = (buffer.getLineCount?() ? buffer.lines.length) - 1
|
||||||
else if str[0] in ["+", "-"]
|
else if str[0] in ["+", "-"]
|
||||||
addr = curPos.row + @parseOffset(str)
|
addr = row + @parseOffset(str)
|
||||||
else if not isNaN(str)
|
else if not isNaN(str)
|
||||||
addr = parseInt(str) - 1
|
addr = parseInt(str) - 1
|
||||||
else if str[0] is "'" # Parse Mark...
|
else if str[0] is "'" # Parse Mark...
|
||||||
unless @vimState?
|
unless @vimState?
|
||||||
throw new CommandError("Couldn't get access to vim-mode.")
|
throw new CommandError("Couldn't get access to vim-mode.")
|
||||||
mark = @vimState.marks[str[1]]
|
mark = @vimState.mark.marks[str[1]]
|
||||||
unless mark?
|
unless mark?
|
||||||
throw new CommandError("Mark #{str} not set.")
|
throw new CommandError("Mark #{str} not set.")
|
||||||
addr = mark.bufferMarker.range.end.row
|
addr = mark.getEndBufferPosition().row
|
||||||
else if str[0] is "/"
|
else if str[0] is "/"
|
||||||
addr = Find.findNextInBuffer(@editor.buffer, curPos, str[1...-1])
|
str = str[1...]
|
||||||
|
if str[str.length-1] is "/"
|
||||||
|
str = str[...-1]
|
||||||
|
addr = Find.scanEditor(str, @editor, cursor.getCurrentLineBufferRange().end)[0]
|
||||||
unless addr?
|
unless addr?
|
||||||
throw new CommandError("Pattern not found: #{str[1...-1]}")
|
throw new CommandError("Pattern not found: #{str}")
|
||||||
|
addr = addr.start.row
|
||||||
else if str[0] is "?"
|
else if str[0] is "?"
|
||||||
addr = Find.findPreviousInBuffer(@editor.buffer, curPos, str[1...-1])
|
str = str[1...]
|
||||||
|
if str[str.length-1] is "?"
|
||||||
|
str = str[...-1]
|
||||||
|
addr = Find.scanEditor(str, @editor, cursor.getCurrentLineBufferRange().start, true)[0]
|
||||||
unless addr?
|
unless addr?
|
||||||
throw new CommandError("Pattern not found: #{str[1...-1]}")
|
throw new CommandError("Pattern not found: #{str[1...-1]}")
|
||||||
|
addr = addr.start.row
|
||||||
|
|
||||||
return addr
|
return addr
|
||||||
|
|
||||||
|
|
@ -63,7 +77,9 @@ class Command
|
||||||
return
|
return
|
||||||
|
|
||||||
# Step 4: Address parsing
|
# Step 4: Address parsing
|
||||||
lastLine = @editor.getBuffer().lines.length - 1
|
# see comment in parseAddr about line length
|
||||||
|
buffer = @editor.getBuffer()
|
||||||
|
lastLine = (buffer.getLineCount?() ? buffer.lines.length) - 1
|
||||||
if cl[0] is '%'
|
if cl[0] is '%'
|
||||||
range = [0, lastLine]
|
range = [0, lastLine]
|
||||||
cl = cl[1..]
|
cl = cl[1..]
|
||||||
|
|
@ -75,8 +91,8 @@ class Command
|
||||||
\$| # Last line
|
\$| # Last line
|
||||||
\d+| # n-th line
|
\d+| # n-th line
|
||||||
'[\[\]<>'`"^.(){}a-zA-Z]| # Marks
|
'[\[\]<>'`"^.(){}a-zA-Z]| # Marks
|
||||||
/.*?[^\\]/| # Regex
|
/.*?(?:[^\\]/|$)| # Regex
|
||||||
\?.*?[^\\]\?| # Backwards search
|
\?.*?(?:[^\\]\?|$)| # Backwards search
|
||||||
[+-]\d* # Current line +/- a number of lines
|
[+-]\d* # Current line +/- a number of lines
|
||||||
)((?:\s*[+-]\d*)*) # Line offset
|
)((?:\s*[+-]\d*)*) # Line offset
|
||||||
)?
|
)?
|
||||||
|
|
@ -95,34 +111,45 @@ class Command
|
||||||
|
|
||||||
[match, addr1, off1, addr2, off2] = cl.match(addrPattern)
|
[match, addr1, off1, addr2, off2] = cl.match(addrPattern)
|
||||||
|
|
||||||
curPos = @editor.getCursorBufferPosition()
|
cursor = @editor.getLastCursor()
|
||||||
|
|
||||||
if addr1?
|
# Special case: run command on selection. This can't be handled by simply
|
||||||
address1 = @parseAddr(addr1, curPos)
|
# parsing the mark since vim-mode doesn't set it (and it would be fairly
|
||||||
|
# useless with multiple selections)
|
||||||
|
if addr1 is "'<" and addr2 is "'>"
|
||||||
|
runOverSelections = true
|
||||||
else
|
else
|
||||||
# If no addr1 is given (,+3), assume it is '.'
|
runOverSelections = false
|
||||||
address1 = curPos.row
|
if addr1?
|
||||||
if off1?
|
address1 = @parseAddr(addr1, cursor)
|
||||||
address1 += @parseOffset(off1)
|
else
|
||||||
|
# If no addr1 is given (,+3), assume it is '.'
|
||||||
|
address1 = cursor.getBufferRow()
|
||||||
|
if off1?
|
||||||
|
address1 += @parseOffset(off1)
|
||||||
|
|
||||||
address1 = 0 if address1 is -1
|
address1 = 0 if address1 is -1
|
||||||
|
address1 = lastLine if address1 > lastLine
|
||||||
|
|
||||||
if address1 < 0 or address1 > lastLine
|
if address1 < 0
|
||||||
throw new CommandError('Invalid range')
|
throw new CommandError('Invalid range')
|
||||||
|
|
||||||
if addr2?
|
if addr2?
|
||||||
address2 = @parseAddr(addr2, curPos)
|
address2 = @parseAddr(addr2, cursor)
|
||||||
if off2?
|
if off2?
|
||||||
address2 += @parseOffset(off2)
|
address2 += @parseOffset(off2)
|
||||||
|
|
||||||
if address2 < 0 or address2 > lastLine
|
address2 = 0 if address2 is -1
|
||||||
throw new CommandError('Invalid range')
|
address2 = lastLine if address2 > lastLine
|
||||||
|
|
||||||
if address2 < address1
|
if address2 < 0
|
||||||
throw new CommandError('Backwards range given')
|
throw new CommandError('Invalid range')
|
||||||
|
|
||||||
|
if address2 < address1
|
||||||
|
throw new CommandError('Backwards range given')
|
||||||
|
|
||||||
range = [address1, if address2? then address2 else address1]
|
range = [address1, if address2? then address2 else address1]
|
||||||
cl = cl[match?.length..]
|
cl = cl[match?.length..]
|
||||||
|
|
||||||
# Step 5: Leading blanks are ignored
|
# Step 5: Leading blanks are ignored
|
||||||
cl = cl.trimLeft()
|
cl = cl.trimLeft()
|
||||||
|
|
@ -149,9 +176,7 @@ class Command
|
||||||
[m, command, args] = cl.match(/^(\w+)(.*)/)
|
[m, command, args] = cl.match(/^(\w+)(.*)/)
|
||||||
|
|
||||||
# If the command matches an existing one exactly, execute that one
|
# If the command matches an existing one exactly, execute that one
|
||||||
if (func = Ex.singleton()[command])?
|
unless (func = Ex.singleton()[command])?
|
||||||
func({ range, args, @vimState, @exState, @editor })
|
|
||||||
else
|
|
||||||
# Step 8: Match command against existing commands
|
# Step 8: Match command against existing commands
|
||||||
matching = (name for name, val of Ex.singleton() when \
|
matching = (name for name, val of Ex.singleton() when \
|
||||||
name.indexOf(command) is 0)
|
name.indexOf(command) is 0)
|
||||||
|
|
@ -161,9 +186,16 @@ class Command
|
||||||
command = matching[0]
|
command = matching[0]
|
||||||
|
|
||||||
func = Ex.singleton()[command]
|
func = Ex.singleton()[command]
|
||||||
if func?
|
|
||||||
func({ range, args, @vimState, @exState, @editor })
|
if func?
|
||||||
|
if runOverSelections
|
||||||
|
for id, selection of @selections
|
||||||
|
bufferRange = selection.getBufferRange()
|
||||||
|
range = [bufferRange.start.row, bufferRange.end.row]
|
||||||
|
func({ range, args, @vimState, @exState, @editor })
|
||||||
else
|
else
|
||||||
throw new CommandError("Not an editor command: #{input.characters}")
|
func({ range, args, @vimState, @exState, @editor })
|
||||||
|
else
|
||||||
|
throw new CommandError("Not an editor command: #{input.characters}")
|
||||||
|
|
||||||
module.exports = Command
|
module.exports = Command
|
||||||
|
|
|
||||||
|
|
@ -35,3 +35,28 @@ module.exports = ExMode =
|
||||||
consumeVim: (vim) ->
|
consumeVim: (vim) ->
|
||||||
@vim = vim
|
@vim = vim
|
||||||
@globalExState.setVim(vim)
|
@globalExState.setVim(vim)
|
||||||
|
|
||||||
|
consumeVimModePlus: (vim) ->
|
||||||
|
this.consumeVim(vim)
|
||||||
|
|
||||||
|
config:
|
||||||
|
splitbelow:
|
||||||
|
title: 'Split below'
|
||||||
|
description: 'when splitting, split from below'
|
||||||
|
type: 'boolean'
|
||||||
|
default: 'false'
|
||||||
|
splitright:
|
||||||
|
title: 'Split right'
|
||||||
|
description: 'when splitting, split from right'
|
||||||
|
type: 'boolean'
|
||||||
|
default: 'false'
|
||||||
|
gdefault:
|
||||||
|
title: 'Gdefault'
|
||||||
|
description: 'When on, the ":substitute" flag \'g\' is default on'
|
||||||
|
type: 'boolean'
|
||||||
|
default: 'false'
|
||||||
|
onlyCloseBuffers:
|
||||||
|
title: 'Only close buffers'
|
||||||
|
description: 'When on, quitall only closes all buffers, not entire Atom instance'
|
||||||
|
type: 'boolean'
|
||||||
|
default: 'false'
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ class ExCommandModeInputElement extends HTMLDivElement
|
||||||
@editorContainer.style.height = "0px"
|
@editorContainer.style.height = "0px"
|
||||||
|
|
||||||
@editorElement = document.createElement "atom-text-editor"
|
@editorElement = document.createElement "atom-text-editor"
|
||||||
@editorElement.classList.add('editor')
|
@editorElement.classList.add('editor') # Consider this deprecated!
|
||||||
|
@editorElement.classList.add('ex-mode-editor')
|
||||||
@editorElement.getModel().setMini(true)
|
@editorElement.getModel().setMini(true)
|
||||||
@editorElement.setAttribute('mini', '')
|
@editorElement.setAttribute('mini', '')
|
||||||
@editorContainer.appendChild(@editorElement)
|
@editorContainer.appendChild(@editorElement)
|
||||||
|
|
@ -40,6 +41,7 @@ class ExCommandModeInputElement extends HTMLDivElement
|
||||||
|
|
||||||
atom.commands.add(@editorElement, 'core:confirm', @confirm.bind(this))
|
atom.commands.add(@editorElement, 'core:confirm', @confirm.bind(this))
|
||||||
atom.commands.add(@editorElement, 'core:cancel', @cancel.bind(this))
|
atom.commands.add(@editorElement, 'core:cancel', @cancel.bind(this))
|
||||||
|
atom.commands.add(@editorElement, 'ex-mode:close', @cancel.bind(this))
|
||||||
atom.commands.add(@editorElement, 'blur', @cancel.bind(this))
|
atom.commands.add(@editorElement, 'blur', @cancel.bind(this))
|
||||||
|
|
||||||
backspace: ->
|
backspace: ->
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,13 @@ class ExState
|
||||||
@clearOpStack()
|
@clearOpStack()
|
||||||
@emitter.emit('processed-op-stack')
|
@emitter.emit('processed-op-stack')
|
||||||
|
|
||||||
|
# Returns all non-empty selections
|
||||||
|
getSelections: ->
|
||||||
|
filtered = {}
|
||||||
|
for id, selection of @editor.getSelections()
|
||||||
|
unless selection.isEmpty()
|
||||||
|
filtered[id] = selection
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
|
||||||
module.exports = ExState
|
module.exports = ExState
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,41 @@
|
||||||
{ViewModel, Input} = require './view-model'
|
{ViewModel, Input} = require './view-model'
|
||||||
|
AutoComplete = require './autocomplete'
|
||||||
|
Ex = require './ex'
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
class ExViewModel extends ViewModel
|
class ExViewModel extends ViewModel
|
||||||
constructor: (@exCommand) ->
|
constructor: (@exCommand, withSelection) ->
|
||||||
super(@exCommand, class: 'command')
|
super(@exCommand, class: 'command')
|
||||||
@historyIndex = -1
|
@historyIndex = -1
|
||||||
|
|
||||||
|
if withSelection
|
||||||
|
@view.editorElement.getModel().setText("'<,'>")
|
||||||
|
|
||||||
|
@view.editorElement.addEventListener('keydown', @tabAutocomplete)
|
||||||
atom.commands.add(@view.editorElement, 'core:move-up', @increaseHistoryEx)
|
atom.commands.add(@view.editorElement, 'core:move-up', @increaseHistoryEx)
|
||||||
atom.commands.add(@view.editorElement, 'core:move-down', @decreaseHistoryEx)
|
atom.commands.add(@view.editorElement, 'core:move-down', @decreaseHistoryEx)
|
||||||
|
|
||||||
|
@autoComplete = new AutoComplete(Ex.getCommands())
|
||||||
|
|
||||||
restoreHistory: (index) ->
|
restoreHistory: (index) ->
|
||||||
@view.editorElement.getModel().setText(@history(index).value)
|
@view.editorElement.getModel().setText(@history(index).value)
|
||||||
|
|
||||||
history: (index) ->
|
history: (index) ->
|
||||||
@exState.getExHistoryItem(index)
|
@exState.getExHistoryItem(index)
|
||||||
|
|
||||||
|
tabAutocomplete: (event) =>
|
||||||
|
if event.keyCode == 9
|
||||||
|
event.stopPropagation()
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
completed = @autoComplete.getAutocomplete(@view.editorElement.getModel().getText())
|
||||||
|
if completed
|
||||||
|
@view.editorElement.getModel().setText(completed)
|
||||||
|
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
@autoComplete.resetCompletion()
|
||||||
|
|
||||||
increaseHistoryEx: =>
|
increaseHistoryEx: =>
|
||||||
if @history(@historyIndex + 1)?
|
if @history(@historyIndex + 1)?
|
||||||
@historyIndex += 1
|
@historyIndex += 1
|
||||||
|
|
|
||||||
179
lib/ex.coffee
179
lib/ex.coffee
|
|
@ -3,13 +3,28 @@ CommandError = require './command-error'
|
||||||
fs = require 'fs-plus'
|
fs = require 'fs-plus'
|
||||||
VimOption = require './vim-option'
|
VimOption = require './vim-option'
|
||||||
_ = require 'underscore-plus'
|
_ = require 'underscore-plus'
|
||||||
|
atom
|
||||||
|
|
||||||
|
defer = () ->
|
||||||
|
deferred = {}
|
||||||
|
deferred.promise = new Promise((resolve, reject) ->
|
||||||
|
deferred.resolve = resolve
|
||||||
|
deferred.reject = reject
|
||||||
|
)
|
||||||
|
return deferred
|
||||||
|
|
||||||
|
|
||||||
trySave = (func) ->
|
trySave = (func) ->
|
||||||
deferred = Promise.defer()
|
deferred = defer()
|
||||||
|
|
||||||
try
|
try
|
||||||
func()
|
response = func()
|
||||||
deferred.resolve()
|
|
||||||
|
if response instanceof Promise
|
||||||
|
response.then ->
|
||||||
|
deferred.resolve()
|
||||||
|
else
|
||||||
|
deferred.resolve()
|
||||||
catch error
|
catch error
|
||||||
if error.message.endsWith('is a directory')
|
if error.message.endsWith('is a directory')
|
||||||
atom.notifications.addWarning("Unable to save file: #{error.message}")
|
atom.notifications.addWarning("Unable to save file: #{error.message}")
|
||||||
|
|
@ -109,11 +124,20 @@ class Ex
|
||||||
@registerAlias: (alias, name) =>
|
@registerAlias: (alias, name) =>
|
||||||
@singleton()[alias] = (args) => @singleton()[name](args)
|
@singleton()[alias] = (args) => @singleton()[name](args)
|
||||||
|
|
||||||
|
@getCommands: () =>
|
||||||
|
Object.keys(Ex.singleton()).concat(Object.keys(Ex.prototype)).filter((cmd, index, list) ->
|
||||||
|
list.indexOf(cmd) == index
|
||||||
|
)
|
||||||
|
|
||||||
quit: ->
|
quit: ->
|
||||||
atom.workspace.getActivePane().destroyActiveItem()
|
atom.workspace.getActivePane().destroyActiveItem()
|
||||||
|
|
||||||
quitall: ->
|
quitall: ->
|
||||||
atom.close()
|
if !atom.config.get('ex-mode.onlyCloseBuffers')
|
||||||
|
atom.close()
|
||||||
|
else
|
||||||
|
atom.workspace.getTextEditors().forEach (editor) ->
|
||||||
|
editor.destroy()
|
||||||
|
|
||||||
q: => @quit()
|
q: => @quit()
|
||||||
|
|
||||||
|
|
@ -127,11 +151,11 @@ class Ex
|
||||||
|
|
||||||
tabe: (args) => @tabedit(args)
|
tabe: (args) => @tabedit(args)
|
||||||
|
|
||||||
tabnew: ({ range, args }) =>
|
tabnew: (args) =>
|
||||||
if args.trim() is ''
|
if args.args.trim() is ''
|
||||||
atom.workspace.open()
|
atom.workspace.open()
|
||||||
else
|
else
|
||||||
@tabedit(range, args)
|
@tabedit(args)
|
||||||
|
|
||||||
tabclose: (args) => @quit(args)
|
tabclose: (args) => @quit(args)
|
||||||
|
|
||||||
|
|
@ -149,6 +173,16 @@ class Ex
|
||||||
|
|
||||||
tabp: => @tabprevious()
|
tabp: => @tabprevious()
|
||||||
|
|
||||||
|
tabonly: ->
|
||||||
|
tabBar = atom.workspace.getPanes()[0]
|
||||||
|
tabBarElement = atom.views.getView(tabBar).querySelector(".tab-bar")
|
||||||
|
tabBarElement.querySelector(".right-clicked") && tabBarElement.querySelector(".right-clicked").classList.remove("right-clicked")
|
||||||
|
tabBarElement.querySelector(".active").classList.add("right-clicked")
|
||||||
|
atom.commands.dispatch(tabBarElement, 'tabs:close-other-tabs')
|
||||||
|
tabBarElement.querySelector(".active").classList.remove("right-clicked")
|
||||||
|
|
||||||
|
tabo: => @tabonly()
|
||||||
|
|
||||||
edit: ({ range, args, editor }) ->
|
edit: ({ range, args, editor }) ->
|
||||||
filePath = args.trim()
|
filePath = args.trim()
|
||||||
if filePath[0] is '!'
|
if filePath[0] is '!'
|
||||||
|
|
@ -177,9 +211,7 @@ class Ex
|
||||||
e: (args) => @edit(args)
|
e: (args) => @edit(args)
|
||||||
|
|
||||||
enew: ->
|
enew: ->
|
||||||
buffer = atom.workspace.getActiveTextEditor().buffer
|
atom.workspace.open()
|
||||||
buffer.setPath(undefined)
|
|
||||||
buffer.load()
|
|
||||||
|
|
||||||
write: ({ range, args, editor, saveas }) ->
|
write: ({ range, args, editor, saveas }) ->
|
||||||
saveas ?= false
|
saveas ?= false
|
||||||
|
|
@ -194,32 +226,33 @@ class Ex
|
||||||
if filePath.indexOf(' ') isnt -1
|
if filePath.indexOf(' ') isnt -1
|
||||||
throw new CommandError('Only one file name allowed')
|
throw new CommandError('Only one file name allowed')
|
||||||
|
|
||||||
deferred = Promise.defer()
|
deferred = defer()
|
||||||
|
|
||||||
editor = atom.workspace.getActiveTextEditor()
|
editor = atom.workspace.getActiveTextEditor()
|
||||||
saved = false
|
|
||||||
|
# Case 1; path is provided
|
||||||
if filePath.length isnt 0
|
if filePath.length isnt 0
|
||||||
fullPath = getFullPath(filePath)
|
fullPath = getFullPath filePath
|
||||||
if editor.getPath()? and (not fullPath? or editor.getPath() == fullPath)
|
|
||||||
if saveas
|
|
||||||
throw new CommandError("Argument required")
|
|
||||||
else
|
|
||||||
# Use editor.save when no path is given or the path to the file is given
|
|
||||||
trySave(-> editor.save()).then(deferred.resolve)
|
|
||||||
saved = true
|
|
||||||
else if not fullPath?
|
|
||||||
fullPath = atom.showSaveDialogSync()
|
|
||||||
|
|
||||||
if not saved and fullPath?
|
# Only write when it does not exist or we have a force flag set.
|
||||||
if not force and fs.existsSync(fullPath)
|
if force or not fs.existsSync(fullPath)
|
||||||
throw new CommandError("File exists (add ! to override)")
|
editor.saveAs(fullPath)
|
||||||
if saveas
|
return deferred.promise
|
||||||
editor = atom.workspace.getActiveTextEditor()
|
|
||||||
trySave(-> editor.saveAs(fullPath, editor)).then(deferred.resolve)
|
|
||||||
else
|
|
||||||
trySave(-> saveAs(fullPath, editor)).then(deferred.resolve)
|
|
||||||
|
|
||||||
deferred.promise
|
throw new CommandError("File exists (add ! to override)")
|
||||||
|
|
||||||
|
# Case 2; no path provided, call editor save.
|
||||||
|
editor = atom.workspace.getActiveTextEditor()
|
||||||
|
|
||||||
|
# Does the current buffer exist?
|
||||||
|
if editor.getPath()? and fs.existsSync(editor.getPath())
|
||||||
|
trySave(-> editor.save()).then(deferred.promise)
|
||||||
|
else
|
||||||
|
# Cant see what the better API is but Pane.saveActiveItemAs() is the only call
|
||||||
|
# I could find that states it will ask the user.
|
||||||
|
trySave(-> atom.workspace.getActivePane().saveActiveItemAs()).then(deferred.promise)
|
||||||
|
|
||||||
|
return deferred.promise
|
||||||
|
|
||||||
wall: ->
|
wall: ->
|
||||||
atom.workspace.saveAll()
|
atom.workspace.saveAll()
|
||||||
|
|
@ -228,7 +261,7 @@ class Ex
|
||||||
@write(args)
|
@write(args)
|
||||||
|
|
||||||
wq: (args) =>
|
wq: (args) =>
|
||||||
@write(args).then => @quit()
|
@write(args).then(=> @quit())
|
||||||
|
|
||||||
wa: =>
|
wa: =>
|
||||||
@wall()
|
@wall()
|
||||||
|
|
@ -252,19 +285,30 @@ class Ex
|
||||||
|
|
||||||
xit: (args) => @wq(args)
|
xit: (args) => @wq(args)
|
||||||
|
|
||||||
|
x: (args) => @xit(args)
|
||||||
|
|
||||||
split: ({ range, args }) ->
|
split: ({ range, args }) ->
|
||||||
args = args.trim()
|
args = args.trim()
|
||||||
filePaths = args.split(' ')
|
filePaths = args.split(' ')
|
||||||
filePaths = undefined if filePaths.length is 1 and filePaths[0] is ''
|
filePaths = undefined if filePaths.length is 1 and filePaths[0] is ''
|
||||||
pane = atom.workspace.getActivePane()
|
pane = atom.workspace.getActivePane()
|
||||||
if filePaths? and filePaths.length > 0
|
if atom.config.get('ex-mode.splitbelow')
|
||||||
newPane = pane.splitUp()
|
if filePaths? and filePaths.length > 0
|
||||||
for file in filePaths
|
newPane = pane.splitDown()
|
||||||
do ->
|
for file in filePaths
|
||||||
atom.workspace.openURIInPane file, newPane
|
do ->
|
||||||
|
atom.workspace.openURIInPane file, newPane
|
||||||
|
else
|
||||||
|
pane.splitDown(copyActiveItem: true)
|
||||||
else
|
else
|
||||||
pane.splitUp(copyActiveItem: true)
|
if filePaths? and filePaths.length > 0
|
||||||
|
newPane = pane.splitUp()
|
||||||
|
for file in filePaths
|
||||||
|
do ->
|
||||||
|
atom.workspace.openURIInPane file, newPane
|
||||||
|
else
|
||||||
|
pane.splitUp(copyActiveItem: true)
|
||||||
|
|
||||||
|
|
||||||
sp: (args) => @split(args)
|
sp: (args) => @split(args)
|
||||||
|
|
||||||
|
|
@ -300,16 +344,30 @@ class Ex
|
||||||
|
|
||||||
[pattern, substition, flags] = parsed
|
[pattern, substition, flags] = parsed
|
||||||
if pattern is ''
|
if pattern is ''
|
||||||
pattern = vimState.getSearchHistoryItem()
|
if vimState.getSearchHistoryItem?
|
||||||
|
# vim-mode
|
||||||
|
pattern = vimState.getSearchHistoryItem()
|
||||||
|
else if vimState.searchHistory?
|
||||||
|
# vim-mode-plus
|
||||||
|
pattern = vimState.searchHistory.get('prev')
|
||||||
|
|
||||||
if not pattern?
|
if not pattern?
|
||||||
atom.beep()
|
atom.beep()
|
||||||
throw new CommandError('No previous regular expression')
|
throw new CommandError('No previous regular expression')
|
||||||
else
|
else
|
||||||
vimState.pushSearchHistory(pattern)
|
if vimState.pushSearchHistory?
|
||||||
|
# vim-mode
|
||||||
|
vimState.pushSearchHistory(pattern)
|
||||||
|
else if vimState.searchHistory?
|
||||||
|
# vim-mode-plus
|
||||||
|
vimState.searchHistory.save(pattern)
|
||||||
|
|
||||||
try
|
try
|
||||||
flagsObj = {}
|
flagsObj = {}
|
||||||
flags.split('').forEach((flag) -> flagsObj[flag] = true)
|
flags.split('').forEach((flag) -> flagsObj[flag] = true)
|
||||||
|
# gdefault option
|
||||||
|
if atom.config.get('ex-mode.gdefault')
|
||||||
|
flagsObj.g = !flagsObj.g
|
||||||
patternRE = getSearchTerm(pattern, flagsObj)
|
patternRE = getSearchTerm(pattern, flagsObj)
|
||||||
catch e
|
catch e
|
||||||
if e.message.indexOf('Invalid flags supplied to RegExp constructor') is 0
|
if e.message.indexOf('Invalid flags supplied to RegExp constructor') is 0
|
||||||
|
|
@ -335,13 +393,22 @@ class Ex
|
||||||
filePaths = args.split(' ')
|
filePaths = args.split(' ')
|
||||||
filePaths = undefined if filePaths.length is 1 and filePaths[0] is ''
|
filePaths = undefined if filePaths.length is 1 and filePaths[0] is ''
|
||||||
pane = atom.workspace.getActivePane()
|
pane = atom.workspace.getActivePane()
|
||||||
if filePaths? and filePaths.length > 0
|
if atom.config.get('ex-mode.splitright')
|
||||||
newPane = pane.splitLeft()
|
if filePaths? and filePaths.length > 0
|
||||||
for file in filePaths
|
newPane = pane.splitRight()
|
||||||
do ->
|
for file in filePaths
|
||||||
atom.workspace.openURIInPane file, newPane
|
do ->
|
||||||
|
atom.workspace.openURIInPane file, newPane
|
||||||
|
else
|
||||||
|
pane.splitRight(copyActiveItem: true)
|
||||||
else
|
else
|
||||||
pane.splitLeft(copyActiveItem: true)
|
if filePaths? and filePaths.length > 0
|
||||||
|
newPane = pane.splitLeft()
|
||||||
|
for file in filePaths
|
||||||
|
do ->
|
||||||
|
atom.workspace.openURIInPane file, newPane
|
||||||
|
else
|
||||||
|
pane.splitLeft(copyActiveItem: true)
|
||||||
|
|
||||||
vsp: (args) => @vsplit(args)
|
vsp: (args) => @vsplit(args)
|
||||||
|
|
||||||
|
|
@ -382,4 +449,24 @@ class Ex
|
||||||
throw new CommandError("No such option: #{option}")
|
throw new CommandError("No such option: #{option}")
|
||||||
optionProcessor()
|
optionProcessor()
|
||||||
|
|
||||||
|
sort: ({ range }) =>
|
||||||
|
editor = atom.workspace.getActiveTextEditor()
|
||||||
|
sortingRange = [[]]
|
||||||
|
|
||||||
|
# If no range is provided, the entire file should be sorted.
|
||||||
|
isMultiLine = range[1] - range[0] > 1
|
||||||
|
if isMultiLine
|
||||||
|
sortingRange = [[range[0], 0], [range[1] + 1, 0]]
|
||||||
|
else
|
||||||
|
sortingRange = [[0, 0], [editor.getLastBufferRow(), 0]]
|
||||||
|
|
||||||
|
# Store every bufferedRow string in an array.
|
||||||
|
textLines = []
|
||||||
|
for lineIndex in [sortingRange[0][0]..sortingRange[1][0] - 1]
|
||||||
|
textLines.push(editor.lineTextForBufferRow(lineIndex))
|
||||||
|
|
||||||
|
# Sort the array and join them together with newlines for writing back to the file.
|
||||||
|
sortedText = _.sortBy(textLines).join('\n') + '\n'
|
||||||
|
editor.buffer.setTextInRange(sortingRange, sortedText)
|
||||||
|
|
||||||
module.exports = Ex
|
module.exports = Ex
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,40 @@
|
||||||
|
_ = require 'underscore-plus'
|
||||||
|
|
||||||
|
getSearchTerm = (term, modifiers = {'g': true}) ->
|
||||||
|
|
||||||
|
escaped = false
|
||||||
|
hasc = false
|
||||||
|
hasC = false
|
||||||
|
term_ = term
|
||||||
|
term = ''
|
||||||
|
for char in term_
|
||||||
|
if char is '\\' and not escaped
|
||||||
|
escaped = true
|
||||||
|
term += char
|
||||||
|
else
|
||||||
|
if char is 'c' and escaped
|
||||||
|
hasc = true
|
||||||
|
term = term[...-1]
|
||||||
|
else if char is 'C' and escaped
|
||||||
|
hasC = true
|
||||||
|
term = term[...-1]
|
||||||
|
else if char isnt '\\'
|
||||||
|
term += char
|
||||||
|
escaped = false
|
||||||
|
|
||||||
|
if hasC
|
||||||
|
modifiers['i'] = false
|
||||||
|
if (not hasC and not term.match('[A-Z]') and \
|
||||||
|
atom.config.get("vim-mode:useSmartcaseForSearch")) or hasc
|
||||||
|
modifiers['i'] = true
|
||||||
|
|
||||||
|
modFlags = Object.keys(modifiers).filter((key) -> modifiers[key]).join('')
|
||||||
|
|
||||||
|
try
|
||||||
|
new RegExp(term, modFlags)
|
||||||
|
catch
|
||||||
|
new RegExp(_.escapeRegExp(term), modFlags)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
findInBuffer : (buffer, pattern) ->
|
findInBuffer : (buffer, pattern) ->
|
||||||
found = []
|
found = []
|
||||||
|
|
@ -23,4 +60,26 @@ module.exports = {
|
||||||
return found[found.length - 1].start.row
|
return found[found.length - 1].start.row
|
||||||
else
|
else
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
# Returns an array of ranges of all occurences of `term` in `editor`.
|
||||||
|
# The array is sorted so that the first occurences after the cursor come
|
||||||
|
# first (and the search wraps around). If `reverse` is true, the array is
|
||||||
|
# reversed so that the first occurence before the cursor comes first.
|
||||||
|
scanEditor: (term, editor, position, reverse = false) ->
|
||||||
|
[rangesBefore, rangesAfter] = [[], []]
|
||||||
|
editor.scan getSearchTerm(term), ({range}) ->
|
||||||
|
if reverse
|
||||||
|
isBefore = range.start.compare(position) < 0
|
||||||
|
else
|
||||||
|
isBefore = range.start.compare(position) <= 0
|
||||||
|
|
||||||
|
if isBefore
|
||||||
|
rangesBefore.push(range)
|
||||||
|
else
|
||||||
|
rangesAfter.push(range)
|
||||||
|
|
||||||
|
if reverse
|
||||||
|
rangesAfter.concat(rangesBefore).reverse()
|
||||||
|
else
|
||||||
|
rangesAfter.concat(rangesBefore)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,46 @@ class VimOption
|
||||||
nonu: =>
|
nonu: =>
|
||||||
@nonumber()
|
@nonumber()
|
||||||
|
|
||||||
|
splitright: =>
|
||||||
|
atom.config.set("ex-mode.splitright", true)
|
||||||
|
|
||||||
|
spr: =>
|
||||||
|
@splitright()
|
||||||
|
|
||||||
|
nosplitright: =>
|
||||||
|
atom.config.set("ex-mode.splitright", false)
|
||||||
|
|
||||||
|
nospr: =>
|
||||||
|
@nosplitright()
|
||||||
|
|
||||||
|
splitbelow: =>
|
||||||
|
atom.config.set("ex-mode.splitbelow", true)
|
||||||
|
|
||||||
|
sb: =>
|
||||||
|
@splitbelow()
|
||||||
|
|
||||||
|
nosplitbelow: =>
|
||||||
|
atom.config.set("ex-mode.splitbelow", false)
|
||||||
|
|
||||||
|
nosb: =>
|
||||||
|
@nosplitbelow()
|
||||||
|
|
||||||
|
smartcase: =>
|
||||||
|
atom.config.set("vim-mode.useSmartcaseForSearch", true)
|
||||||
|
|
||||||
|
scs: =>
|
||||||
|
@smartcase()
|
||||||
|
|
||||||
|
nosmartcase: =>
|
||||||
|
atom.config.set("vim-mode.useSmartcaseForSearch", false)
|
||||||
|
|
||||||
|
noscs: =>
|
||||||
|
@nosmartcase()
|
||||||
|
|
||||||
|
gdefault: =>
|
||||||
|
atom.config.set("ex-mode.gdefault", true)
|
||||||
|
|
||||||
|
nogdefault: =>
|
||||||
|
atom.config.set("ex-mode.gdefault", false)
|
||||||
|
|
||||||
module.exports = VimOption
|
module.exports = VimOption
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "ex-mode",
|
"name": "ex-mode",
|
||||||
"main": "./lib/ex-mode",
|
"main": "./lib/ex-mode",
|
||||||
"version": "0.8.0",
|
"version": "0.18.0",
|
||||||
"description": "Ex for Atom's vim-mode",
|
"description": "Ex for Atom's vim-mode",
|
||||||
"activationCommands": {
|
"activationCommands": {
|
||||||
"atom-workspace": "ex-mode:open"
|
"atom-workspace": "ex-mode:open"
|
||||||
|
|
@ -23,6 +23,11 @@
|
||||||
"versions": {
|
"versions": {
|
||||||
"^0.1.0": "consumeVim"
|
"^0.1.0": "consumeVim"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"vim-mode-plus": {
|
||||||
|
"versions": {
|
||||||
|
"^0.1.0": "consumeVimModePlus"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"providedServices": {
|
"providedServices": {
|
||||||
|
|
|
||||||
100
spec/autocomplete-spec.coffee
Normal file
100
spec/autocomplete-spec.coffee
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
fs = require 'fs-plus'
|
||||||
|
path = require 'path'
|
||||||
|
os = require 'os'
|
||||||
|
uuid = require 'node-uuid'
|
||||||
|
|
||||||
|
helpers = require './spec-helper'
|
||||||
|
AutoComplete = require '../lib/autocomplete'
|
||||||
|
|
||||||
|
describe "autocomplete functionality", ->
|
||||||
|
beforeEach ->
|
||||||
|
@autoComplete = new AutoComplete(['taba', 'tabb', 'tabc'])
|
||||||
|
@testDir = path.join(os.tmpdir(), "atom-ex-mode-spec-#{uuid.v4()}")
|
||||||
|
@nonExistentTestDir = path.join(os.tmpdir(), "atom-ex-mode-spec-#{uuid.v4()}")
|
||||||
|
@testFile1 = path.join(@testDir, "atom-ex-testfile-a.txt")
|
||||||
|
@testFile2 = path.join(@testDir, "atom-ex-testfile-b.txt")
|
||||||
|
|
||||||
|
runs =>
|
||||||
|
fs.makeTreeSync(@testDir)
|
||||||
|
fs.closeSync(fs.openSync(@testFile1, 'w'));
|
||||||
|
fs.closeSync(fs.openSync(@testFile2, 'w'));
|
||||||
|
spyOn(@autoComplete, 'resetCompletion').andCallThrough()
|
||||||
|
spyOn(@autoComplete, 'getFilePathCompletion').andCallThrough()
|
||||||
|
spyOn(@autoComplete, 'getCommandCompletion').andCallThrough()
|
||||||
|
|
||||||
|
afterEach ->
|
||||||
|
fs.removeSync(@testDir)
|
||||||
|
|
||||||
|
describe "autocomplete commands", ->
|
||||||
|
beforeEach ->
|
||||||
|
@completed = @autoComplete.getAutocomplete('tab')
|
||||||
|
|
||||||
|
it "returns taba", ->
|
||||||
|
expect(@completed).toEqual('taba')
|
||||||
|
|
||||||
|
it "calls command function", ->
|
||||||
|
expect(@autoComplete.getCommandCompletion.callCount).toBe(1)
|
||||||
|
|
||||||
|
describe "autocomplete commands, then autoComplete again", ->
|
||||||
|
beforeEach ->
|
||||||
|
@completed = @autoComplete.getAutocomplete('tab')
|
||||||
|
@completed = @autoComplete.getAutocomplete('tab')
|
||||||
|
|
||||||
|
it "returns tabb", ->
|
||||||
|
expect(@completed).toEqual('tabb')
|
||||||
|
|
||||||
|
it "calls command function", ->
|
||||||
|
expect(@autoComplete.getCommandCompletion.callCount).toBe(1)
|
||||||
|
|
||||||
|
describe "autocomplete directory", ->
|
||||||
|
beforeEach ->
|
||||||
|
filePath = path.join(os.tmpdir(), 'atom-ex-mode-spec-')
|
||||||
|
@completed = @autoComplete.getAutocomplete('tabe ' + filePath)
|
||||||
|
|
||||||
|
it "returns testDir", ->
|
||||||
|
expected = 'tabe ' + @testDir + path.sep
|
||||||
|
expect(@completed).toEqual(expected)
|
||||||
|
|
||||||
|
it "clears autocomplete", ->
|
||||||
|
expect(@autoComplete.resetCompletion.callCount).toBe(1)
|
||||||
|
|
||||||
|
describe "autocomplete directory, then autocomplete again", ->
|
||||||
|
beforeEach ->
|
||||||
|
filePath = path.join(os.tmpdir(), 'atom-ex-mode-spec-')
|
||||||
|
@completed = @autoComplete.getAutocomplete('tabe ' + filePath)
|
||||||
|
@completed = @autoComplete.getAutocomplete(@completed)
|
||||||
|
|
||||||
|
it "returns test file 1", ->
|
||||||
|
expect(@completed).toEqual('tabe ' + @testFile1)
|
||||||
|
|
||||||
|
it "lists files twice", ->
|
||||||
|
expect(@autoComplete.getFilePathCompletion.callCount).toBe(2)
|
||||||
|
|
||||||
|
describe "autocomplete full directory, then autocomplete again", ->
|
||||||
|
beforeEach ->
|
||||||
|
filePath = path.join(@testDir, 'a')
|
||||||
|
@completed = @autoComplete.getAutocomplete('tabe ' + filePath)
|
||||||
|
@completed = @autoComplete.getAutocomplete(@completed)
|
||||||
|
|
||||||
|
it "returns test file 2", ->
|
||||||
|
expect(@completed).toEqual('tabe ' + @testFile2)
|
||||||
|
|
||||||
|
it "lists files once", ->
|
||||||
|
expect(@autoComplete.getFilePathCompletion.callCount).toBe(1)
|
||||||
|
|
||||||
|
describe "autocomplete non existent directory", ->
|
||||||
|
beforeEach ->
|
||||||
|
@completed = @autoComplete.getAutocomplete('tabe ' + @nonExistentTestDir)
|
||||||
|
|
||||||
|
it "returns no completions", ->
|
||||||
|
expected = '';
|
||||||
|
expect(@completed).toEqual(expected)
|
||||||
|
|
||||||
|
describe "autocomplete existing file as directory", ->
|
||||||
|
beforeEach ->
|
||||||
|
filePath = @testFile1 + path.sep
|
||||||
|
@completed = @autoComplete.getAutocomplete('tabe ' + filePath)
|
||||||
|
|
||||||
|
it "returns no completions", ->
|
||||||
|
expected = '';
|
||||||
|
expect(@completed).toEqual(expected)
|
||||||
|
|
@ -11,7 +11,7 @@ describe "the commands", ->
|
||||||
[editor, editorElement, vimState, exState, dir, dir2] = []
|
[editor, editorElement, vimState, exState, dir, dir2] = []
|
||||||
projectPath = (fileName) -> path.join(dir, fileName)
|
projectPath = (fileName) -> path.join(dir, fileName)
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
vimMode = atom.packages.loadPackage('vim-mode')
|
vimMode = atom.packages.loadPackage('vim-mode-plus')
|
||||||
exMode = atom.packages.loadPackage('ex-mode')
|
exMode = atom.packages.loadPackage('ex-mode')
|
||||||
waitsForPromise ->
|
waitsForPromise ->
|
||||||
activationPromise = exMode.activate()
|
activationPromise = exMode.activate()
|
||||||
|
|
@ -36,12 +36,12 @@ describe "the commands", ->
|
||||||
|
|
||||||
helpers.getEditorElement (element) ->
|
helpers.getEditorElement (element) ->
|
||||||
atom.commands.dispatch(element, "ex-mode:open")
|
atom.commands.dispatch(element, "ex-mode:open")
|
||||||
keydown('escape')
|
atom.commands.dispatch(element.getModel().normalModeInputView.editorElement,
|
||||||
|
"core:cancel")
|
||||||
editorElement = element
|
editorElement = element
|
||||||
editor = editorElement.getModel()
|
editor = editorElement.getModel()
|
||||||
vimState = vimMode.mainModule.getEditorState(editor)
|
vimState = vimMode.mainModule.getEditorState(editor)
|
||||||
exState = exMode.mainModule.exStates.get(editor)
|
exState = exMode.mainModule.exStates.get(editor)
|
||||||
vimState.activateNormalMode()
|
|
||||||
vimState.resetNormalMode()
|
vimState.resetNormalMode()
|
||||||
editor.setText("abc\ndef\nabc\ndef")
|
editor.setText("abc\ndef\nabc\ndef")
|
||||||
|
|
||||||
|
|
@ -61,6 +61,99 @@ describe "the commands", ->
|
||||||
commandEditor.getModel().setText(text)
|
commandEditor.getModel().setText(text)
|
||||||
atom.commands.dispatch(commandEditor, "core:confirm")
|
atom.commands.dispatch(commandEditor, "core:confirm")
|
||||||
|
|
||||||
|
openEx = ->
|
||||||
|
atom.commands.dispatch(editorElement, "ex-mode:open")
|
||||||
|
|
||||||
|
describe "as a motion", ->
|
||||||
|
beforeEach ->
|
||||||
|
editor.setCursorBufferPosition([0, 0])
|
||||||
|
|
||||||
|
it "moves the cursor to a specific line", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '2'
|
||||||
|
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [1, 0]
|
||||||
|
|
||||||
|
it "moves to the second address", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '1,3'
|
||||||
|
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [2, 0]
|
||||||
|
|
||||||
|
it "works with offsets", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '2+1'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [2, 0]
|
||||||
|
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '-2'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||||
|
|
||||||
|
it "limits to the last line", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '10'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [3, 0]
|
||||||
|
editor.setCursorBufferPosition([0, 0])
|
||||||
|
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '3,10'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [3, 0]
|
||||||
|
editor.setCursorBufferPosition([0, 0])
|
||||||
|
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '$+1000'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [3, 0]
|
||||||
|
editor.setCursorBufferPosition([0, 0])
|
||||||
|
|
||||||
|
it "goes to the first line with address 0", ->
|
||||||
|
editor.setCursorBufferPosition([2, 0])
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '0'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||||
|
|
||||||
|
editor.setCursorBufferPosition([2, 0])
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '0,0'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||||
|
|
||||||
|
it "doesn't move when the address is the current line", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '.'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||||
|
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText ','
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||||
|
|
||||||
|
it "moves to the last line", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '$'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [3, 0]
|
||||||
|
|
||||||
|
it "moves to a mark's line", ->
|
||||||
|
keydown('l')
|
||||||
|
keydown('m')
|
||||||
|
normalModeInputKeydown 'a'
|
||||||
|
keydown('j')
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText "'a"
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||||
|
|
||||||
|
it "moves to a specified search", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '/def'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [1, 0]
|
||||||
|
|
||||||
|
editor.setCursorBufferPosition([2, 0])
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '?def'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [1, 0]
|
||||||
|
|
||||||
|
editor.setCursorBufferPosition([3, 0])
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText '/ef'
|
||||||
|
expect(editor.getCursorBufferPosition()).toEqual [1, 0]
|
||||||
|
|
||||||
describe ":write", ->
|
describe ":write", ->
|
||||||
describe "when editing a new file", ->
|
describe "when editing a new file", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
|
@ -68,22 +161,23 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "opens the save dialog", ->
|
it "opens the save dialog", ->
|
||||||
spyOn(atom, 'showSaveDialogSync')
|
spyOn(atom, 'showSaveDialogSync')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('write')
|
submitNormalModeInputText('write')
|
||||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||||
|
|
||||||
it "saves when a path is specified in the save dialog", ->
|
it "saves when a path is specified in the save dialog", ->
|
||||||
filePath = projectPath('write-from-save-dialog')
|
filePath = projectPath('write-from-save-dialog')
|
||||||
spyOn(atom, 'showSaveDialogSync').andReturn(filePath)
|
spyOn(atom, 'showSaveDialogSync').andReturn(filePath)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('write')
|
submitNormalModeInputText('write')
|
||||||
expect(fs.existsSync(filePath)).toBe(true)
|
expect(fs.existsSync(filePath)).toBe(true)
|
||||||
expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc\ndef')
|
expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc\ndef')
|
||||||
|
expect(editor.isModified()).toBe(false)
|
||||||
|
|
||||||
it "saves when a path is specified in the save dialog", ->
|
it "saves when a path is specified in the save dialog", ->
|
||||||
spyOn(atom, 'showSaveDialogSync').andReturn(undefined)
|
spyOn(atom, 'showSaveDialogSync').andReturn(undefined)
|
||||||
spyOn(fs, 'writeFileSync')
|
spyOn(fs, 'writeFileSync')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('write')
|
submitNormalModeInputText('write')
|
||||||
expect(fs.writeFileSync.calls.length).toBe(0)
|
expect(fs.writeFileSync.calls.length).toBe(0)
|
||||||
|
|
||||||
|
|
@ -99,7 +193,7 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "saves the file", ->
|
it "saves the file", ->
|
||||||
editor.setText('abc')
|
editor.setText('abc')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('write')
|
submitNormalModeInputText('write')
|
||||||
expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc')
|
expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc')
|
||||||
expect(editor.isModified()).toBe(false)
|
expect(editor.isModified()).toBe(false)
|
||||||
|
|
@ -110,7 +204,7 @@ describe "the commands", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
newPath = path.relative(dir, "#{filePath}.new")
|
newPath = path.relative(dir, "#{filePath}.new")
|
||||||
editor.getBuffer().setText('abc')
|
editor.getBuffer().setText('abc')
|
||||||
keydown(':')
|
openEx()
|
||||||
|
|
||||||
afterEach ->
|
afterEach ->
|
||||||
submitNormalModeInputText("write #{newPath}")
|
submitNormalModeInputText("write #{newPath}")
|
||||||
|
|
@ -132,7 +226,7 @@ describe "the commands", ->
|
||||||
newPath = path.join('~', newPath)
|
newPath = path.join('~', newPath)
|
||||||
|
|
||||||
it "throws an error with more than one path", ->
|
it "throws an error with more than one path", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('write path1 path2')
|
submitNormalModeInputText('write path1 path2')
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
'Command error: Only one file name allowed'
|
'Command error: Only one file name allowed'
|
||||||
|
|
@ -149,7 +243,7 @@ describe "the commands", ->
|
||||||
fs.removeSync(existsPath)
|
fs.removeSync(existsPath)
|
||||||
|
|
||||||
it "throws an error if the file already exists", ->
|
it "throws an error if the file already exists", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText("write #{existsPath}")
|
submitNormalModeInputText("write #{existsPath}")
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
'Command error: File exists (add ! to override)'
|
'Command error: File exists (add ! to override)'
|
||||||
|
|
@ -157,7 +251,7 @@ describe "the commands", ->
|
||||||
expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc')
|
expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc')
|
||||||
|
|
||||||
it "writes if forced with :write!", ->
|
it "writes if forced with :write!", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText("write! #{existsPath}")
|
submitNormalModeInputText("write! #{existsPath}")
|
||||||
expect(atom.notifications.notifications).toEqual([])
|
expect(atom.notifications.notifications).toEqual([])
|
||||||
expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc\ndef')
|
expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc\ndef')
|
||||||
|
|
@ -165,7 +259,7 @@ describe "the commands", ->
|
||||||
describe ":wall", ->
|
describe ":wall", ->
|
||||||
it "saves all", ->
|
it "saves all", ->
|
||||||
spyOn(atom.workspace, 'saveAll')
|
spyOn(atom.workspace, 'saveAll')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('wall')
|
submitNormalModeInputText('wall')
|
||||||
expect(atom.workspace.saveAll).toHaveBeenCalled()
|
expect(atom.workspace.saveAll).toHaveBeenCalled()
|
||||||
|
|
||||||
|
|
@ -176,14 +270,14 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "opens the save dialog", ->
|
it "opens the save dialog", ->
|
||||||
spyOn(atom, 'showSaveDialogSync')
|
spyOn(atom, 'showSaveDialogSync')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('saveas')
|
submitNormalModeInputText('saveas')
|
||||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||||
|
|
||||||
it "saves when a path is specified in the save dialog", ->
|
it "saves when a path is specified in the save dialog", ->
|
||||||
filePath = projectPath('saveas-from-save-dialog')
|
filePath = projectPath('saveas-from-save-dialog')
|
||||||
spyOn(atom, 'showSaveDialogSync').andReturn(filePath)
|
spyOn(atom, 'showSaveDialogSync').andReturn(filePath)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('saveas')
|
submitNormalModeInputText('saveas')
|
||||||
expect(fs.existsSync(filePath)).toBe(true)
|
expect(fs.existsSync(filePath)).toBe(true)
|
||||||
expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc\ndef')
|
expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc\ndef')
|
||||||
|
|
@ -191,7 +285,7 @@ describe "the commands", ->
|
||||||
it "saves when a path is specified in the save dialog", ->
|
it "saves when a path is specified in the save dialog", ->
|
||||||
spyOn(atom, 'showSaveDialogSync').andReturn(undefined)
|
spyOn(atom, 'showSaveDialogSync').andReturn(undefined)
|
||||||
spyOn(fs, 'writeFileSync')
|
spyOn(fs, 'writeFileSync')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('saveas')
|
submitNormalModeInputText('saveas')
|
||||||
expect(fs.writeFileSync.calls.length).toBe(0)
|
expect(fs.writeFileSync.calls.length).toBe(0)
|
||||||
|
|
||||||
|
|
@ -207,7 +301,7 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "complains if no path given", ->
|
it "complains if no path given", ->
|
||||||
editor.setText('abc')
|
editor.setText('abc')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('saveas')
|
submitNormalModeInputText('saveas')
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
'Command error: Argument required'
|
'Command error: Argument required'
|
||||||
|
|
@ -219,7 +313,7 @@ describe "the commands", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
newPath = path.relative(dir, "#{filePath}.new")
|
newPath = path.relative(dir, "#{filePath}.new")
|
||||||
editor.getBuffer().setText('abc')
|
editor.getBuffer().setText('abc')
|
||||||
keydown(':')
|
openEx()
|
||||||
|
|
||||||
afterEach ->
|
afterEach ->
|
||||||
submitNormalModeInputText("saveas #{newPath}")
|
submitNormalModeInputText("saveas #{newPath}")
|
||||||
|
|
@ -241,7 +335,7 @@ describe "the commands", ->
|
||||||
newPath = path.join('~', newPath)
|
newPath = path.join('~', newPath)
|
||||||
|
|
||||||
it "throws an error with more than one path", ->
|
it "throws an error with more than one path", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('saveas path1 path2')
|
submitNormalModeInputText('saveas path1 path2')
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
'Command error: Only one file name allowed'
|
'Command error: Only one file name allowed'
|
||||||
|
|
@ -258,7 +352,7 @@ describe "the commands", ->
|
||||||
fs.removeSync(existsPath)
|
fs.removeSync(existsPath)
|
||||||
|
|
||||||
it "throws an error if the file already exists", ->
|
it "throws an error if the file already exists", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText("saveas #{existsPath}")
|
submitNormalModeInputText("saveas #{existsPath}")
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
'Command error: File exists (add ! to override)'
|
'Command error: File exists (add ! to override)'
|
||||||
|
|
@ -266,7 +360,7 @@ describe "the commands", ->
|
||||||
expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc')
|
expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc')
|
||||||
|
|
||||||
it "writes if forced with :saveas!", ->
|
it "writes if forced with :saveas!", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText("saveas! #{existsPath}")
|
submitNormalModeInputText("saveas! #{existsPath}")
|
||||||
expect(atom.notifications.notifications).toEqual([])
|
expect(atom.notifications.notifications).toEqual([])
|
||||||
expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc\ndef')
|
expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc\ndef')
|
||||||
|
|
@ -280,7 +374,7 @@ describe "the commands", ->
|
||||||
atom.workspace.open()
|
atom.workspace.open()
|
||||||
|
|
||||||
it "closes the active pane item if not modified", ->
|
it "closes the active pane item if not modified", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('quit')
|
submitNormalModeInputText('quit')
|
||||||
expect(pane.destroyActiveItem).toHaveBeenCalled()
|
expect(pane.destroyActiveItem).toHaveBeenCalled()
|
||||||
expect(pane.getItems().length).toBe(1)
|
expect(pane.getItems().length).toBe(1)
|
||||||
|
|
@ -291,14 +385,14 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "opens the prompt to save", ->
|
it "opens the prompt to save", ->
|
||||||
spyOn(pane, 'promptToSaveItem')
|
spyOn(pane, 'promptToSaveItem')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('quit')
|
submitNormalModeInputText('quit')
|
||||||
expect(pane.promptToSaveItem).toHaveBeenCalled()
|
expect(pane.promptToSaveItem).toHaveBeenCalled()
|
||||||
|
|
||||||
describe ":quitall", ->
|
describe ":quitall", ->
|
||||||
it "closes Atom", ->
|
it "closes Atom", ->
|
||||||
spyOn(atom, 'close')
|
spyOn(atom, 'close')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('quitall')
|
submitNormalModeInputText('quitall')
|
||||||
expect(atom.close).toHaveBeenCalled()
|
expect(atom.close).toHaveBeenCalled()
|
||||||
|
|
||||||
|
|
@ -306,7 +400,7 @@ describe "the commands", ->
|
||||||
it "acts as an alias to :quit", ->
|
it "acts as an alias to :quit", ->
|
||||||
spyOn(Ex, 'tabclose').andCallThrough()
|
spyOn(Ex, 'tabclose').andCallThrough()
|
||||||
spyOn(Ex, 'quit').andCallThrough()
|
spyOn(Ex, 'quit').andCallThrough()
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('tabclose')
|
submitNormalModeInputText('tabclose')
|
||||||
expect(Ex.quit).toHaveBeenCalledWith(Ex.tabclose.calls[0].args...)
|
expect(Ex.quit).toHaveBeenCalledWith(Ex.tabclose.calls[0].args...)
|
||||||
|
|
||||||
|
|
@ -320,13 +414,13 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "switches to the next tab", ->
|
it "switches to the next tab", ->
|
||||||
pane.activateItemAtIndex(1)
|
pane.activateItemAtIndex(1)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('tabnext')
|
submitNormalModeInputText('tabnext')
|
||||||
expect(pane.getActiveItemIndex()).toBe(2)
|
expect(pane.getActiveItemIndex()).toBe(2)
|
||||||
|
|
||||||
it "wraps around", ->
|
it "wraps around", ->
|
||||||
pane.activateItemAtIndex(pane.getItems().length - 1)
|
pane.activateItemAtIndex(pane.getItems().length - 1)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('tabnext')
|
submitNormalModeInputText('tabnext')
|
||||||
expect(pane.getActiveItemIndex()).toBe(0)
|
expect(pane.getActiveItemIndex()).toBe(0)
|
||||||
|
|
||||||
|
|
@ -340,13 +434,13 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "switches to the previous tab", ->
|
it "switches to the previous tab", ->
|
||||||
pane.activateItemAtIndex(1)
|
pane.activateItemAtIndex(1)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('tabprevious')
|
submitNormalModeInputText('tabprevious')
|
||||||
expect(pane.getActiveItemIndex()).toBe(0)
|
expect(pane.getActiveItemIndex()).toBe(0)
|
||||||
|
|
||||||
it "wraps around", ->
|
it "wraps around", ->
|
||||||
pane.activateItemAtIndex(0)
|
pane.activateItemAtIndex(0)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('tabprevious')
|
submitNormalModeInputText('tabprevious')
|
||||||
expect(pane.getActiveItemIndex()).toBe(pane.getItems().length - 1)
|
expect(pane.getActiveItemIndex()).toBe(pane.getItems().length - 1)
|
||||||
|
|
||||||
|
|
@ -357,7 +451,7 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "writes the file, then quits", ->
|
it "writes the file, then quits", ->
|
||||||
spyOn(atom, 'showSaveDialogSync').andReturn(projectPath('wq-1'))
|
spyOn(atom, 'showSaveDialogSync').andReturn(projectPath('wq-1'))
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('wq')
|
submitNormalModeInputText('wq')
|
||||||
expect(Ex.write).toHaveBeenCalled()
|
expect(Ex.write).toHaveBeenCalled()
|
||||||
# Since `:wq` only calls `:quit` after `:write` is finished, we need to
|
# Since `:wq` only calls `:quit` after `:write` is finished, we need to
|
||||||
|
|
@ -366,7 +460,7 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "doesn't quit when the file is new and no path is specified in the save dialog", ->
|
it "doesn't quit when the file is new and no path is specified in the save dialog", ->
|
||||||
spyOn(atom, 'showSaveDialogSync').andReturn(undefined)
|
spyOn(atom, 'showSaveDialogSync').andReturn(undefined)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('wq')
|
submitNormalModeInputText('wq')
|
||||||
expect(Ex.write).toHaveBeenCalled()
|
expect(Ex.write).toHaveBeenCalled()
|
||||||
wasNotCalled = false
|
wasNotCalled = false
|
||||||
|
|
@ -376,7 +470,7 @@ describe "the commands", ->
|
||||||
waitsFor((-> wasNotCalled), 100)
|
waitsFor((-> wasNotCalled), 100)
|
||||||
|
|
||||||
it "passes the file name", ->
|
it "passes the file name", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('wq wq-2')
|
submitNormalModeInputText('wq wq-2')
|
||||||
expect(Ex.write)
|
expect(Ex.write)
|
||||||
.toHaveBeenCalled()
|
.toHaveBeenCalled()
|
||||||
|
|
@ -386,15 +480,22 @@ describe "the commands", ->
|
||||||
describe ":xit", ->
|
describe ":xit", ->
|
||||||
it "acts as an alias to :wq", ->
|
it "acts as an alias to :wq", ->
|
||||||
spyOn(Ex, 'wq')
|
spyOn(Ex, 'wq')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('xit')
|
submitNormalModeInputText('xit')
|
||||||
expect(Ex.wq).toHaveBeenCalled()
|
expect(Ex.wq).toHaveBeenCalled()
|
||||||
|
|
||||||
|
describe ":x", ->
|
||||||
|
it "acts as an alias to :xit", ->
|
||||||
|
spyOn(Ex, 'xit')
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText('x')
|
||||||
|
expect(Ex.xit).toHaveBeenCalled()
|
||||||
|
|
||||||
describe ":wqall", ->
|
describe ":wqall", ->
|
||||||
it "calls :wall, then :quitall", ->
|
it "calls :wall, then :quitall", ->
|
||||||
spyOn(Ex, 'wall')
|
spyOn(Ex, 'wall')
|
||||||
spyOn(Ex, 'quitall')
|
spyOn(Ex, 'quitall')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('wqall')
|
submitNormalModeInputText('wqall')
|
||||||
expect(Ex.wall).toHaveBeenCalled()
|
expect(Ex.wall).toHaveBeenCalled()
|
||||||
expect(Ex.quitall).toHaveBeenCalled()
|
expect(Ex.quitall).toHaveBeenCalled()
|
||||||
|
|
@ -406,7 +507,7 @@ describe "the commands", ->
|
||||||
editor.getBuffer().setText('abc')
|
editor.getBuffer().setText('abc')
|
||||||
editor.saveAs(filePath)
|
editor.saveAs(filePath)
|
||||||
fs.writeFileSync(filePath, 'def')
|
fs.writeFileSync(filePath, 'def')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('edit')
|
submitNormalModeInputText('edit')
|
||||||
# Reloading takes a bit
|
# Reloading takes a bit
|
||||||
waitsFor((-> editor.getText() is 'def'),
|
waitsFor((-> editor.getText() is 'def'),
|
||||||
|
|
@ -418,7 +519,7 @@ describe "the commands", ->
|
||||||
editor.saveAs(filePath)
|
editor.saveAs(filePath)
|
||||||
editor.getBuffer().setText('abcd')
|
editor.getBuffer().setText('abcd')
|
||||||
fs.writeFileSync(filePath, 'def')
|
fs.writeFileSync(filePath, 'def')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('edit')
|
submitNormalModeInputText('edit')
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
'Command error: No write since last change (add ! to override)')
|
'Command error: No write since last change (add ! to override)')
|
||||||
|
|
@ -432,7 +533,7 @@ describe "the commands", ->
|
||||||
editor.saveAs(filePath)
|
editor.saveAs(filePath)
|
||||||
editor.getBuffer().setText('abcd')
|
editor.getBuffer().setText('abcd')
|
||||||
fs.writeFileSync(filePath, 'def')
|
fs.writeFileSync(filePath, 'def')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('edit!')
|
submitNormalModeInputText('edit!')
|
||||||
expect(atom.notifications.notifications.length).toBe(0)
|
expect(atom.notifications.notifications.length).toBe(0)
|
||||||
waitsFor((-> editor.getText() is 'def')
|
waitsFor((-> editor.getText() is 'def')
|
||||||
|
|
@ -440,7 +541,7 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "throws an error when editing a new file", ->
|
it "throws an error when editing a new file", ->
|
||||||
editor.getBuffer().reload()
|
editor.getBuffer().reload()
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('edit')
|
submitNormalModeInputText('edit')
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
'Command error: No file name')
|
'Command error: No file name')
|
||||||
|
|
@ -456,18 +557,18 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "opens the specified path", ->
|
it "opens the specified path", ->
|
||||||
filePath = projectPath('edit-new-test')
|
filePath = projectPath('edit-new-test')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText("edit #{filePath}")
|
submitNormalModeInputText("edit #{filePath}")
|
||||||
expect(atom.workspace.open).toHaveBeenCalledWith(filePath)
|
expect(atom.workspace.open).toHaveBeenCalledWith(filePath)
|
||||||
|
|
||||||
it "opens a relative path", ->
|
it "opens a relative path", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('edit edit-relative-test')
|
submitNormalModeInputText('edit edit-relative-test')
|
||||||
expect(atom.workspace.open).toHaveBeenCalledWith(
|
expect(atom.workspace.open).toHaveBeenCalledWith(
|
||||||
projectPath('edit-relative-test'))
|
projectPath('edit-relative-test'))
|
||||||
|
|
||||||
it "throws an error if trying to open more than one file", ->
|
it "throws an error if trying to open more than one file", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('edit edit-new-test-1 edit-new-test-2')
|
submitNormalModeInputText('edit edit-new-test-1 edit-new-test-2')
|
||||||
expect(atom.workspace.open.callCount).toBe(0)
|
expect(atom.workspace.open.callCount).toBe(0)
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
|
|
@ -477,14 +578,14 @@ describe "the commands", ->
|
||||||
it "acts as an alias to :edit if supplied with a path", ->
|
it "acts as an alias to :edit if supplied with a path", ->
|
||||||
spyOn(Ex, 'tabedit').andCallThrough()
|
spyOn(Ex, 'tabedit').andCallThrough()
|
||||||
spyOn(Ex, 'edit')
|
spyOn(Ex, 'edit')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('tabedit tabedit-test')
|
submitNormalModeInputText('tabedit tabedit-test')
|
||||||
expect(Ex.edit).toHaveBeenCalledWith(Ex.tabedit.calls[0].args...)
|
expect(Ex.edit).toHaveBeenCalledWith(Ex.tabedit.calls[0].args...)
|
||||||
|
|
||||||
it "acts as an alias to :tabnew if not supplied with a path", ->
|
it "acts as an alias to :tabnew if not supplied with a path", ->
|
||||||
spyOn(Ex, 'tabedit').andCallThrough()
|
spyOn(Ex, 'tabedit').andCallThrough()
|
||||||
spyOn(Ex, 'tabnew')
|
spyOn(Ex, 'tabnew')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('tabedit ')
|
submitNormalModeInputText('tabedit ')
|
||||||
expect(Ex.tabnew)
|
expect(Ex.tabnew)
|
||||||
.toHaveBeenCalledWith(Ex.tabedit.calls[0].args...)
|
.toHaveBeenCalledWith(Ex.tabedit.calls[0].args...)
|
||||||
|
|
@ -492,31 +593,56 @@ describe "the commands", ->
|
||||||
describe ":tabnew", ->
|
describe ":tabnew", ->
|
||||||
it "opens a new tab", ->
|
it "opens a new tab", ->
|
||||||
spyOn(atom.workspace, 'open')
|
spyOn(atom.workspace, 'open')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('tabnew')
|
submitNormalModeInputText('tabnew')
|
||||||
expect(atom.workspace.open).toHaveBeenCalled()
|
expect(atom.workspace.open).toHaveBeenCalled()
|
||||||
|
|
||||||
|
it "opens a new tab for editing when provided an argument", ->
|
||||||
|
spyOn(Ex, 'tabnew').andCallThrough()
|
||||||
|
spyOn(Ex, 'tabedit')
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText('tabnew tabnew-test')
|
||||||
|
expect(Ex.tabedit)
|
||||||
|
.toHaveBeenCalledWith(Ex.tabnew.calls[0].args...)
|
||||||
|
|
||||||
describe ":split", ->
|
describe ":split", ->
|
||||||
it "splits the current file upwards", ->
|
it "splits the current file upwards/downward", ->
|
||||||
pane = atom.workspace.getActivePane()
|
pane = atom.workspace.getActivePane()
|
||||||
spyOn(pane, 'splitUp').andCallThrough()
|
if atom.config.get('ex-mode.splitbelow')
|
||||||
filePath = projectPath('split')
|
spyOn(pane, 'splitDown').andCallThrough()
|
||||||
editor.saveAs(filePath)
|
filePath = projectPath('split')
|
||||||
keydown(':')
|
editor.saveAs(filePath)
|
||||||
submitNormalModeInputText('split')
|
openEx()
|
||||||
expect(pane.splitUp).toHaveBeenCalled()
|
submitNormalModeInputText('split')
|
||||||
|
expect(pane.splitDown).toHaveBeenCalled()
|
||||||
|
else
|
||||||
|
spyOn(pane, 'splitUp').andCallThrough()
|
||||||
|
filePath = projectPath('split')
|
||||||
|
editor.saveAs(filePath)
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText('split')
|
||||||
|
expect(pane.splitUp).toHaveBeenCalled()
|
||||||
# FIXME: Should test whether the new pane contains a TextEditor
|
# FIXME: Should test whether the new pane contains a TextEditor
|
||||||
# pointing to the same path
|
# pointing to the same path
|
||||||
|
|
||||||
describe ":vsplit", ->
|
describe ":vsplit", ->
|
||||||
it "splits the current file to the left", ->
|
it "splits the current file to the left/right", ->
|
||||||
pane = atom.workspace.getActivePane()
|
if atom.config.get('ex-mode.splitright')
|
||||||
spyOn(pane, 'splitLeft').andCallThrough()
|
pane = atom.workspace.getActivePane()
|
||||||
filePath = projectPath('vsplit')
|
spyOn(pane, 'splitRight').andCallThrough()
|
||||||
editor.saveAs(filePath)
|
filePath = projectPath('vsplit')
|
||||||
keydown(':')
|
editor.saveAs(filePath)
|
||||||
submitNormalModeInputText('vsplit')
|
openEx()
|
||||||
expect(pane.splitLeft).toHaveBeenCalled()
|
submitNormalModeInputText('vsplit')
|
||||||
|
expect(pane.splitLeft).toHaveBeenCalled()
|
||||||
|
else
|
||||||
|
pane = atom.workspace.getActivePane()
|
||||||
|
spyOn(pane, 'splitLeft').andCallThrough()
|
||||||
|
filePath = projectPath('vsplit')
|
||||||
|
editor.saveAs(filePath)
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText('vsplit')
|
||||||
|
expect(pane.splitLeft).toHaveBeenCalled()
|
||||||
# FIXME: Should test whether the new pane contains a TextEditor
|
# FIXME: Should test whether the new pane contains a TextEditor
|
||||||
# pointing to the same path
|
# pointing to the same path
|
||||||
|
|
||||||
|
|
@ -526,32 +652,31 @@ describe "the commands", ->
|
||||||
editor.setCursorBufferPosition([2, 0])
|
editor.setCursorBufferPosition([2, 0])
|
||||||
|
|
||||||
it "deletes the current line", ->
|
it "deletes the current line", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('delete')
|
submitNormalModeInputText('delete')
|
||||||
expect(editor.getText()).toEqual('abc\ndef\njkl')
|
expect(editor.getText()).toEqual('abc\ndef\njkl')
|
||||||
|
|
||||||
it "copies the deleted text", ->
|
it "copies the deleted text", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('delete')
|
submitNormalModeInputText('delete')
|
||||||
expect(atom.clipboard.read()).toEqual('ghi\n')
|
expect(atom.clipboard.read()).toEqual('ghi\n')
|
||||||
|
|
||||||
it "deletes the lines in the given range", ->
|
it "deletes the lines in the given range", ->
|
||||||
processedOpStack = false
|
processedOpStack = false
|
||||||
exState.onDidProcessOpStack -> processedOpStack = true
|
exState.onDidProcessOpStack -> processedOpStack = true
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('1,2delete')
|
submitNormalModeInputText('1,2delete')
|
||||||
expect(editor.getText()).toEqual('ghi\njkl')
|
expect(editor.getText()).toEqual('ghi\njkl')
|
||||||
|
|
||||||
waitsFor -> processedOpStack
|
waitsFor -> processedOpStack
|
||||||
editor.setText('abc\ndef\nghi\njkl')
|
editor.setText('abc\ndef\nghi\njkl')
|
||||||
editor.setCursorBufferPosition([1, 1])
|
editor.setCursorBufferPosition([1, 1])
|
||||||
# For some reason, keydown(':') doesn't work here :/
|
|
||||||
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
submitNormalModeInputText(',/k/delete')
|
submitNormalModeInputText(',/k/delete')
|
||||||
expect(editor.getText()).toEqual('abc\n')
|
expect(editor.getText()).toEqual('abc\n')
|
||||||
|
|
||||||
it "undos deleting several lines at once", ->
|
it "undos deleting several lines at once", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('-1,.delete')
|
submitNormalModeInputText('-1,.delete')
|
||||||
expect(editor.getText()).toEqual('abc\njkl')
|
expect(editor.getText()).toEqual('abc\njkl')
|
||||||
atom.commands.dispatch(editorElement, 'core:undo')
|
atom.commands.dispatch(editorElement, 'core:undo')
|
||||||
|
|
@ -563,17 +688,17 @@ describe "the commands", ->
|
||||||
editor.setCursorBufferPosition([0, 0])
|
editor.setCursorBufferPosition([0, 0])
|
||||||
|
|
||||||
it "replaces a character on the current line", ->
|
it "replaces a character on the current line", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute /a/x')
|
submitNormalModeInputText(':substitute /a/x')
|
||||||
expect(editor.getText()).toEqual('xbcaABC\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('xbcaABC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "doesn't need a space before the arguments", ->
|
it "doesn't need a space before the arguments", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/a/x')
|
submitNormalModeInputText(':substitute/a/x')
|
||||||
expect(editor.getText()).toEqual('xbcaABC\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('xbcaABC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "respects modifiers passed to it", ->
|
it "respects modifiers passed to it", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/a/x/g')
|
submitNormalModeInputText(':substitute/a/x/g')
|
||||||
expect(editor.getText()).toEqual('xbcxABC\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('xbcxABC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
|
|
@ -582,7 +707,7 @@ describe "the commands", ->
|
||||||
expect(editor.getText()).toEqual('xbcxxBC\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('xbcxxBC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "replaces on multiple lines", ->
|
it "replaces on multiple lines", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':%substitute/abc/ghi')
|
submitNormalModeInputText(':%substitute/abc/ghi')
|
||||||
expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nghiaABC')
|
expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nghiaABC')
|
||||||
|
|
||||||
|
|
@ -590,24 +715,35 @@ describe "the commands", ->
|
||||||
submitNormalModeInputText(':%substitute/abc/ghi/ig')
|
submitNormalModeInputText(':%substitute/abc/ghi/ig')
|
||||||
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nghiaghi')
|
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nghiaghi')
|
||||||
|
|
||||||
|
it "set gdefault option", ->
|
||||||
|
openEx()
|
||||||
|
atom.config.set('ex-mode.gdefault', true)
|
||||||
|
submitNormalModeInputText(':substitute/a/x')
|
||||||
|
expect(editor.getText()).toEqual('xbcxABC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
atom.config.set('ex-mode.gdefault', true)
|
||||||
|
submitNormalModeInputText(':substitute/a/x/g')
|
||||||
|
expect(editor.getText()).toEqual('xbcaABC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
describe ":yank", ->
|
describe ":yank", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
editor.setText('abc\ndef\nghi\njkl')
|
editor.setText('abc\ndef\nghi\njkl')
|
||||||
editor.setCursorBufferPosition([2, 0])
|
editor.setCursorBufferPosition([2, 0])
|
||||||
|
|
||||||
it "yanks the current line", ->
|
it "yanks the current line", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('yank')
|
submitNormalModeInputText('yank')
|
||||||
expect(atom.clipboard.read()).toEqual('ghi\n')
|
expect(atom.clipboard.read()).toEqual('ghi\n')
|
||||||
|
|
||||||
it "yanks the lines in the given range", ->
|
it "yanks the lines in the given range", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('1,2yank')
|
submitNormalModeInputText('1,2yank')
|
||||||
expect(atom.clipboard.read()).toEqual('abc\ndef\n')
|
expect(atom.clipboard.read()).toEqual('abc\ndef\n')
|
||||||
|
|
||||||
describe "illegal delimiters", ->
|
describe "illegal delimiters", ->
|
||||||
test = (delim) ->
|
test = (delim) ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(":substitute #{delim}a#{delim}x#{delim}gi")
|
submitNormalModeInputText(":substitute #{delim}a#{delim}x#{delim}gi")
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
"Command error: Regular expressions can't be delimited by alphanumeric characters, '\\', '\"' or '|'")
|
"Command error: Regular expressions can't be delimited by alphanumeric characters, '\\', '\"' or '|'")
|
||||||
|
|
@ -624,12 +760,12 @@ describe "the commands", ->
|
||||||
editor.setText('abcabc\nabcabc')
|
editor.setText('abcabc\nabcabc')
|
||||||
|
|
||||||
it "removes the pattern without modifiers", ->
|
it "removes the pattern without modifiers", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(":substitute/abc//")
|
submitNormalModeInputText(":substitute/abc//")
|
||||||
expect(editor.getText()).toEqual('abc\nabcabc')
|
expect(editor.getText()).toEqual('abc\nabcabc')
|
||||||
|
|
||||||
it "removes the pattern with modifiers", ->
|
it "removes the pattern with modifiers", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(":substitute/abc//g")
|
submitNormalModeInputText(":substitute/abc//g")
|
||||||
expect(editor.getText()).toEqual('\nabcabc')
|
expect(editor.getText()).toEqual('\nabcabc')
|
||||||
|
|
||||||
|
|
@ -638,7 +774,7 @@ describe "the commands", ->
|
||||||
editor.setText('abc,def,ghi')
|
editor.setText('abc,def,ghi')
|
||||||
|
|
||||||
test = (escapeChar, escaped) ->
|
test = (escapeChar, escaped) ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(":substitute/,/\\#{escapeChar}/g")
|
submitNormalModeInputText(":substitute/,/\\#{escapeChar}/g")
|
||||||
expect(editor.getText()).toEqual("abc#{escaped}def#{escaped}ghi")
|
expect(editor.getText()).toEqual("abc#{escaped}def#{escaped}ghi")
|
||||||
|
|
||||||
|
|
@ -653,26 +789,26 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "uses case sensitive search if smartcase is off and the pattern is lowercase", ->
|
it "uses case sensitive search if smartcase is off and the pattern is lowercase", ->
|
||||||
atom.config.set('vim-mode.useSmartcaseForSearch', false)
|
atom.config.set('vim-mode.useSmartcaseForSearch', false)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/abc/ghi/g')
|
submitNormalModeInputText(':substitute/abc/ghi/g')
|
||||||
expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "uses case sensitive search if smartcase is off and the pattern is uppercase", ->
|
it "uses case sensitive search if smartcase is off and the pattern is uppercase", ->
|
||||||
editor.setText('abcaABC\ndefdDEF\nabcaABC')
|
editor.setText('abcaABC\ndefdDEF\nabcaABC')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/ABC/ghi/g')
|
submitNormalModeInputText(':substitute/ABC/ghi/g')
|
||||||
expect(editor.getText()).toEqual('abcaghi\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('abcaghi\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "uses case insensitive search if smartcase is on and the pattern is lowercase", ->
|
it "uses case insensitive search if smartcase is on and the pattern is lowercase", ->
|
||||||
editor.setText('abcaABC\ndefdDEF\nabcaABC')
|
editor.setText('abcaABC\ndefdDEF\nabcaABC')
|
||||||
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/abc/ghi/g')
|
submitNormalModeInputText(':substitute/abc/ghi/g')
|
||||||
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "uses case sensitive search if smartcase is on and the pattern is uppercase", ->
|
it "uses case sensitive search if smartcase is on and the pattern is uppercase", ->
|
||||||
editor.setText('abcaABC\ndefdDEF\nabcaABC')
|
editor.setText('abcaABC\ndefdDEF\nabcaABC')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/ABC/ghi/g')
|
submitNormalModeInputText(':substitute/ABC/ghi/g')
|
||||||
expect(editor.getText()).toEqual('abcaghi\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('abcaghi\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
|
|
@ -682,37 +818,37 @@ describe "the commands", ->
|
||||||
|
|
||||||
it "uses case insensitive search if smartcase is off and \c is in the pattern", ->
|
it "uses case insensitive search if smartcase is off and \c is in the pattern", ->
|
||||||
atom.config.set('vim-mode.useSmartcaseForSearch', false)
|
atom.config.set('vim-mode.useSmartcaseForSearch', false)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/abc\\c/ghi/g')
|
submitNormalModeInputText(':substitute/abc\\c/ghi/g')
|
||||||
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "doesn't matter where in the pattern \\c is", ->
|
it "doesn't matter where in the pattern \\c is", ->
|
||||||
atom.config.set('vim-mode.useSmartcaseForSearch', false)
|
atom.config.set('vim-mode.useSmartcaseForSearch', false)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/a\\cbc/ghi/g')
|
submitNormalModeInputText(':substitute/a\\cbc/ghi/g')
|
||||||
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "uses case sensitive search if smartcase is on, \\C is in the pattern and the pattern is lowercase", ->
|
it "uses case sensitive search if smartcase is on, \\C is in the pattern and the pattern is lowercase", ->
|
||||||
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/a\\Cbc/ghi/g')
|
submitNormalModeInputText(':substitute/a\\Cbc/ghi/g')
|
||||||
expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "overrides \\C with \\c if \\C comes first", ->
|
it "overrides \\C with \\c if \\C comes first", ->
|
||||||
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/a\\Cb\\cc/ghi/g')
|
submitNormalModeInputText(':substitute/a\\Cb\\cc/ghi/g')
|
||||||
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "overrides \\C with \\c if \\c comes first", ->
|
it "overrides \\C with \\c if \\c comes first", ->
|
||||||
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/a\\cb\\Cc/ghi/g')
|
submitNormalModeInputText(':substitute/a\\cb\\Cc/ghi/g')
|
||||||
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "overrides an appended /i flag with \\C", ->
|
it "overrides an appended /i flag with \\C", ->
|
||||||
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
atom.config.set('vim-mode.useSmartcaseForSearch', true)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/ab\\Cc/ghi/gi')
|
submitNormalModeInputText(':substitute/ab\\Cc/ghi/gi')
|
||||||
expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
|
|
@ -721,23 +857,23 @@ describe "the commands", ->
|
||||||
editor.setText('abcaABC\ndefdDEF\nabcaABC')
|
editor.setText('abcaABC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "replaces \\1 with the first group", ->
|
it "replaces \\1 with the first group", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/bc(.{2})/X\\1X')
|
submitNormalModeInputText(':substitute/bc(.{2})/X\\1X')
|
||||||
expect(editor.getText()).toEqual('aXaAXBC\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('aXaAXBC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "replaces multiple groups", ->
|
it "replaces multiple groups", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/a([a-z]*)aA([A-Z]*)/X\\1XY\\2Y')
|
submitNormalModeInputText(':substitute/a([a-z]*)aA([A-Z]*)/X\\1XY\\2Y')
|
||||||
expect(editor.getText()).toEqual('XbcXYBCY\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('XbcXYBCY\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
it "replaces \\0 with the entire match", ->
|
it "replaces \\0 with the entire match", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':substitute/ab(ca)AB/X\\0X')
|
submitNormalModeInputText(':substitute/ab(ca)AB/X\\0X')
|
||||||
expect(editor.getText()).toEqual('XabcaABXC\ndefdDEF\nabcaABC')
|
expect(editor.getText()).toEqual('XabcaABXC\ndefdDEF\nabcaABC')
|
||||||
|
|
||||||
describe ":set", ->
|
describe ":set", ->
|
||||||
it "throws an error without a specified option", ->
|
it "throws an error without a specified option", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':set')
|
submitNormalModeInputText(':set')
|
||||||
expect(atom.notifications.notifications[0].message).toEqual(
|
expect(atom.notifications.notifications[0].message).toEqual(
|
||||||
'Command error: No option specified')
|
'Command error: No option specified')
|
||||||
|
|
@ -745,7 +881,7 @@ describe "the commands", ->
|
||||||
it "sets multiple options at once", ->
|
it "sets multiple options at once", ->
|
||||||
atom.config.set('editor.showInvisibles', false)
|
atom.config.set('editor.showInvisibles', false)
|
||||||
atom.config.set('editor.showLineNumbers', false)
|
atom.config.set('editor.showLineNumbers', false)
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':set list number')
|
submitNormalModeInputText(':set list number')
|
||||||
expect(atom.config.get('editor.showInvisibles')).toBe(true)
|
expect(atom.config.get('editor.showInvisibles')).toBe(true)
|
||||||
expect(atom.config.get('editor.showLineNumbers')).toBe(true)
|
expect(atom.config.get('editor.showLineNumbers')).toBe(true)
|
||||||
|
|
@ -756,7 +892,7 @@ describe "the commands", ->
|
||||||
atom.config.set('editor.showLineNumbers', false)
|
atom.config.set('editor.showLineNumbers', false)
|
||||||
|
|
||||||
it "sets (no)list", ->
|
it "sets (no)list", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':set list')
|
submitNormalModeInputText(':set list')
|
||||||
expect(atom.config.get('editor.showInvisibles')).toBe(true)
|
expect(atom.config.get('editor.showInvisibles')).toBe(true)
|
||||||
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
|
@ -764,7 +900,7 @@ describe "the commands", ->
|
||||||
expect(atom.config.get('editor.showInvisibles')).toBe(false)
|
expect(atom.config.get('editor.showInvisibles')).toBe(false)
|
||||||
|
|
||||||
it "sets (no)nu(mber)", ->
|
it "sets (no)nu(mber)", ->
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText(':set nu')
|
submitNormalModeInputText(':set nu')
|
||||||
expect(atom.config.get('editor.showLineNumbers')).toBe(true)
|
expect(atom.config.get('editor.showLineNumbers')).toBe(true)
|
||||||
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
|
@ -777,11 +913,61 @@ describe "the commands", ->
|
||||||
submitNormalModeInputText(':set nonumber')
|
submitNormalModeInputText(':set nonumber')
|
||||||
expect(atom.config.get('editor.showLineNumbers')).toBe(false)
|
expect(atom.config.get('editor.showLineNumbers')).toBe(false)
|
||||||
|
|
||||||
|
it "sets (no)sp(lit)r(ight)", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText(':set spr')
|
||||||
|
expect(atom.config.get('ex-mode.splitright')).toBe(true)
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
submitNormalModeInputText(':set nospr')
|
||||||
|
expect(atom.config.get('ex-mode.splitright')).toBe(false)
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
submitNormalModeInputText(':set splitright')
|
||||||
|
expect(atom.config.get('ex-mode.splitright')).toBe(true)
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
submitNormalModeInputText(':set nosplitright')
|
||||||
|
expect(atom.config.get('ex-mode.splitright')).toBe(false)
|
||||||
|
|
||||||
|
it "sets (no)s(plit)b(elow)", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText(':set sb')
|
||||||
|
expect(atom.config.get('ex-mode.splitbelow')).toBe(true)
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
submitNormalModeInputText(':set nosb')
|
||||||
|
expect(atom.config.get('ex-mode.splitbelow')).toBe(false)
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
submitNormalModeInputText(':set splitbelow')
|
||||||
|
expect(atom.config.get('ex-mode.splitbelow')).toBe(true)
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
submitNormalModeInputText(':set nosplitbelow')
|
||||||
|
expect(atom.config.get('ex-mode.splitbelow')).toBe(false)
|
||||||
|
|
||||||
|
it "sets (no)s(mart)c(a)s(e)", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText(':set scs')
|
||||||
|
expect(atom.config.get('vim-mode.useSmartcaseForSearch')).toBe(true)
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText(':set noscs')
|
||||||
|
expect(atom.config.get('vim-mode.useSmartcaseForSearch')).toBe(false)
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText(':set smartcase')
|
||||||
|
expect(atom.config.get('vim-mode.useSmartcaseForSearch')).toBe(true)
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText(':set nosmartcase')
|
||||||
|
expect(atom.config.get('vim-mode.useSmartcaseForSearch')).toBe(false)
|
||||||
|
|
||||||
|
it "sets (no)gdefault", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText(':set gdefault')
|
||||||
|
expect(atom.config.get('ex-mode.gdefault')).toBe(true)
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
submitNormalModeInputText(':set nogdefault')
|
||||||
|
expect(atom.config.get('ex-mode.gdefault')).toBe(false)
|
||||||
|
|
||||||
describe "aliases", ->
|
describe "aliases", ->
|
||||||
it "calls the aliased function without arguments", ->
|
it "calls the aliased function without arguments", ->
|
||||||
ExClass.registerAlias('W', 'w')
|
ExClass.registerAlias('W', 'w')
|
||||||
spyOn(Ex, 'write')
|
spyOn(Ex, 'write')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('W')
|
submitNormalModeInputText('W')
|
||||||
expect(Ex.write).toHaveBeenCalled()
|
expect(Ex.write).toHaveBeenCalled()
|
||||||
|
|
||||||
|
|
@ -789,8 +975,45 @@ describe "the commands", ->
|
||||||
ExClass.registerAlias('W', 'write')
|
ExClass.registerAlias('W', 'write')
|
||||||
spyOn(Ex, 'W').andCallThrough()
|
spyOn(Ex, 'W').andCallThrough()
|
||||||
spyOn(Ex, 'write')
|
spyOn(Ex, 'write')
|
||||||
keydown(':')
|
openEx()
|
||||||
submitNormalModeInputText('W')
|
submitNormalModeInputText('W')
|
||||||
WArgs = Ex.W.calls[0].args[0]
|
WArgs = Ex.W.calls[0].args[0]
|
||||||
writeArgs = Ex.write.calls[0].args[0]
|
writeArgs = Ex.write.calls[0].args[0]
|
||||||
expect(WArgs).toBe writeArgs
|
expect(WArgs).toBe writeArgs
|
||||||
|
|
||||||
|
describe "with selections", ->
|
||||||
|
it "executes on the selected range", ->
|
||||||
|
spyOn(Ex, 's')
|
||||||
|
editor.setCursorBufferPosition([0, 0])
|
||||||
|
editor.selectToBufferPosition([2, 1])
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
submitNormalModeInputText("'<,'>s/abc/def")
|
||||||
|
expect(Ex.s.calls[0].args[0].range).toEqual [0, 2]
|
||||||
|
|
||||||
|
it "calls the functions multiple times if there are multiple selections", ->
|
||||||
|
spyOn(Ex, 's')
|
||||||
|
editor.setCursorBufferPosition([0, 0])
|
||||||
|
editor.selectToBufferPosition([2, 1])
|
||||||
|
editor.addCursorAtBufferPosition([3, 0])
|
||||||
|
editor.selectToBufferPosition([3, 2])
|
||||||
|
atom.commands.dispatch(editorElement, 'ex-mode:open')
|
||||||
|
submitNormalModeInputText("'<,'>s/abc/def")
|
||||||
|
calls = Ex.s.calls
|
||||||
|
expect(calls.length).toEqual 2
|
||||||
|
expect(calls[0].args[0].range).toEqual [0, 2]
|
||||||
|
expect(calls[1].args[0].range).toEqual [3, 3]
|
||||||
|
|
||||||
|
describe ':sort', ->
|
||||||
|
beforeEach ->
|
||||||
|
editor.setText('ghi\nabc\njkl\ndef\n142\nzzz\n91xfds9\n')
|
||||||
|
editor.setCursorBufferPosition([0, 0])
|
||||||
|
|
||||||
|
it "sorts entire file if range is not multi-line", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText('sort')
|
||||||
|
expect(editor.getText()).toEqual('142\n91xfds9\nabc\ndef\nghi\njkl\nzzz\n')
|
||||||
|
|
||||||
|
it "sorts specific range if range is multi-line", ->
|
||||||
|
openEx()
|
||||||
|
submitNormalModeInputText('2,4sort')
|
||||||
|
expect(editor.getText()).toEqual('ghi\nabc\ndef\njkl\n142\nzzz\n91xfds9\n')
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ helpers = require './spec-helper'
|
||||||
describe "the input element", ->
|
describe "the input element", ->
|
||||||
[editor, editorElement, vimState, exState] = []
|
[editor, editorElement, vimState, exState] = []
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
vimMode = atom.packages.loadPackage('vim-mode')
|
vimMode = atom.packages.loadPackage('vim-mode-plus')
|
||||||
exMode = atom.packages.loadPackage('ex-mode')
|
exMode = atom.packages.loadPackage('ex-mode')
|
||||||
waitsForPromise ->
|
waitsForPromise ->
|
||||||
activationPromise = exMode.activate()
|
activationPromise = exMode.activate()
|
||||||
|
|
@ -26,7 +26,6 @@ describe "the input element", ->
|
||||||
atom.commands.dispatch(getCommandEditor(), "core:cancel")
|
atom.commands.dispatch(getCommandEditor(), "core:cancel")
|
||||||
vimState = vimMode.mainModule.getEditorState(editor)
|
vimState = vimMode.mainModule.getEditorState(editor)
|
||||||
exState = exMode.mainModule.exStates.get(editor)
|
exState = exMode.mainModule.exStates.get(editor)
|
||||||
vimState.activateNormalMode()
|
|
||||||
vimState.resetNormalMode()
|
vimState.resetNormalMode()
|
||||||
editor.setText("abc\ndef\nabc\ndef")
|
editor.setText("abc\ndef\nabc\ndef")
|
||||||
|
|
||||||
|
|
@ -70,6 +69,7 @@ describe "the input element", ->
|
||||||
expect(getVisibility()).toBe true
|
expect(getVisibility()).toBe true
|
||||||
commandEditor = getCommandEditor()
|
commandEditor = getCommandEditor()
|
||||||
model = commandEditor.getModel()
|
model = commandEditor.getModel()
|
||||||
|
expect(model.getText()).toBe ''
|
||||||
model.setText('abc')
|
model.setText('abc')
|
||||||
atom.commands.dispatch(commandEditor, "core:backspace")
|
atom.commands.dispatch(commandEditor, "core:backspace")
|
||||||
expect(getVisibility()).toBe true
|
expect(getVisibility()).toBe true
|
||||||
|
|
@ -82,3 +82,11 @@ describe "the input element", ->
|
||||||
expect(model.getText()).toBe ''
|
expect(model.getText()).toBe ''
|
||||||
atom.commands.dispatch(commandEditor, "core:backspace")
|
atom.commands.dispatch(commandEditor, "core:backspace")
|
||||||
expect(getVisibility()).toBe false
|
expect(getVisibility()).toBe false
|
||||||
|
|
||||||
|
it "contains '<,'> when opened while there are selections", ->
|
||||||
|
editor.setCursorBufferPosition([0, 0])
|
||||||
|
editor.selectToBufferPosition([0, 1])
|
||||||
|
editor.addCursorAtBufferPosition([2, 0])
|
||||||
|
editor.selectToBufferPosition([2, 1])
|
||||||
|
atom.commands.dispatch(editorElement, "ex-mode:open")
|
||||||
|
expect(getCommandEditor().getModel().getText()).toBe "'<,'>"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue