diff --git a/.gitignore b/.gitignore index 8702603e218..e63d34e7fd8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ node_modules *.sw? .vscode-test .DS_Store -*.vsix *.log typings/* diff --git a/.vscode/settings.json b/.vscode/settings.json index d8b16d8e43c..551a669ad26 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,6 @@ }, "typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version "editor.tabSize": 2, - "editor.insertSpaces": true + "editor.insertSpaces": true, + "editor.formatOnSave": true } \ No newline at end of file diff --git a/README.md b/README.md index 9f31a4275d5..ce4c21475d8 100644 --- a/README.md +++ b/README.md @@ -1,494 +1,42 @@ -


VSCodeVim

+


VSCodeNeoVim

Vim emulation for Visual Studio Code.

-![http://aka.ms/vscodevim](https://vsmarketplacebadge.apphb.com/version/vscodevim.vim.svg) -![https://travis-ci.org/VSCodeVim/Vim]( https://travis-ci.org/VSCodeVim/Vim.svg?branch=master) -
-VSCodeVim is a [Visual Studio Code](https://code.visualstudio.com/) extension that enables Vim keybindings, including: - -* Modes: normal, insert, command-line, visual, visual line, visual block -* Command combinations (`c3w`, `daw`, `2dd`, etc) -* Highly versatile command remapping (`jj` to ``, `:` to command panel, etc.) -* Highly versatile command remapping (`jj` to ``, `:` to command panel, etc.) -* Incremental search with `/` and `?` -* Marks -* Popular vim plugin features built-in (easymotion, surround, commentary) -* Vim settings similar to those found in .vimrc -* Multi-cursor support, run vim commands everywhere! -* And much more! Refer to the [roadmap](ROADMAP.md) for everything we support. - -Please [report missing features/bugs on GitHub](https://github.com/VSCodeVim/Vim/issues), which will help us get to them faster. - -Ask us questions, talk about contributing, or just say hi on [Slack](https://vscodevim-slackin.azurewebsites.net)! - -## Donations - -[![Donate](https://www.paypalobjects.com/webstatic/en_US/i/btn/png/btn_donate_92x26.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FNUBXQADN5VG4) - -[Make a donation to VSCodeVim here!](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FNUBXQADN5VG4) - -Donations help convince me to work on this project rather than my other (non-open-source) projects. I'd love to work on VSCodeVim full time, but I need money to live! - -## Contents - -* [Getting Started](#getting-started) - * [Mac setup](#mac-setup) - * [Windows setup](#windows-setup) -* [Settings](#settings) - * [VSCodeVim settings](#vscodevim-settings) - * [Neovim Integration](#neovim-integration) - * [Key remapping](#key-remapping) - * [Vim settings](#vim-settings) - * [Status bar colors (vim-airline)](#status-bar-color-settings) -* [Multi-cursor mode](#multi-cursor-mode) -* [Emulated plugins](#emulated-plugins) - * [vim-easymotion](#vim-easymotion) - * [vim-surround](#vim-surround) - * [vim-commentary](#vim-commentary) - * [vim-indent-object](#vim-indent-object) -* [VSCodeVim tricks](#vscodevim-tricks) -* [F.A.Q / Troubleshooting](#faq) -* [Contributing](#contributing) -* [Release notes](https://github.com/VSCodeVim/Vim/releases) - -## Getting started - -The plugin will be automatically enabled after [installing](https://marketplace.visualstudio.com/items?itemName=vscodevim.vim) it and reloading VSCode. The plugin can only be disabled from the Extension manager in VSCode, with no quick way to switch between modal and modeless editing. - -Just like real vim, your editor will now be in Normal mode, which is reported to VSCode's status bar. From here, all your regular vim commands will work as normal, hooray! - -### Vim compatibility - -All common Vim commands are supported. For an in-depth look at what Vim features are supported, check out the [roadmap](ROADMAP.md). Vimscript isn't supported, so you aren't able to load your `.vimrc` or use `.vim` plugins. You have to replicated these using our [Settings](#settings) and [Emulated plugins](#emulated-plugins). - -### Mac setup - -If key repeating isn't working for you, execute this in your Terminal. - -```sh -defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false # For VS Code -defaults write com.microsoft.VSCodeInsiders ApplePressAndHoldEnabled -bool false # For VS Code Insider -``` - -We also recommend going into *System Preferences -> Keyboard* and cranking up the Key Repeat and Delay Until Repeat settings to improve your speed. - -### Windows setup - -VSCodeVim will take over your control keys, just like real vim, so you get the _full_ vim experience. This behaviour can be adjusted with the [`useCtrlKeys`](#vimusectrlkeys) and [`handleKeys`](#vimhandlekeys) settings. - -## Settings - -### Quick example settings - -Below is an example of a [settings.json](https://code.visualstudio.com/Docs/customization/userandworkspace) file for VSCode settings applicable to this extension. Continue on below for more in-depth documentation. - -```json -{ - "vim.easymotion": true, - "vim.incsearch": true, - "vim.useSystemClipboard": true, - "vim.useCtrlKeys": true, - "vim.hlsearch": true, - "vim.insertModeKeyBindings": [ - { - "before": ["j","j"], - "after": [""] - } - ], - "vim.otherModesKeyBindingsNonRecursive": [ - { - "before": ["","d"], - "after": ["d", "d"] - }, - { - "before":[""], - "after":[], - "commands": [ - { - "command": ":nohl" - } - ] - } - ], - "vim.leader": "", - "vim.handleKeys":{ - "": false, - "": false - } -} -``` - -The following is a subset of the supported settings; the full list is described in the `Contributions` tab for this extension, or in our [package.json](https://github.com/VSCodeVim/Vim/blob/master/package.json#L175). - -### VSCodeVim settings - -These settings are specific to VSCodeVim. - -#### `"vim.startInInsertMode"` -* Have VSCodeVim start in Insert Mode rather than Normal Mode. -* We would be remiss in our duties as Vim users not to say that you should really be staying in Normal mode as much as you can, but hey, who are we to stop you? - -#### `"vim.overrideCopy"` -* Override VSCode's copy command with our own, which works correctly with VSCodeVim. -* If cmd-c or ctrl-c is giving you issues, set this to false and complain at https://github.com/Microsoft/vscode/issues/217. -* Type: Boolean (Default: `true`) - -#### `"vim.useSystemClipboard"` -* Enable yanking to the system clipboard by default -* Type: Boolean (Default: `false`) - -#### `"vim.searchHighlightColor"` -* Set the color of search highlights. -* Type: Color String (Default: `rgba(150, 150, 150, 0.3)`) - -#### `"vim.useSolidBlockCursor"` -We have removed this option, due to it making VSCodeVim's performance suffer immensely. - -#### `"vim.useCtrlKeys"` -* Enable Vim ctrl keys overriding common VS Code operations (eg. copy, paste, find, etc). Enabling this setting will: - * `ctrl+c`, `ctrl+[` => `` - * `ctrl+f` => Full Page Forward - * `ctrl+d` => Half Page Back - * `ctrl+b` => Half Page Forward - * `ctrl+v` => Visual Block Mode - * etc. -* Type: Boolean (Default: `true`) - -#### `"vim.handleKeys"` -* Allows user to select certain modifier keybindings and delegate them back to VSCode so that VSCodeVim does not process them. -* Complete list of keys that can be delegated back to VSCode can be found in our [package.json](https://github.com/VSCodeVim/Vim/blob/master/package.json#L44). Each key that has a vim.use in the when argument can be delegated back to vscode by doing "":false. -* An example would be if a user wanted to continue to use ctrl + f for find, but wants to have [`useCtrlKeys`](#vimusectrlkeys) set to true so that other vim bindings work. - -```json - "vim.handleKeys": { - "": false, - "": false - } -``` - -#### `"vim.visualstar"` -* In visual mode, start a search with * or # using the current selection -* Type: Boolean (Default: `false`) - -### Neovim Integration - -We now have neovim integration for Ex-commands. If you want to take advantage of this integration, set `"vim.enableNeovim"` to `true`, and set your `"vim.neovimPath"`. If you don't have neovim installed, [install neovim here](https://github.com/neovim/neovim/wiki/Installing-Neovim). If you don't want to install neovim, all of the old functionality should still work as is (we would really suggest neovim installing though. The new Ex support is super cool, and we'd like to integrate neovim more in the future). - -Please leave feedback on neovim [here](https://github.com/VSCodeVim/Vim/issues/1735). - -Here's some ideas on what you can do with your newfound neovim integration! - -* [The power of g](http://vim.wikia.com/wiki/Power_of_g) -* [The :normal command](https://vi.stackexchange.com/questions/4418/execute-normal-command-over-range) -* Faster search and replace! - -### Key remapping - -There's several different settings you can use to define custom remappings. Also related are the [`useCtrlKeys`](#vimusectrlkeys) and [`handleKeys`](#vimhandlekeys) settings. - -#### `"vim.insertModeKeyBindings"`/`"vim.otherModesKeyBindings"` -* Keybinding overrides to use for insert and other (non-insert) modes. - -Bind `jj` to `` in insert mode: - -```json - "vim.insertModeKeyBindings": [ - { - "before": ["j", "j"], - "after": [""] - } - ] -``` - -Bind `:` to show the command palette: - -```json -"vim.otherModesKeyBindingsNonRecursive": [ - { - "before": [":"], - "after": [], - "commands": [ - { - "command": "workbench.action.showCommands", - "args": [] - } - ] - } -] -``` - -Bind `ZZ` to save and close the current file: - -```json - "vim.otherModesKeyBindingsNonRecursive": [ - { - "before": ["Z", "Z"], - "after": [], - "commands": [ - { - "command": "workbench.action.files.save", - "args": [] - }, - { - "command": "workbench.action.closeActiveEditor", - "args": [] - } - ] - } - ] -``` - -Or bind ctrl+n to turn off search highlighting and `w` to save the current file: - -```json - "vim.otherModesKeyBindingsNonRecursive": [ - { - "before":[""], - "after":[], - "commands": [ - { - "command": ":nohl", - "args": [] - } - ] - }, - { - "before": ["leader", "w"], - "after": [], - "commands": [ - { - "command": "workbench.action.files.save", - "args": [] - } - ] - } - ] -``` - - -#### `"vim.insertModeKeyBindingsNonRecursive"`/`"otherModesKeyBindingsNonRecursive"` -* Non-recursive keybinding overrides to use for insert and other (non-insert) modes (similar to `:noremap`) -* *Example:* Bind `j` to `gj`. Notice that if you attempted this binding normally, the j in gj would be expanded into gj, on and on forever. Stop this recursive expansion using insertModeKeyBindingsNonRecursive and/or otherModesKeyBindingNonRecursive. - -```json - `"vim.otherModesKeyBindingsNonRecursive": [ - { - "before": ["j"], - "after": ["g", "j"] - } - ] -``` - -### Status bar color settings - -Almost like vim-airline in VSCode! - -#### `"vim.statusBarColorControl"` -* Control status bar color based on current mode -* Type: Boolean (Default: `false`) -* Notes: Experimental feature, currently due to VSCode API limitations, this function MUST modify settings.json in the workspace. This causes a slight amount of latency and a constant changing diff in your working directory. [Issue #1565](https://github.com/VSCodeVim/Vim/issues/1565) - -Once this is set, you need to set `"vim.statusBarColors"` as well with these exact strings for modenames. The colors can be adjusted to suit the user. - -```json - "vim.statusBarColorControl": true, - "vim.statusBarColors" : { - "normal": "#005f5f", - "insert": "#5f0000", - "visual": "#5f00af", - "visualline": "#005f87", - "visualblock": "#86592d", - "replace": "#000000" - } -``` - -### Vim settings - -Configuration settings that have been copied from vim. - -Vim settings are loaded in the following sequence: - -1. `:set {setting}` -2. `vim.{setting}` from user/workspace settings. -3. VSCode settings -4. VSCodeVim default values - -#### `"vim.ignorecase"` -* Ignore case in search patterns -* Type: Boolean (Default: `true`) - -#### `"vim.smartcase"` -* Override the 'ignorecase' setting if the search pattern contains upper case characters -* Type: Boolean (Default: `true`) - -#### `"vim.hlsearch"` -* When there is a previous search pattern, highlight all its matches -* Type: Boolean (Default: `false`) - -#### `"vim.incsearch"` -* Show the next search match while you're searching. -* Type: Boolean (Default: `true`) - -#### `"vim.autoindent"` -* Copy indent from current line when starting a new line -* Type: Boolean (Default: `true`) - -#### `"vim.timeout"` -* Timeout in milliseconds for remapped commands -* Type: Number (Default: `1000`) - -#### `"vim.showcmd"` -* Show the text of any command you are in the middle of writing. -* Type: Boolean (Default: `true`) - -#### `"vim.textwidth"` -* Width to word-wrap to when using `gq`. -* Type: number (Default: `80`) - -#### `"vim.leader"` -* What key should `` map to in key remappings? -* Type: string (Default: `\`) - -## Multi-Cursor mode - -> ⚡ Multi-Cursor mode is currently in beta. Please report things you expected to work but didn't [to our feedback thread.](https://github.com/VSCodeVim/Vim/issues/824) - -### Getting into multi-cursor mode - -You can enter multi-cursor mode by: - -* Pressing cmd-d on OSX. -* Running "Add Cursor Above/Below" or the shortcut on any platform. -* Pressing `gb`, a new shortcut we added which is equivalent to cmd-d on OSX or ctrl-d on Windows. (It adds another cursor at the next word that matches the word the cursor is currently on.) - -### Doing stuff - -Now that you have multiple cursors, you should be able to use Vim commands as you see fit. Most of them should work. There is a list of things I know of which don't [here](https://github.com/VSCodeVim/Vim/pull/587). If you find yourself wanting one of these, please [add it to our feedback thread.](https://github.com/VSCodeVim/Vim/issues/824) - -Each cursor has its own clipboard. - -Pressing Escape in Multi-Cursor Visual Mode will bring you to Multi-Cursor Normal mode. Pressing it again will return you to Normal mode. - -## Emulated plugins - -### vim-easymotion - -Easymotion is based on [vim-easymotion](https://github.com/easymotion/vim-easymotion). To activate easymotion, you need to make sure that `easymotion` is set to `true` in settings.json (as the default is `false`). - -Once easymotion is active, you can initiate motions using the following commands. After you initiate the motion, text decorators/markers will be displayed and you can press the keys displayed to jump to that position. `leader` is configurable and is `\` by default. - -Motion Command | Description ----|-------- -` s `|Search character -` f `|Find character forwards -` F `|Find character backwards -` t `|Til character forwards -` T `|Til character backwards -` w`|Start of word forwards -` b`|Start of word backwards -` e`|End of word forwards -` g e`|End of word backwards - -You can customize the appearance of your easymotion markers (the boxes with letters) using the following settings: - -Setting | Description ----|-------- -`vim.easymotionMarkerBackgroundColor`|The background color of the marker box. -`vim.easymotionMarkerForegroundColorOneChar`|The font color for one-character markers. -`vim.easymotionMarkerForegroundColorTwoChar`|The font color for two-character markers, used to differentiate from one-character markers. -`vim.easymotionMarkerWidthPerChar`|The width in pixels allotted to each character. -`vim.easymotionMarkerHeight`|The height of the marker. -`vim.easymotionMarkerFontFamily`|The font family used for the marker text. -`vim.easymotionMarkerFontSize`|The font size used for the marker text. -`vim.easymotionMarkerFontWeight`|The font weight used for the marker text. -`vim.easymotionMarkerYOffset`|The distance between the top of the marker and the text (will typically need some adjusting if height or font size have been changed). - -### vim-surround - -Surround plugin based on tpope's [surround.vim](https://github.com/tpope/vim-surround) plugin is used to work with surrounding characters like parenthesis, brackets, quotes, and XML tags. - -`t` or `<` as `` or `` will do tags and enter tag entry mode. - -Surround can be disabled by setting vim.surround : false - -Surround Command | Description ----|-------- -`d s `|Delete existing surround -`c s `|Change surround existing to desired -`y s `|Surround something with something using motion (as in "you surround") -`S `|Surround when in visual modes (surrounds full selection) - -Some examples: - -* `"test"` with cursor inside quotes type cs"' to end up with `'test'` -* `"test"` with cursor inside quotes type ds" to end up with `test` -* `"test"` with cursor inside quotes type cs"t and enter 123> to end up with `<123>test` -* `test` with cursor on word test type ysaw) to end up with `(test)` - -### vim-commentary - -Commentary in VSCodeVim works similarly to tpope's [vim-commentary](https://github.com/tpope/vim-commentary) but uses the VSCode native "Toggle Line Comment" and "Toggle Block Comment" features. - -Usage examples: -* `gc` - toggles line comment. For example `gcc` to toggle line comment for current line and `gc2j` to toggle line comments for the current line and the next line. -* `gC` - toggles block comment. For example `gCi)` to comment out everything within parenthesis. - - -### vim-indent-object - -Indent Objects in VSCodeVim are identical to [michaeljsmith/vim-indent-object](https://github.com/michaeljsmith/vim-indent-object) and allow you to treat blocks of code at the current indentation level as text objects. This is very useful in languages that don't use braces around statements, like Python. - -Provided there is a new line between the opening and closing braces / tag, it can be considered an agnostic `cib`/`ci{`/`ci[`/`cit`. - -Command | Description ----|-------- -`ii`|This indentation level -`ai`|This indentation level and the line above (think `if` statements in Python) -`aI`|This indentation level, the line above, and the line after (think `if` statements in C/C++/Java/etc) - -## VSCodeVim tricks! - -**Awesome Features You Might Not Know About** - -Vim has a lot of nooks and crannies. VSCodeVim preserves some of the coolest nooks and crannies of Vim. And then we add some of our own! Some of our favorite include: - -* `gd` - jump to definition. _Astoundingly_ useful in any language that VSCode provides definition support for. I use this one probably hundreds of times a day. -* `gq` - on a visual selection - Reflow and wordwrap blocks of text, preserving commenting style. Great for formatting documentation comments. -* `gb` - which adds another cursor on the next word it finds which is the same as the word under the cursor. -* `af` - a command that I added in visual mode, which selects increasingly large blocks of text. e.g. if you had "blah (foo [bar 'ba|z'])" then it would select 'baz' first. If you pressed `af` again, it'd then select [bar 'baz'], and if you did it a third time it would select "(foo [bar 'baz'])". -* `gh` - another custom VSCodeVim command. This one is equivalent to hovering your mouse over wherever the cursor is. Handy for seeing types and error messages without reaching for the mouse! - -(The mnemonic: selecting blocks is fast af! :wink:) - -## F.A.Q. - -### Help! None of the vim `ctrl` (e.g. `ctrl+f`, `ctrl+v`) commands work - -Set the [`useCtrlKeys` setting](#vimusectrlkeys) to `true`. - -### Moving j and k over folds opens up the folds! This extension is unusable! - -You can try setting `vim.foldfix` to `true`. Note, however, that it is a hack. It works fine, but there are side effects. We are unable to fix this issue properly due to VSCode API limitations. Go to [here](https://github.com/Microsoft/vscode/issues/22276) for updates on the issue. - -### Key repeat doesn't work! And I'm on Mac! +VSCodeNeoVim is a [Visual Studio Code](https://code.visualstudio.com/) rewrite of VSCodeVim, changing everything to be backed by Neovim. -Take a look [here](#mac-setup). +This is the temporary repo for VSCodeNeovim for development purposes. Please submit PRs/issues here. This extension will not be released on the marketplace, but I will be providing a .vsix for testing purposes. -### There are annoying intellisense/notifications/popups that I can't close with ``! Or I'm in a snippet and I want to close intellisense! +Some notes on contributing: +* Much of the VSCodeVim code is still in here. The only places it's used are things like some utility Position functions, etc. +* Most of the "hard" work is in extension.ts. +* The rest of the work is in srcNV. NvUtil contains a lot of utility functions with working with neovim rpc requests. RPCHandlers contain handlers for RPC requests. +* You need to have the newest nvim (installed from nightly/master) installed. At the very least, you need a version of neovim that has this issue fixed: https://github.com/neovim/neovim/issues/6166 +* The easiest way to test is to have the neovim instance for VSCode be created by connecting to a pipe. In order to do so, set NVIM_LISTEN_ADDRESS equal to `/tmp/nvim`, open a neovim instance, and then open the extension. This allows you to see what's happening on the neovim side (any errors/prompts/etc.) This is how my typical setup for development looks: ![](https://i.imgur.com/gwck9Do.jpg). There's some code to do this automatically, but it doesn't check if `/tmp/nvim` is a socket, so it may still fail. Also, I think performance is slightly worse when you do this. -Press `shift+` to close all of those boxes. -## Contributing +Other helpful documentation links: +https://vscodevim.slack.com/files/U3EUW86U9/F62R31A5V/Integrating_Neovim_into_VSCode "Design" doc +https://neovim.io/doc/user/api.html#api-global Neovim RPC API documentation +https://neovim.io/doc/user/msgpack_rpc.html#rpc-remote-ui Neovim remote UI documentation. We currently aren't using it but will likely use it at some point in the future. +https://github.com/neovim/node-client The node client we're using +https://github.com/lunixbochs/ActualVim Another neovim backed vim plugin (for sublime) -This project is maintained by a group of awesome [people](https://github.com/VSCodeVim/Vim/graphs/contributors) and contributions are extremely welcome :heart:. For a quick tutorial on how you can help, see our [contributing guide](/.github/CONTRIBUTING.md). -### Special shoutouts to cool contributors +Important discussions links: +https://gitter.im/neovim/neovim For talking to the neovim devs +https://gitter.im/neovim/node-client For talking to the neovim node-client devs +https://vscodevim.slack.com/ For talking to the VSCodeVim devs -* Thanks to @xconverge for making over 100 commits to the repo. If you're wondering why your least favorite bug packed up and left, it was probably him. -* Thanks to @Metamist for implementing EasyMotion! -* Thanks to @sectioneight for implementing text objects! -* Special props to [Kevin Coleman](http://kevincoleman.io), who created our awesome logo! +Important Neovim PRs to follow: +https://github.com/neovim/neovim/pull/5269 Text Diffs (so we don't have to sync the entire buffer each time) +https://github.com/neovim/neovim/pull/7173 For CmdLine syncing (it's a continuation of https://github.com/neovim/neovim/pull/6162) +https://github.com/neovim/neovim/pull/6544 For allowing UI to update immediately (should allow Easymotion to work!) +https://github.com/neovim/neovim/pull/6168 For externalizing the wildmenu +Some screenshots (kinda hard to show what's changed without at least a gif...): +Wildmenu working! +![](https://i.imgur.com/7zMEd1G.jpg) +Autocomplete working! +![](https://i.imgur.com/aQ7jQyY.jpg) diff --git a/designdoc.md b/designdoc.md new file mode 100644 index 00000000000..c3df11cefe7 --- /dev/null +++ b/designdoc.md @@ -0,0 +1,69 @@ +# Integrating Neovim into VSCode + +## Things we will not port over from Vim (for now). As in, we will have our own implementations that do not try to match vim behavior +1. Folds +2. Splits +3. Intellisense/autocomplete (this includes go to definition, autocomplete, etc.) +4. Syntax highlighting +These are all things that I see no benefit in including from vim. + +## Philosophy +As much as possible, we need to pretend like we're in neovim. That means that when we override `gd`, it needs to look exactly like we just did a `gd`. When we're doing autocomplete, it needs to look like the user typed in the text to neovim. + +## Overarching strategy +1. Pass all relevant input to neovim. +2. Get all relevant information back (text, cursor position, mode, tabline) +Simple enough. + +## Things I need to figure out +1. Multicursor. + * How do I ensure that some commands are only performed once? + For example, undo. + * How do I handle operations that are local to each cursor. For example, copy and paste. + * Maybe we can use this? https://github.com/terryma/vim-multiple-cursors + * Will be handled by core neovim (eventually, says @justinmk http://i.imgur.com/jPQtHoU.jpg). + +2. Handling operators properly + * https://github.com/neovim/neovim/issues/6166 + * Done through a hack right now + * This is annoying. We don't want to need to maintain state on the vscode side. This is why commands like 2dd or gqq don't work in ActualVim. + +3. Ensuring that actions coming in are performed in the correct order. + * Transformation queue? + * I don't think this is an issue if we don't do multiple cursors? nvim.input() isn't blocking, but I'm not sure. + +4. Handling cross file jumping for commands that we're overriding (gd for example) + * Just open files regularly on editor side, feed it back to neovim, and then handle it regularly. + Flow of events: + 1. "gd" (or other) is pressed in neovim. + 2. A RPC request is made to VSCode. + 3. VSCode receives the request, opens up the necessary file on the neovim side. + 4. nvim.eval("m'") and then make the right jumps through setpos() and such. + +5. Handling splits/folds. + * All of this should be handled through editor commands. + * We can override in vimrc through a map to a rpc call. + * We need some kind of API for this though, to handle keybindings properly. + * Maybe folds are possible to play nice, but they both seem like fundamental ui incompatibilities. + +6. Handling settings that must be handled on the vscode side. + * I wonder whether there are some things that won't play nice that we might want to lint for. + * Sync them up either with a .vimrc or at startup. I'm leaning towards in the .vimrc (to maintain transparency) + +7. Snippets? + * no clue how these are implemented. + +8. Insert mode autocomplete and such. + * This is tricky. There's a lot of things that mess up the autocompletion engine. + +9. Editor inserted text + * If we can get a diff we can insert it on the vim side as another action? + +10. Handling opening files and other such things. + * Autocommands that sync up VSCode and neovim state + +11. Handling commands that we want vscode to handle. + * Don't pass anything that's not a single character//. + * Have an "ignore this" array. + +12. Mouse \ No newline at end of file diff --git a/extension.ts b/extension.ts index 1720fb76020..2db14023bdb 100644 --- a/extension.ts +++ b/extension.ts @@ -1,385 +1,198 @@ 'use strict'; -/** - * Extension.ts is a lightweight wrapper around ModeHandler. It converts key - * events to their string names and passes them on to ModeHandler via - * handleKeyEvent(). - */ - import * as vscode from 'vscode'; -import * as _ from "lodash"; -import { showCmdLine } from './src/cmd_line/main'; -import { ModeHandler } from './src/mode/modeHandler'; -import { taskQueue } from './src/taskQueue'; +import * as fs from 'fs'; +import * as _ from 'lodash'; +import { attach } from 'neovim'; +import { NeovimClient } from 'neovim/lib/api/client'; +import { TaskQueue } from 'aurelia-task-queue'; import { Position } from './src/common/motion/position'; import { Globals } from './src/globals'; -import { AngleBracketNotation } from './src/notation'; -import { ModeName } from './src/mode/mode'; import { Configuration } from './src/configuration/configuration'; -import { ICodeKeybinding } from './src/mode/remapper'; -import { runCmdLine } from './src/cmd_line/main'; -import './src/actions/vim.all'; -import { attach } from "promised-neovim-client"; -import { spawn } from "child_process"; -import { Neovim } from "./src/neovim/nvimUtil"; +import { spawn } from 'child_process'; +import { NvUtil } from './srcNV/nvUtil'; +import { RpcRequest } from './srcNV/rpcHandlers'; +import { TextEditor } from './src/textEditor'; +import { Screen, IgnoredKeys } from './srcNV/screen'; +import { VimSettings } from './srcNV/vimSettings'; +import { VscHandlers } from './srcNV/vscHandlers'; interface VSCodeKeybinding { key: string; - mac?: string; - linux?: string; command: string; when: string; + vimKey: string; } const packagejson: { contributes: { keybindings: VSCodeKeybinding[]; - } + }; } = require('../package.json'); // out/../package.json -export class EditorIdentity { - private _fileName: string; - private _viewColumn: vscode.ViewColumn; - - constructor(textEditor?: vscode.TextEditor) { - this._fileName = textEditor && textEditor.document.fileName || ""; - this._viewColumn = textEditor && textEditor.viewColumn || vscode.ViewColumn.One; - } - - get fileName() { - return this._fileName; - } - - get viewColumn() { - return this._viewColumn; - } - - public hasSameBuffer(identity: EditorIdentity): boolean { - return this.fileName === identity.fileName; - } - - public isEqual(identity: EditorIdentity): boolean { - return this.fileName === identity.fileName && this.viewColumn === identity.viewColumn; - } - - public toString() { - return this.fileName + this.viewColumn; - } -} - -let extensionContext: vscode.ExtensionContext; - -/** - * Note: We can't initialize modeHandler here, or even inside activate(), because some people - * see a bug where VSC hasn't fully initialized yet, which pretty much breaks VSCodeVim entirely. - */ -let modeHandlerToEditorIdentity: { [key: string]: ModeHandler } = {}; -let previousActiveEditorId: EditorIdentity = new EditorIdentity(); - -export async function getAndUpdateModeHandler(): Promise { - const prevHandler = modeHandlerToEditorIdentity[previousActiveEditorId.toString()]; - const activeEditorId = new EditorIdentity(vscode.window.activeTextEditor); - - let curHandler = modeHandlerToEditorIdentity[activeEditorId.toString()]; - if (!curHandler) { - if (!Configuration.disableAnnoyingNeovimMessage) { - vscode.window.showInformationMessage("We have now added neovim integration for Ex-commands.\ - Enable it with vim.enableNeovim in settings", "Never show again").then((result) => { - if (result !== "Close") { - vscode.workspace.getConfiguration("vim").update("disableAnnoyingNeovimMessage", true, true); - Configuration.disableAnnoyingNeovimMessage = true; - } - }); - } - const newModeHandler = await new ModeHandler(); - if (Configuration.enableNeovim) { - await Neovim.initNvim(newModeHandler.vimState); - } - modeHandlerToEditorIdentity[activeEditorId.toString()] = newModeHandler; - extensionContext.subscriptions.push(newModeHandler); - - curHandler = newModeHandler; - } - - curHandler.vimState.editor = vscode.window.activeTextEditor!; - if (!prevHandler || curHandler.identity !== prevHandler.identity) { - setTimeout(() => { - curHandler.syncCursors(); - }, 0); - } - - if (previousActiveEditorId.hasSameBuffer(activeEditorId)) { - if (!previousActiveEditorId.isEqual(activeEditorId)) { - // We have opened two editors, working on the same file. - previousActiveEditorId = activeEditorId; - } - } else { - previousActiveEditorId = activeEditorId; - - await curHandler.updateView(curHandler.vimState, {drawSelection: false, revealRange: false}); - } - - if (prevHandler && curHandler.vimState.focusChanged) { - curHandler.vimState.focusChanged = false; - prevHandler.vimState.focusChanged = true; - } - - vscode.commands.executeCommand('setContext', 'vim.mode', curHandler.vimState.currentModeName()); - - // Temporary workaround for vscode bug not changing cursor style properly - // https://github.com/Microsoft/vscode/issues/17472 - // https://github.com/Microsoft/vscode/issues/17513 - const options = curHandler.vimState.editor.options; - const desiredStyle = options.cursorStyle; - - // Temporarily change to any other cursor style besides the desired type, then change back - if (desiredStyle === vscode.TextEditorCursorStyle.Block) { - curHandler.vimState.editor.options.cursorStyle = vscode.TextEditorCursorStyle.Line; - curHandler.vimState.editor.options.cursorStyle = desiredStyle; - } else { - curHandler.vimState.editor.options.cursorStyle = vscode.TextEditorCursorStyle.Block; - curHandler.vimState.editor.options.cursorStyle = desiredStyle; - } - - return curHandler; -} - -class CompositionState { - public isInComposition: boolean = false; - public composingText: string = ""; +export namespace Vim { + export let nv: NeovimClient; + export let channelId: number; + export let mode: { mode: string; blocking: boolean } = { mode: 'n', blocking: false }; + export let screen: Screen; + export let prevState: { bufferTick: number } = { + bufferTick: -1, + }; + export let numVimChangesToApply = 0; + export let taskQueue = new TaskQueue(); + // We're connecting to an already existing terminal instance, so externalized ui won't work. + export let DEBUG: boolean; } export async function activate(context: vscode.ExtensionContext) { - extensionContext = context; - let compositionState = new CompositionState(); - - // Event to update active configuration items when changed without restarting vscode - vscode.workspace.onDidChangeConfiguration((e: void) => { - Configuration.updateConfiguration(); - - /* tslint:disable:forin */ - // Update the remappers foreach modehandler - for (let mh in modeHandlerToEditorIdentity) { - modeHandlerToEditorIdentity[mh].createRemappers(); - } - }); - - vscode.window.onDidChangeActiveTextEditor(handleActiveEditorChange, this); - - vscode.workspace.onDidChangeTextDocument((event) => { - if (!Globals.active) { + vscode.workspace.onDidCloseTextDocument(async event => { + const deleted_file = event.fileName; + let buf_id = await nvim.call('bufnr', [`^${deleted_file}$`]); + if (buf_id === -1) { return; } + // await nvim.command(`noautocmd ${buf_id}bw!`); + }); - /** - * Change from vscode editor should set document.isDirty to true but they initially don't! - * There is a timing issue in vscode codebase between when the isDirty flag is set and - * when registered callbacks are fired. https://github.com/Microsoft/vscode/issues/11339 - */ - - let contentChangeHandler = (modeHandler: ModeHandler) => { - if (modeHandler.vimState.currentMode === ModeName.Insert) { - if (modeHandler.vimState.historyTracker.currentContentChanges === undefined) { - modeHandler.vimState.historyTracker.currentContentChanges = []; - } + vscode.window.onDidChangeActiveTextEditor(VscHandlers.handleActiveTextEditorChange, this); - modeHandler.vimState.historyTracker.currentContentChanges = - modeHandler.vimState.historyTracker.currentContentChanges.concat(event.contentChanges); + vscode.window.onDidChangeTextEditorSelection(async e => { + if (e.kind === vscode.TextEditorSelectionChangeKind.Mouse) { + if (e.selections[0]) { + await NvUtil.setSelection(e.selections[0]); } - }; - - if (Globals.isTesting) { - contentChangeHandler(Globals.modeHandlerForTesting as ModeHandler); - } else { - _.filter(modeHandlerToEditorIdentity, modeHandler => modeHandler.identity.fileName === event.document.fileName) - .forEach(modeHandler => { - contentChangeHandler(modeHandler); - }); } - setTimeout(() => { - if (!event.document.isDirty && !event.document.isUntitled && event.contentChanges.length) { - handleContentChangedFromDisk(event.document); - }} - , 0); - }); + vscode.workspace.onDidChangeTextDocument(VscHandlers.handleTextDocumentChange); - overrideCommand(context, 'type', async (args) => { - taskQueue.enqueueTask({ - promise: async () => { - const mh = await getAndUpdateModeHandler(); - - if (compositionState.isInComposition) { - compositionState.composingText += args.text; - } else { - await mh.handleKeyEvent(args.text); - } - }, - isRunning: false - }); + // Event to update active configuration items when changed without restarting vscode + vscode.workspace.onDidChangeConfiguration((e: void) => { + Configuration.updateConfiguration(); }); - overrideCommand(context, 'replacePreviousChar', async (args) => { - taskQueue.enqueueTask({ - promise: async () => { - const mh = await getAndUpdateModeHandler(); - - if (compositionState.isInComposition) { - compositionState.composingText = compositionState.composingText - .substr(0, compositionState.composingText.length - args.replaceCharCnt) + args.text; - } else { - await vscode.commands.executeCommand('default:replacePreviousChar', { - text: args.text, - replaceCharCnt: args.replaceCharCnt - }); - mh.vimState.cursorPosition = Position.FromVSCodePosition(mh.vimState.editor.selection.start); - mh.vimState.cursorStartPosition = Position.FromVSCodePosition(mh.vimState.editor.selection.start); - } - }, - isRunning: false + overrideCommand(context, 'type', async args => { + Vim.taskQueue.queueMicroTask(() => { + VscHandlers.handleKeyEventNV(args.text); }); }); - overrideCommand(context, 'compositionStart', async (args) => { - taskQueue.enqueueTask({ - promise: async () => { - compositionState.isInComposition = true; - }, - isRunning: false - }); - }); + const keysToBind = packagejson.contributes.keybindings; + const ignoreKeys = Configuration.ignoreKeys; - overrideCommand(context, 'compositionEnd', async (args) => { - taskQueue.enqueueTask({ - promise: async () => { - const mh = await getAndUpdateModeHandler(); - let text = compositionState.composingText; - compositionState = new CompositionState(); - await mh.handleMultipleKeyEvents(text.split("")); - }, - isRunning: false + for (let key of keysToBind) { + if (ignoreKeys.all.indexOf(key.vimKey) !== -1) { + continue; + } + vscode.commands.executeCommand('setContext', `vim.use_${key.vimKey}`, true); + registerCommand(context, key.command, () => { + Vim.taskQueue.queueMicroTask(() => { + VscHandlers.handleKeyEventNV(`${key.vimKey}`); + }); }); - }); - - registerCommand(context, 'extension.showCmdLine', () => { - showCmdLine("", modeHandlerToEditorIdentity[new EditorIdentity(vscode.window.activeTextEditor).toString()]); - }); + } - registerCommand(context, 'vim.remap', async (args: ICodeKeybinding) => { - taskQueue.enqueueTask({ - promise: async () => { - const mh = await getAndUpdateModeHandler(); - if (args.after) { - for (const key of args.after) { - await mh.handleKeyEvent(AngleBracketNotation.Normalize(key)); - } - return; - } + const proc = spawn( + Configuration.neovimPath, + [ + // '-u', + // 'NONE', + '-N', + '--embed', + vscode.window.activeTextEditor ? vscode.window.activeTextEditor!.document.fileName : '', + ], + { + cwd: __dirname, + } + ); - if (args.commands) { - for (const command of args.commands) { - // Check if this is a vim command by looking for : - if (command.command.slice(0, 1) === ":") { - await runCmdLine(command.command.slice(1, command.command.length), mh); - await mh.updateView(mh.vimState); - } else { - await vscode.commands.executeCommand(command.command, command.args); - } - } - } - }, - isRunning: false - }); + proc.on('error', function(err) { + console.log(err); + vscode.window.showErrorMessage('Unable to setup neovim instance! Check your path.'); }); - - registerCommand(context, 'toggleVim', async () => { - Globals.active = !Globals.active; - if (Globals.active) { - await vscode.commands.executeCommand('setContext', 'vim.active', Globals.active); - compositionState = new CompositionState(); - modeHandlerToEditorIdentity = {}; - let mh = await getAndUpdateModeHandler(); - mh.updateView(mh.vimState, { drawSelection: false, revealRange: false }); - } else { - let cursorStyle = await vscode.workspace.getConfiguration('editor').get('cursorStyle', 'line'); - vscode.window.visibleTextEditors.forEach(editor => { - let options = editor.options; - switch (cursorStyle) { - case 'line': - options.cursorStyle = vscode.TextEditorCursorStyle.Line; - break; - case 'block': - options.cursorStyle = vscode.TextEditorCursorStyle.Block; - break; - case 'underline': - options.cursorStyle = vscode.TextEditorCursorStyle.Underline; - break; - - default: - break; - } - editor.options = options; - }); - await vscode.commands.executeCommand('setContext', 'vim.active', Globals.active); - let mh = await getAndUpdateModeHandler(); - mh.setStatusBarText('-- VIM: DISABLED --'); + let nvim: NeovimClient; + if (fs.existsSync('/tmp/nvim') && fs.lstatSync('/tmp/nvim').isSocket()) { + nvim = attach({ socket: '/tmp/nvim' }); + Vim.DEBUG = true; + } else { + nvim = attach({ proc: proc }); + Vim.DEBUG = false; + } + Vim.nv = nvim; + + Vim.channelId = (await nvim.requestApi())[0] as number; + + const WIDTH = 50; + const HEIGHT = 50; + nvim.uiAttach(WIDTH, HEIGHT, { ext_cmdline: true, ext_wildmenu: true }); + Vim.screen = new Screen({ width: WIDTH, height: HEIGHT }); + + const code = ` +function _vscode_copy_text(text, line, char) + vim.api.nvim_command('undojoin') + vim.api.nvim_buf_set_lines(0, 0, -1, true, text) + vim.api.nvim_call_function('setpos', {'.', {0, line, char, false}}) +end +`; + + await Vim.nv.lua(code, []); + await nvim.command('autocmd!'); + + // todo(chilli): Create this map just from RPCHandlers and a decorator. + const autocmdMap: { [autocmd: string]: string } = { + BufWriteCmd: 'writeBuf', + QuitPre: 'closeBuf', + BufEnter: 'enterBuf', + TabNewEntered: 'newTabEntered', + }; + + for (const autocmd of Object.keys(autocmdMap)) { + await nvim.command( + `autocmd ${autocmd} * :call rpcrequest(${Vim.channelId}, "${autocmdMap[ + autocmd + ]}", expand(""), fnamemodify(expand(''), ':p'), expand(""))` + ); + } + + // Overriding commands to handle them on the vscode side. + // await nvim.command(`nnoremap gd :call rpcrequest(${Vim.channelId},"goToDefinition")`); + + await NvUtil.setSettings(['noswapfile', 'hidden']); + nvim.on('notification', (method: any, args: any) => { + if (vscode.window.activeTextEditor && method === 'redraw') { + Vim.screen.redraw(args); } }); - vscode.commands.executeCommand('setContext', 'vim.active', Globals.active); - - // Clear boundKeyCombinations array incase there are any entries in it so - // that we have a clean list of keys with no duplicates - Configuration.boundKeyCombinations = []; - - for (let keybinding of packagejson.contributes.keybindings) { - if (keybinding.when.indexOf("listFocus") !== -1) { - continue; - } - let keyToBeBound = ""; - /** - * On OSX, handle mac keybindings if we specified one. - */ - if (process.platform === "darwin") { - keyToBeBound = keybinding.mac || keybinding.key; - } else if (process.platform === "linux") { - keyToBeBound = keybinding.linux || keybinding.key; + nvim.on('request', async (method: string, args: Array, resp: any) => { + if (RpcRequest[method] !== undefined) { + const f = RpcRequest[method]; + f(args, resp); } else { - keyToBeBound = keybinding.key; + console.log(`${method} is not defined!`); } + }); - const bracketedKey = AngleBracketNotation.Normalize(keyToBeBound); - - // Store registered key bindings in bracket notation form - Configuration.boundKeyCombinations.push(bracketedKey); - - registerCommand(context, keybinding.command, () => handleKeyEvent(`${ bracketedKey }`)); - } - - // Update configuration now that bound keys array is populated - Configuration.updateConfiguration(); - - // Initialize mode handler for current active Text Editor at startup. if (vscode.window.activeTextEditor) { - let mh = await getAndUpdateModeHandler(); - mh.updateView(mh.vimState, {drawSelection: false, revealRange: false}); + await VscHandlers.handleActiveTextEditorChange(); } } -function overrideCommand(context: vscode.ExtensionContext, command: string, callback: (...args: any[]) => any) { - let disposable = vscode.commands.registerCommand(command, async (args) => { - if (!Globals.active) { - await vscode.commands.executeCommand("default:" + command, args); - return; - } - +function overrideCommand( + context: vscode.ExtensionContext, + command: string, + callback: (...args: any[]) => any +) { + let disposable = vscode.commands.registerCommand(command, async args => { if (!vscode.window.activeTextEditor) { return; } - if (vscode.window.activeTextEditor.document && vscode.window.activeTextEditor.document.uri.toString() === "debug:input") { - await vscode.commands.executeCommand("default:" + command, args); + if ( + vscode.window.activeTextEditor.document && + vscode.window.activeTextEditor.document.uri.toString() === 'debug:input' + ) { + await vscode.commands.executeCommand('default:' + command, args); return; } @@ -388,8 +201,12 @@ function overrideCommand(context: vscode.ExtensionContext, command: string, call context.subscriptions.push(disposable); } -function registerCommand(context: vscode.ExtensionContext, command: string, callback: (...args: any[]) => any) { - let disposable = vscode.commands.registerCommand(command, async (args) => { +function registerCommand( + context: vscode.ExtensionContext, + command: string, + callback: (...args: any[]) => any +) { + let disposable = vscode.commands.registerCommand(command, async args => { if (!vscode.window.activeTextEditor) { return; } @@ -399,46 +216,6 @@ function registerCommand(context: vscode.ExtensionContext, command: string, call context.subscriptions.push(disposable); } -async function handleKeyEvent(key: string): Promise { - const mh = await getAndUpdateModeHandler(); - - taskQueue.enqueueTask({ - promise : async () => { - await mh.handleKeyEvent(key); - }, - isRunning : false - }); -} - -function handleContentChangedFromDisk(document : vscode.TextDocument) : void { - _.filter(modeHandlerToEditorIdentity, modeHandler => modeHandler.identity.fileName === document.fileName) - .forEach(modeHandler => { - modeHandler.vimState.historyTracker.clear(); - }); -} - -async function handleActiveEditorChange(): Promise { - if (!Globals.active) { - return; - } - - // Don't run this event handler during testing - if (Globals.isTesting) { - return; - } - - taskQueue.enqueueTask({ - promise: async () => { - if (vscode.window.activeTextEditor !== undefined) { - const mh = await getAndUpdateModeHandler(); - - mh.updateView(mh.vimState, {drawSelection: false, revealRange: false}); - } - }, - isRunning: false - }); -} - process.on('unhandledRejection', function(reason: any, p: any) { - console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason); + console.log('Unhandled Rejection at: Promise ', p, ' reason: ', reason); }); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000000..4dcb6bda350 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5666 @@ +{ + "name": "vim", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/glob": { + "version": "5.0.32", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.32.tgz", + "integrity": "sha512-DMcj5b67Alb/e4KhpzyvphC5nVDHn1oCOGZao3oBddZVMH5vgI/cvdp+O/kcxZGZaPqs0ZLAsK4YrjbtZHO05g==", + "dev": true, + "requires": { + "@types/minimatch": "3.0.1", + "@types/node": "8.0.47" + } + }, + "@types/minimatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.1.tgz", + "integrity": "sha512-rUO/jz10KRSyA9SHoCWQ8WX9BICyj5jZYu1/ucKEJKb4KzLZCKMURdYbadP157Q6Zl1x0vHsrU+Z/O0XlhYQDw==", + "dev": true + }, + "@types/mocha": { + "version": "2.2.43", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.43.tgz", + "integrity": "sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw==", + "dev": true + }, + "@types/msgpack-lite": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@types/msgpack-lite/-/msgpack-lite-0.1.5.tgz", + "integrity": "sha512-tA1kW6rmEF8xZWTksPzeHQB2vKElEWJeUwZgfemGhYR0iExRuFAAFXcd85rHR5gaBEZNezgrKkLmib0hKpAO2Q==", + "requires": { + "@types/node": "8.0.47" + } + }, + "@types/node": { + "version": "8.0.47", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.47.tgz", + "integrity": "sha512-kOwL746WVvt/9Phf6/JgX/bsGQvbrK5iUgzyfwZNcKVFcjAUVSpF9HxevLTld2SG9aywYHOILj38arDdY1r/iQ==" + }, + "@types/shelljs": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.7.4.tgz", + "integrity": "sha512-/RQcPAeUMEz4h2p818OZ4ghBJEliVf2MneJGiKl35guqJ1+CTNu+v1zP1qG9Dym61aktec60riIkja1X+GPS3A==", + "dev": true, + "requires": { + "@types/glob": "5.0.32", + "@types/node": "8.0.47" + } + }, + "@types/which": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/@types/which/-/which-1.0.28.tgz", + "integrity": "sha1-AW44dim4gXvtZT/jLqtdESecjfY=", + "dev": true + }, + "@types/winston": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@types/winston/-/winston-2.3.6.tgz", + "integrity": "sha512-gZsUc53u4JHqt5nvfgTnjNP1SkzDmDtY7eZz/8WUFL43Pp8KMR+g8LiHjslwLLheIS/hfGH55QW4tJVjNwYh/Q==", + "requires": { + "@types/node": "8.0.47" + } + }, + "abbrev": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", + "dev": true + }, + "agent-base": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", + "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", + "dev": true, + "requires": { + "extend": "3.0.1", + "semver": "5.0.3" + }, + "dependencies": { + "semver": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", + "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", + "dev": true + } + } + }, + "ajv": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz", + "integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "any-shell-escape": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/any-shell-escape/-/any-shell-escape-0.1.1.tgz", + "integrity": "sha1-1Vq5ciRMcaml4asIefML8RCAaVk=", + "dev": true + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-slice": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.0.0.tgz", + "integrity": "sha1-5zA08A3MH0CHYAj9IP6ud71LfC8=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "async": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", + "dev": true, + "requires": { + "lodash": "4.17.4" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aurelia-pal": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/aurelia-pal/-/aurelia-pal-1.4.0.tgz", + "integrity": "sha1-EUjyVshUwOAgoKj0Zcv0pu6vkYg=" + }, + "aurelia-task-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/aurelia-task-queue/-/aurelia-task-queue-1.2.1.tgz", + "integrity": "sha1-LiXHzWY7fjB08dmXROstFOxc5ZQ=", + "requires": { + "aurelia-pal": "1.4.0" + } + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "bump-regex": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/bump-regex/-/bump-regex-2.8.0.tgz", + "integrity": "sha512-prjTDXzGEbTvCgDVEAKvOGpAqZnz5EmzJNiYi2L72TjNy+T91w3SbPgofnAsLXZZBqZigv+kN4oF5oEIyr6LPw==", + "dev": true, + "requires": { + "semver": "5.4.1", + "xtend": "4.0.1" + }, + "dependencies": { + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + } + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "2.1.1", + "map-obj": "1.0.1" + } + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "clipboardy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.1.4.tgz", + "integrity": "sha1-UbF1dPxoJYji3Slc+m5qoQnqte4=", + "requires": { + "execa": "0.6.3" + } + }, + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "cloneable-readable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.0.0.tgz", + "integrity": "sha1-pikNQT8hemEjL5XkWP84QYz7ARc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "process-nextick-args": "1.0.7", + "through2": "2.0.3" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + } + }, + "configstore": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", + "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=", + "dev": true, + "requires": { + "dot-prop": "3.0.0", + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "os-tmpdir": "1.0.2", + "osenv": "0.1.4", + "uuid": "2.0.3", + "write-file-atomic": "1.3.4", + "xdg-basedir": "2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "1.0.2" + } + }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "dateformat": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.0.0.tgz", + "integrity": "sha1-J0Pjq7XD/CRi5SfcpEXgTp9N7hc=", + "dev": true + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "deep-assign": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-1.0.0.tgz", + "integrity": "sha1-sJJ0O+hCfcYh6gBnzex+cN0Z83s=", + "dev": true, + "requires": { + "is-obj": "1.0.1" + } + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "1.0.2" + } + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + }, + "dependencies": { + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "deprecated": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", + "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=", + "dev": true + }, + "detect-file": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", + "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", + "dev": true, + "requires": { + "fs-exists-sync": "0.1.0" + } + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "dev": true + }, + "diff-match-patch": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.0.tgz", + "integrity": "sha1-HMPIOkkNZ/ldkeOfatHy4Ia2MEg=" + }, + "dot-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "dev": true, + "requires": { + "is-obj": "1.0.1" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "1.1.14" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "duplexify": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.1.tgz", + "integrity": "sha512-j5goxHTwVED1Fpe5hh3q9R93Kip0Bg2KVAt4f8CEYM3UEwYcPSvWbXaUQOzdX/HtiNomipv+gU7ASQPDbV7pGQ==", + "dev": true, + "requires": { + "end-of-stream": "1.4.0", + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "stream-shift": "1.0.0" + }, + "dependencies": { + "end-of-stream": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", + "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", + "dev": true, + "requires": { + "once": "1.4.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "end-of-stream": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", + "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "dev": true, + "requires": { + "once": "1.3.3" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es-abstract": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.2.tgz", + "integrity": "sha512-dvhwFL3yjQxNNsOWx6exMlaDrRHCRGMQlnx5lsXDCZ/J7G/frgIIl94zhZSp/galVAYp7VzPi1OrAHta89/yGQ==", + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "event-lite": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/event-lite/-/event-lite-0.1.1.tgz", + "integrity": "sha1-R88IqNN9C2lM23s7F7UfqsZXYIY=" + }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "0.1.1", + "from": "0.1.7", + "map-stream": "0.1.0", + "pause-stream": "0.0.11", + "split": "0.3.3", + "stream-combiner": "0.0.4", + "through": "2.3.8" + } + }, + "execa": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.6.3.tgz", + "integrity": "sha1-V7aaWU8IF1nGnlNw8NF7nLEWWP4=", + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "2.2.3" + } + }, + "expand-tilde": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", + "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "1.1.0" + }, + "dependencies": { + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + } + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" + }, + "fancy-log": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.0.tgz", + "integrity": "sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "time-stamp": "1.1.0" + } + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "1.2.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } + }, + "find-index": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", + "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "findup-sync": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz", + "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", + "dev": true, + "requires": { + "detect-file": "0.1.0", + "is-glob": "2.0.1", + "micromatch": "2.3.11", + "resolve-dir": "0.1.1" + } + }, + "fined": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", + "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.1" + }, + "dependencies": { + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.1" + } + } + } + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", + "dev": true + }, + "flagged-respawn": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.2.tgz", + "integrity": "sha1-/xke3c1wiKZ1smEP/8l2vpuAdLU=", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, + "fs-exists-sync": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", + "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gaze": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", + "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "dev": true, + "requires": { + "globule": "0.1.0" + } + }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "1.0.2" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.3.3" + }, + "dependencies": { + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + } + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "glob-stream": { + "version": "3.1.18", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", + "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", + "dev": true, + "requires": { + "glob": "4.5.3", + "glob2base": "0.0.12", + "minimatch": "2.0.10", + "ordered-read-streams": "0.1.0", + "through2": "0.6.5", + "unique-stream": "1.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + } + } + }, + "glob-watcher": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", + "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", + "dev": true, + "requires": { + "gaze": "0.5.2" + } + }, + "glob2base": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "dev": true, + "requires": { + "find-index": "0.1.1" + } + }, + "global-modules": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", + "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", + "dev": true, + "requires": { + "global-prefix": "0.1.5", + "is-windows": "0.2.0" + } + }, + "global-prefix": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", + "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.1", + "ini": "1.3.4", + "is-windows": "0.2.0", + "which": "1.3.0" + } + }, + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", + "dev": true, + "requires": { + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" + }, + "dependencies": { + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "dev": true, + "requires": { + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", + "dev": true + }, + "inherits": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", + "dev": true + }, + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=", + "dev": true + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "glogg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", + "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", + "dev": true, + "requires": { + "sparkles": "1.0.0" + } + }, + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "dev": true, + "requires": { + "natives": "1.1.0" + } + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, + "gulp": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", + "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", + "dev": true, + "requires": { + "archy": "1.0.0", + "chalk": "1.1.3", + "deprecated": "0.0.1", + "gulp-util": "3.0.8", + "interpret": "1.0.4", + "liftoff": "2.3.0", + "minimist": "1.2.0", + "orchestrator": "0.3.8", + "pretty-hrtime": "1.0.3", + "semver": "4.3.6", + "tildify": "1.2.0", + "v8flags": "2.1.1", + "vinyl-fs": "0.3.14" + } + }, + "gulp-bump": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/gulp-bump/-/gulp-bump-2.8.0.tgz", + "integrity": "sha512-syvQLax2xQo1EDFJxanUqX1rv+YkVB4/cx/THN+uInmSjMGezT1/6WYLdXqkBAMQUw2KlyB2melz0DzrHdwkLA==", + "dev": true, + "requires": { + "bump-regex": "2.8.0", + "plugin-error": "0.1.2", + "plugin-log": "0.1.0", + "semver": "5.4.1", + "through2": "2.0.3" + }, + "dependencies": { + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + } + } + }, + "gulp-chmod": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-chmod/-/gulp-chmod-2.0.0.tgz", + "integrity": "sha1-AMOQuSigeZslGsz2MaoJ4BzGKZw=", + "dev": true, + "requires": { + "deep-assign": "1.0.0", + "stat-mode": "0.2.2", + "through2": "2.0.3" + } + }, + "gulp-filter": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp-filter/-/gulp-filter-5.0.1.tgz", + "integrity": "sha512-5olRzAhFdXB2klCu1lnazP65aO9YdA/5WfC9VdInIc8PrUeDIoZfaA3Edb0yUBGhVdHv4eHKL9Fg5tUoEJ9z5A==", + "dev": true, + "requires": { + "gulp-util": "3.0.8", + "multimatch": "2.1.0", + "streamfilter": "1.0.5" + } + }, + "gulp-git": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/gulp-git/-/gulp-git-2.4.2.tgz", + "integrity": "sha512-YgFW2PmtGSHkUYFql75ByRqFjiEDvOk7jxVkkqH0OSDKc3icxD8GDBR1kbr2zG7oNaIynk2NcKVSypgPHl7ibQ==", + "dev": true, + "requires": { + "any-shell-escape": "0.1.1", + "gulp-util": "3.0.8", + "require-dir": "0.3.2", + "strip-bom-stream": "3.0.0", + "through2": "2.0.3", + "vinyl": "2.1.0" + }, + "dependencies": { + "clone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "vinyl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", + "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "dev": true, + "requires": { + "clone": "2.1.1", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.0.0", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" + } + } + } + }, + "gulp-gunzip": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-gunzip/-/gulp-gunzip-1.0.0.tgz", + "integrity": "sha1-FbdBFF6Dqcb1CIYkG1fMWHHxUak=", + "dev": true, + "requires": { + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "gulp-inject-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-inject-string/-/gulp-inject-string-1.1.0.tgz", + "integrity": "sha1-8C3e2w91GN+61Uj30t8FuuLtH2Q=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "gulp-util": "3.0.8" + } + }, + "gulp-remote-src": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gulp-remote-src/-/gulp-remote-src-0.4.3.tgz", + "integrity": "sha1-VyjP1kNDPdSEXd7wlp8PlxoqtKE=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "node.extend": "1.1.6", + "request": "2.79.0", + "through2": "2.0.3", + "vinyl": "2.0.2" + }, + "dependencies": { + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "request": { + "version": "2.79.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "qs": "6.3.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.4.3", + "uuid": "3.1.0" + } + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + }, + "vinyl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.0.2.tgz", + "integrity": "sha1-CjcT2NTpIhxY8QyhbAEWyeJe2nw=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.0.0", + "is-stream": "1.1.0", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" + } + } + } + }, + "gulp-shell": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.6.3.tgz", + "integrity": "sha1-Lqpu3/+ovf96jwufmFKHbzMzHGs=", + "dev": true, + "requires": { + "async": "2.5.0", + "gulp-util": "3.0.8", + "lodash": "4.17.4", + "through2": "2.0.3" + } + }, + "gulp-sourcemaps": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", + "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", + "dev": true, + "requires": { + "convert-source-map": "1.5.0", + "graceful-fs": "4.1.11", + "strip-bom": "2.0.0", + "through2": "2.0.3", + "vinyl": "1.2.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulp-symdest": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-symdest/-/gulp-symdest-1.1.0.tgz", + "integrity": "sha1-wWUyBzLRks5W/ZQnH/oSMjS/KuA=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "mkdirp": "0.5.1", + "queue": "3.1.0", + "vinyl-fs": "2.4.4" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", + "dev": true, + "requires": { + "extend": "3.0.1", + "glob": "5.0.15", + "glob-parent": "3.1.0", + "micromatch": "2.3.11", + "ordered-read-streams": "0.3.0", + "through2": "0.6.5", + "to-absolute-glob": "0.1.1", + "unique-stream": "2.2.1" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", + "dev": true, + "requires": { + "is-stream": "1.1.0", + "readable-stream": "2.3.3" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "strip-bom": "2.0.0" + } + }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "dev": true, + "requires": { + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-fs": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", + "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", + "dev": true, + "requires": { + "duplexify": "3.5.1", + "glob-stream": "5.3.5", + "graceful-fs": "4.1.11", + "gulp-sourcemaps": "1.6.0", + "is-valid-glob": "0.3.0", + "lazystream": "1.0.0", + "lodash.isequal": "4.5.0", + "merge-stream": "1.0.1", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "readable-stream": "2.3.3", + "strip-bom": "2.0.0", + "strip-bom-stream": "1.0.0", + "through2": "2.0.3", + "through2-filter": "2.0.0", + "vali-date": "1.0.0", + "vinyl": "1.2.0" + } + } + } + }, + "gulp-tag-version": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/gulp-tag-version/-/gulp-tag-version-1.3.0.tgz", + "integrity": "sha1-hEjIfu0YZtuObLWYvEGb4t98R9s=", + "dev": true, + "requires": { + "gulp-git": "0.3.6", + "gulp-util": "2.2.20", + "map-stream": "0.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "dev": true + }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "dev": true + }, + "chalk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "dev": true, + "requires": { + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, + "gulp-git": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/gulp-git/-/gulp-git-0.3.6.tgz", + "integrity": "sha1-d+w9oiklwkbt15bKENQWOWXYFAo=", + "dev": true, + "requires": { + "any-shell-escape": "0.1.1", + "gulp-util": "2.2.20", + "map-stream": "0.1.0", + "through2": "0.4.2" + } + }, + "gulp-util": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", + "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", + "dev": true, + "requires": { + "chalk": "0.5.1", + "dateformat": "1.0.12", + "lodash._reinterpolate": "2.4.1", + "lodash.template": "2.4.1", + "minimist": "0.2.0", + "multipipe": "0.1.2", + "through2": "0.5.1", + "vinyl": "0.2.3" + }, + "dependencies": { + "through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "3.0.0" + } + } + } + }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "dev": true, + "requires": { + "ansi-regex": "0.2.1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", + "integrity": "sha1-TxInqlqHEfxjL1sHofRgequLMiI=", + "dev": true + }, + "lodash.escape": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", + "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", + "dev": true, + "requires": { + "lodash._escapehtmlchar": "2.4.1", + "lodash._reunescapedhtml": "2.4.1", + "lodash.keys": "2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + }, + "lodash.template": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", + "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", + "dev": true, + "requires": { + "lodash._escapestringchar": "2.4.1", + "lodash._reinterpolate": "2.4.1", + "lodash.defaults": "2.4.1", + "lodash.escape": "2.4.1", + "lodash.keys": "2.4.1", + "lodash.templatesettings": "2.4.1", + "lodash.values": "2.4.1" + } + }, + "lodash.templatesettings": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", + "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", + "dev": true, + "requires": { + "lodash._reinterpolate": "2.4.1", + "lodash.escape": "2.4.1" + } + }, + "minimist": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", + "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "dev": true, + "requires": { + "ansi-regex": "0.2.1" + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "dev": true + }, + "through2": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "2.1.2" + }, + "dependencies": { + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dev": true, + "requires": { + "object-keys": "0.4.0" + } + } + } + }, + "vinyl": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", + "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", + "dev": true, + "requires": { + "clone-stats": "0.0.1" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "dev": true + } + } + }, + "gulp-tslint": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gulp-tslint/-/gulp-tslint-8.1.2.tgz", + "integrity": "sha512-0RNGqbp2TKPdbG+sWU3mNMXEMuF/noY1KS4+jd5lOStkvuFINkFL29dHX3IT1u+vVFD4Glwf+lkcdR2QMVNMzA==", + "dev": true, + "requires": { + "gulp-util": "3.0.8", + "map-stream": "0.0.7", + "through": "2.3.8" + }, + "dependencies": { + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "dev": true + } + } + }, + "gulp-typings": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/gulp-typings/-/gulp-typings-2.0.4.tgz", + "integrity": "sha1-z7/yMGfR8sRlWqKM87g/QJMQFY8=", + "dev": true, + "requires": { + "through2": "2.0.3", + "typings-core": "1.6.1", + "typings-global": "1.0.20" + } + }, + "gulp-untar": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/gulp-untar/-/gulp-untar-0.0.6.tgz", + "integrity": "sha1-1r3v3n6ajgVMnxYjhaB4LEvnQAA=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "gulp-util": "3.0.8", + "streamifier": "0.1.1", + "tar": "2.2.1", + "through2": "2.0.3" + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.0.0", + "fancy-log": "1.3.0", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", + "replace-ext": "0.0.1", + "through2": "2.0.3", + "vinyl": "0.5.3" + } + }, + "gulp-vinyl-zip": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-vinyl-zip/-/gulp-vinyl-zip-2.1.0.tgz", + "integrity": "sha1-JOQGhdwFtxSZlSRQmeBZAmO+ja0=", + "dev": true, + "requires": { + "event-stream": "3.3.4", + "queue": "4.4.2", + "through2": "2.0.3", + "vinyl": "2.1.0", + "vinyl-fs": "2.4.4", + "yauzl": "2.9.0", + "yazl": "2.4.2" + }, + "dependencies": { + "clone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", + "dev": true, + "requires": { + "extend": "3.0.1", + "glob": "5.0.15", + "glob-parent": "3.1.0", + "micromatch": "2.3.11", + "ordered-read-streams": "0.3.0", + "through2": "0.6.5", + "to-absolute-glob": "0.1.1", + "unique-stream": "2.2.1" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", + "dev": true, + "requires": { + "is-stream": "1.1.0", + "readable-stream": "2.3.3" + } + }, + "queue": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-4.4.2.tgz", + "integrity": "sha512-fSMRXbwhMwipcDZ08enW2vl+YDmAmhcNcr43sCJL8DIg+CFOsoRLG23ctxA+fwNk1w55SePSiS7oqQQSgQoVJQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "strip-bom": "2.0.0" + } + }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "dev": true, + "requires": { + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" + } + }, + "vinyl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", + "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "dev": true, + "requires": { + "clone": "2.1.1", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.0.0", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" + } + }, + "vinyl-fs": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", + "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", + "dev": true, + "requires": { + "duplexify": "3.5.1", + "glob-stream": "5.3.5", + "graceful-fs": "4.1.11", + "gulp-sourcemaps": "1.6.0", + "is-valid-glob": "0.3.0", + "lazystream": "1.0.0", + "lodash.isequal": "4.5.0", + "merge-stream": "1.0.1", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "readable-stream": "2.3.3", + "strip-bom": "2.0.0", + "strip-bom-stream": "1.0.0", + "through2": "2.0.3", + "through2-filter": "2.0.0", + "vali-date": "1.0.0", + "vinyl": "1.2.0" + }, + "dependencies": { + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.9.0", + "is-my-json-valid": "2.16.1", + "pinkie-promise": "2.0.1" + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "1.0.0" + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "1.0.0" + } + }, + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", + "dev": true + }, + "http-proxy-agent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz", + "integrity": "sha1-zBzjjkU7+YSg93AtLdWcc9CBKEo=", + "dev": true, + "requires": { + "agent-base": "2.1.1", + "debug": "2.6.8", + "extend": "3.0.1" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "https-proxy-agent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz", + "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", + "dev": true, + "requires": { + "agent-base": "2.1.1", + "debug": "2.6.8", + "extend": "3.0.1" + } + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.3.3", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "dev": true + }, + "int64-buffer": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-0.1.9.tgz", + "integrity": "sha1-ngOdoEOyT3ixlrKD4EZT716ZD2E=" + }, + "interpret": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz", + "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=", + "dev": true + }, + "invariant": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "dev": true, + "requires": { + "loose-envify": "1.3.1" + } + }, + "is": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", + "integrity": "sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU=", + "dev": true + }, + "is-absolute": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", + "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", + "dev": true, + "requires": { + "is-relative": "0.2.1", + "is-windows": "0.2.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "is-my-json-valid": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", + "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", + "dev": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "1.0.1" + } + }, + "is-relative": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", + "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", + "dev": true, + "requires": { + "is-unc-path": "0.1.2" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unc-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", + "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", + "dev": true, + "requires": { + "unc-path-regex": "0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-valid-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=", + "dev": true + }, + "is-windows": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", + "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } + }, + "liftoff": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.3.0.tgz", + "integrity": "sha1-qY8v9nGD2Lp8+soQVIvX/wVQs4U=", + "dev": true, + "requires": { + "extend": "3.0.1", + "findup-sync": "0.4.3", + "fined": "1.1.0", + "flagged-respawn": "0.3.2", + "lodash.isplainobject": "4.0.6", + "lodash.isstring": "4.0.1", + "lodash.mapvalues": "4.6.0", + "rechoir": "0.6.2", + "resolve": "1.4.0" + } + }, + "listify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/listify/-/listify-1.0.0.tgz", + "integrity": "sha1-A8p7otFQ1CZ3c/dOV1WNEFPSvuM=", + "dev": true + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + } + } + }, + "lockfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.3.tgz", + "integrity": "sha1-Jjj8OaAzHpysGgS3F5mTHJxQ33k=", + "dev": true + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash.keys": "3.1.2" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._escapehtmlchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", + "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", + "dev": true, + "requires": { + "lodash._htmlescapes": "2.4.1" + } + }, + "lodash._escapestringchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", + "integrity": "sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._htmlescapes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", + "integrity": "sha1-MtFL8IRLbeb4tioFG09nwii2JMs=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=", + "dev": true + }, + "lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._reunescapedhtml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", + "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", + "dev": true, + "requires": { + "lodash._htmlescapes": "2.4.1", + "lodash.keys": "2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + } + } + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "dev": true, + "requires": { + "lodash._objecttypes": "2.4.1" + } + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "dev": true, + "requires": { + "lodash._baseassign": "3.2.0", + "lodash._basecreate": "3.0.3", + "lodash._isiterateecall": "3.0.9" + } + }, + "lodash.defaults": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", + "dev": true, + "requires": { + "lodash._objecttypes": "2.4.1", + "lodash.keys": "2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + } + } + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "3.0.1" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "2.4.1" + } + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" + } + }, + "lodash.values": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", + "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", + "dev": true, + "requires": { + "lodash.keys": "2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + } + } + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" + } + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "make-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", + "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", + "dev": true + }, + "make-error-cause": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", + "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", + "dev": true, + "requires": { + "make-error": "1.3.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + } + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", + "dev": true + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "dev": true, + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mocha": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", + "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "he": "1.1.1", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "msgpack-lite": { + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/msgpack-lite/-/msgpack-lite-0.1.26.tgz", + "integrity": "sha1-3TxQsm8FnyXn7e42REGDWOKprYk=", + "requires": { + "event-lite": "0.1.1", + "ieee754": "1.1.8", + "int64-buffer": "0.1.9", + "isarray": "1.0.0" + } + }, + "multimatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", + "dev": true, + "requires": { + "array-differ": "1.0.0", + "array-union": "1.0.2", + "arrify": "1.0.1", + "minimatch": "3.0.4" + } + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + } + }, + "natives": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.0.tgz", + "integrity": "sha1-6f+EFBimsux6SV6TmYT3jxY+bjE=", + "dev": true + }, + "neovim": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/neovim/-/neovim-3.5.0.tgz", + "integrity": "sha1-VbgpeFzaOSe19Gi6Wpif19v5Fjk=", + "requires": { + "lodash": "4.17.4", + "msgpack-lite": "0.1.26", + "traverse": "0.6.6", + "winston": "2.4.0" + } + }, + "node.extend": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-1.1.6.tgz", + "integrity": "sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y=", + "dev": true, + "requires": { + "is": "3.2.1" + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1.1.0" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "4.3.6", + "validate-npm-package-license": "3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "2.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "dev": true + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "1.0.1", + "array-slice": "1.0.0", + "for-own": "1.0.0", + "isobject": "3.0.1" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.8.2" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "orchestrator": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", + "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", + "dev": true, + "requires": { + "end-of-stream": "0.1.5", + "sequencify": "0.0.7", + "stream-consume": "0.1.0" + } + }, + "ordered-read-streams": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", + "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "dev": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "parse-filepath": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.1.tgz", + "integrity": "sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M=", + "dev": true, + "requires": { + "is-absolute": "0.2.6", + "map-cache": "0.2.2", + "path-root": "0.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "1.3.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "0.1.2" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + } + } + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" + } + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + } + } + }, + "plugin-log": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/plugin-log/-/plugin-log-0.1.0.tgz", + "integrity": "sha1-hgSc9qsQgzOYqTHzaJy67nteEzM=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "dateformat": "1.0.12" + }, + "dependencies": { + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + } + } + }, + "popsicle": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/popsicle/-/popsicle-8.2.0.tgz", + "integrity": "sha1-/0QBAFyrQ6lBipFBBhHAAZdxLSE=", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "arrify": "1.0.1", + "concat-stream": "1.6.0", + "form-data": "2.3.1", + "make-error-cause": "1.2.2", + "throwback": "1.1.1", + "tough-cookie": "2.3.2", + "xtend": "4.0.1" + } + }, + "popsicle-proxy-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/popsicle-proxy-agent/-/popsicle-proxy-agent-3.0.0.tgz", + "integrity": "sha1-uRM8VdlFdZq37mG3cRNkYg066tw=", + "dev": true, + "requires": { + "http-proxy-agent": "1.0.0", + "https-proxy-agent": "1.0.0" + } + }, + "popsicle-retry": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/popsicle-retry/-/popsicle-retry-3.2.1.tgz", + "integrity": "sha1-4G6GZTO0KnoSPrMwy+Y6fOvLoQw=", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "xtend": "4.0.1" + } + }, + "popsicle-rewrite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/popsicle-rewrite/-/popsicle-rewrite-1.0.0.tgz", + "integrity": "sha1-HdTo6pwxgjUfuCD4eTTZkvf7kAc=", + "dev": true + }, + "popsicle-status": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/popsicle-status/-/popsicle-status-2.0.1.tgz", + "integrity": "sha1-jdcMT+fGlBCa3XhP/oDqysHnso0=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "promise-finally": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/promise-finally/-/promise-finally-2.2.1.tgz", + "integrity": "sha1-ImFsS6kCkW6Yi9RsVNfKoIkQzXc=", + "dev": true, + "requires": { + "any-promise": "1.3.0" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", + "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", + "dev": true + }, + "querystringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=", + "dev": true + }, + "queue": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/queue/-/queue-3.1.0.tgz", + "integrity": "sha1-bEnQHwCeIlZ4h4nyv/rGuLmZBYU=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "rc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "dev": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "1.4.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "2.1.0", + "strip-indent": "1.0.1" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "1.0.2" + } + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "dev": true, + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.1", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "dev": true, + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + } + } + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "requires": { + "ajv": "5.3.0", + "har-schema": "2.0.0" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "dev": true, + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.1.0" + } + }, + "hoek": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + } + } + }, + "require-dir": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/require-dir/-/require-dir-0.3.2.tgz", + "integrity": "sha1-wdXHXp+//eny5rM+OD209ZS1pqk=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", + "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "resolve-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", + "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", + "dev": true, + "requires": { + "expand-tilde": "1.2.2", + "global-modules": "0.2.3" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + } + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + }, + "sequencify": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", + "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shelljs": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "dev": true, + "requires": { + "glob": "7.1.2", + "interpret": "1.0.4", + "rechoir": "0.6.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + } + } + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "smartq": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/smartq/-/smartq-1.1.6.tgz", + "integrity": "sha512-W7vTj4kSqN8kHVq+Q6BJTr/UGc36TnO0pzKNU8B4cr7TXG4N98eyubWaaCHPSjCUqDgmUPPru929WXzetai97A==", + "dev": true, + "requires": { + "typings-global": "1.0.20", + "util.promisify": "1.0.0" + } + }, + "smartshell": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/smartshell/-/smartshell-1.0.18.tgz", + "integrity": "sha512-yUeTSEUYbbxTPI1AI/vyYEnjRWW77dzKcQviGPyEn1Bu/cy7QFRtFdEVf7qhRFO8r1aqCS/MqT+obtgHphhlrg==", + "dev": true, + "requires": { + "@types/shelljs": "0.7.4", + "@types/which": "1.0.28", + "shelljs": "0.7.8", + "smartq": "1.1.6", + "typings-global": "1.0.20", + "which": "1.3.0" + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "1.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.0.tgz", + "integrity": "sha512-vUoN3I7fHQe0R/SJLKRdKYuEdRGogsviXFkHHo17AWaTGv17VLnxw+CFXvqy+y4ORZ3doWLQcxRYfwKrsd/H7Q==", + "dev": true, + "requires": { + "source-map": "0.6.1" + } + }, + "sparkles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", + "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", + "dev": true + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true, + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "stat-mode": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", + "integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=", + "dev": true + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "0.1.1" + } + }, + "stream-consume": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", + "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=", + "dev": true + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "streamfilter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/streamfilter/-/streamfilter-1.0.5.tgz", + "integrity": "sha1-h1BxEb644phFFxe1Ec/tjwAqv1M=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } + }, + "streamifier": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/streamifier/-/streamifier-0.1.1.tgz", + "integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=", + "dev": true + }, + "string-template": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz", + "integrity": "sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=", + "dev": true + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "is-utf8": "0.2.1" + } + }, + "strip-bom-buf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz", + "integrity": "sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-3.0.0.tgz", + "integrity": "sha1-lWvMXYRDD2klapDtgjdlzYWOFZw=", + "dev": true, + "requires": { + "first-chunk-stream": "2.0.0", + "strip-bom-buf": "1.0.0" + }, + "dependencies": { + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } + } + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "dev": true, + "requires": { + "any-promise": "1.3.0" + } + }, + "throat": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-3.2.0.tgz", + "integrity": "sha512-/EY8VpvlqJ+sFtLPeOgc8Pl7kQVOWv0woD87KTXVHPIAE842FGT+rokxIhe8xIUP1cfgrkt0as0vDLjDiMtr8w==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.3", + "xtend": "4.0.1" + } + }, + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "dev": true, + "requires": { + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "throwback": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/throwback/-/throwback-1.1.1.tgz", + "integrity": "sha1-8AfnwXYEptFtegfEGqDo/txhhKQ=", + "dev": true, + "requires": { + "any-promise": "1.3.0" + } + }, + "tildify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", + "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "to-absolute-glob": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", + "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "touch": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-1.0.0.tgz", + "integrity": "sha1-RJy+LbrlqMgDjjDXH6D/RklHxN4=", + "dev": true, + "requires": { + "nopt": "1.0.10" + } + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "tslib": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz", + "integrity": "sha512-ymKWWZJST0/CkgduC2qkzjMOWr4bouhuURNXCn/inEX0L57BnRG6FhX76o7FOnsjHazCjfU2LKeSrlS2sIKQJg==", + "dev": true + }, + "tslint": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.8.0.tgz", + "integrity": "sha1-H0mtWy53x2w69N3K5VKuTjYS6xM=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.2.0", + "commander": "2.9.0", + "diff": "3.2.0", + "glob": "7.1.2", + "minimatch": "3.0.4", + "resolve": "1.4.0", + "semver": "5.4.1", + "tslib": "1.8.0", + "tsutils": "2.12.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.0.tgz", + "integrity": "sha512-0BMM/2hG3ZaoPfR6F+h/oWpZtsh3b/s62TjSM6MGCJWEbJDN1acqCXvyhhZsDSVFklpebUoQ5O1kKC7lOzrn9g==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "tsutils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.12.1.tgz", + "integrity": "sha1-9Nlc4zkciXHkblTEzw7bCiHdWyQ=", + "dev": true, + "requires": { + "tslib": "1.8.0" + } + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz", + "integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==", + "dev": true + }, + "typings-core": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/typings-core/-/typings-core-1.6.1.tgz", + "integrity": "sha1-zkspMeovGbuPPay+xpmDrE6WSjc=", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "array-uniq": "1.0.3", + "configstore": "2.1.0", + "debug": "2.6.8", + "detect-indent": "4.0.0", + "graceful-fs": "4.1.11", + "has": "1.0.1", + "invariant": "2.2.2", + "is-absolute": "0.2.6", + "listify": "1.0.0", + "lockfile": "1.0.3", + "make-error-cause": "1.2.2", + "mkdirp": "0.5.1", + "object.pick": "1.3.0", + "parse-json": "2.2.0", + "popsicle": "8.2.0", + "popsicle-proxy-agent": "3.0.0", + "popsicle-retry": "3.2.1", + "popsicle-rewrite": "1.0.0", + "popsicle-status": "2.0.1", + "promise-finally": "2.2.1", + "rc": "1.2.1", + "rimraf": "2.6.2", + "sort-keys": "1.1.2", + "string-template": "1.0.0", + "strip-bom": "2.0.0", + "thenify": "3.3.0", + "throat": "3.2.0", + "touch": "1.0.0", + "typescript": "2.5.3", + "xtend": "4.0.1", + "zip-object": "0.1.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + } + } + }, + "typings-global": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/typings-global/-/typings-global-1.0.20.tgz", + "integrity": "sha512-m3s4wlDTQ1HaGNMNaoWj/N0OjGCzMfU1x+FrPisDLj7DnL6GxdC/nVk9pl0szGwgniDbzoHB0T1C5L/vMUm61w==", + "dev": true, + "requires": { + "semver": "5.4.1", + "smartshell": "1.0.18" + }, + "dependencies": { + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + } + } + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, + "unique-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", + "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=", + "dev": true + }, + "untildify": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.2.tgz", + "integrity": "sha1-fx8wIFWz/qDz6B3HjrNnZstl4/E=" + }, + "url-parse": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz", + "integrity": "sha1-xn8dd11R8KGJEd17P/rSe7nlvRk=", + "dev": true, + "requires": { + "querystringify": "1.0.0", + "requires-port": "1.0.0" + } + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "object.getownpropertydescriptors": "2.0.3" + } + }, + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + }, + "v8flags": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "dev": true, + "requires": { + "user-home": "1.1.1" + } + }, + "vali-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", + "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true, + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-fs": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", + "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", + "dev": true, + "requires": { + "defaults": "1.0.3", + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.11", + "mkdirp": "0.5.1", + "strip-bom": "1.0.0", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "vinyl-source-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-1.1.0.tgz", + "integrity": "sha1-RMvlEIIFJ53rDFZTwJSiiHk4sas=", + "dev": true, + "requires": { + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "vscode": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.6.tgz", + "integrity": "sha1-Ru0a+iwbnWifY5TI8WvR1xkPdfs=", + "dev": true, + "requires": { + "glob": "7.1.2", + "gulp-chmod": "2.0.0", + "gulp-filter": "5.0.1", + "gulp-gunzip": "1.0.0", + "gulp-remote-src": "0.4.3", + "gulp-symdest": "1.1.0", + "gulp-untar": "0.0.6", + "gulp-vinyl-zip": "2.1.0", + "mocha": "4.0.1", + "request": "2.83.0", + "semver": "5.4.1", + "source-map-support": "0.5.0", + "url-parse": "1.1.9", + "vinyl-source-stream": "1.1.0" + }, + "dependencies": { + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "mocha": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz", + "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + } + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "requires": { + "isexe": "2.0.0" + } + }, + "winston": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.0.tgz", + "integrity": "sha1-gIBQuT1SZh7Z+2wms/DIJnCLCu4=", + "requires": { + "async": "1.0.0", + "colors": "1.0.3", + "cycle": "1.0.3", + "eyes": "0.1.8", + "isstream": "0.1.2", + "stack-trace": "0.0.10" + }, + "dependencies": { + "async": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + } + } + }, + "xdg-basedir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", + "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yauzl": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.0.tgz", + "integrity": "sha1-knqoQ5Lu0FMSRJa26o/B9S2uW1I=", + "dev": true, + "requires": { + "buffer-crc32": "0.2.13", + "fd-slicer": "1.0.1" + } + }, + "yazl": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.4.2.tgz", + "integrity": "sha1-FMsZCD4eJacAksFYiqvg9OTdTYg=", + "dev": true, + "requires": { + "buffer-crc32": "0.2.13" + } + }, + "zip-object": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/zip-object/-/zip-object-0.1.0.tgz", + "integrity": "sha1-waDaBMiMg3dW4khoCgP/kC7D9To=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 6f01b6ccc6a..fb90a17d564 100644 --- a/package.json +++ b/package.json @@ -1,563 +1,631 @@ { - "name": "vim", - "displayName": "Vim", - "description": "Vim emulation for Visual Studio Code", - "icon": "images/icon.png", - "version": "0.9.0", - "publisher": "vscodevim", - "galleryBanner": { - "color": "#e3f4ff", - "theme": "light" - }, - "license": "MIT", - "keywords": [ - "vim", - "vi", - "vscodevim" + "activationEvents": [ + "*" ], - "repository": { - "type": "git", - "url": "https://github.com/VSCodeVim/Vim.git" - }, - "homepage": "https://github.com/VSCodeVim/Vim", "bugs": { "url": "https://github.com/VSCodeVim/Vim/issues" }, - "engines": { - "vscode": "^1.12.0" - }, "categories": [ "Other", "Keymaps" ], - "activationEvents": [ - "*" - ], - "main": "./out/extension", "contributes": { - "commands": [ - { - "command": "extension.showCmdLine", - "title": "Vim: Show Command Line" + "commands": [], + "configuration": { + "properties": { + "vim.enableHighlights": { + "default": true + }, + "vim.ignoreKeys": { + "default": { + "all": [ + "", + "", + "", + "", + "", + "", + "" + ], + "normal": [], + "insert": [ + "", + "", + "", + "", + "", + "", + "", + "" + ], + "visual": [] + } + }, + "vim.neovimPath": { + "type": "string", + "description": "Path to run neovim executable. For example, /usr/bin/nvim, or C:\\Program Files\\Neovim\\bin\\nvim.exe", + "default": "nvim" + } }, + "title": "Vim Configuration", + "type": "object" + }, + "keybindings": [ { - "command": "vim.remap", - "title": "Vim: Remap any key combination that VS Code supports to Vim motions/operators/ExCommands/macro." + "key": "ctrl+space", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "command": "toggleVim", - "title": "Vim: Toggle Vim Mode" - } - ], - "keybindings": [ + "key": "space", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, { - "key": "Escape", - "command": "extension.vim_escape", - "when": "editorTextFocus && vim.active && !inDebugRepl" + "key": "ctrl+left", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "Home", - "command": "extension.vim_home", - "when": "editorTextFocus && vim.active && !inDebugRepl && vim.mode != 'Insert'" + "key": "left", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "End", - "command": "extension.vim_end", - "when": "editorTextFocus && vim.active && !inDebugRepl && vim.mode != 'Insert'" + "key": "ctrl+right", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "cmd+left", - "command": "extension.vim_cmd+left", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl && vim.mode != 'Insert'" + "key": "right", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "cmd+right", - "command": "extension.vim_cmd+right", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl && vim.mode != 'Insert'" + "key": "ctrl+up", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "cmd+d", - "command": "extension.vim_cmd+d", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "up", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "cmd+a", - "command": "extension.vim_cmd+a", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl && vim.mode != 'Insert'" + "key": "ctrl+down", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+d", - "command": "extension.vim_ctrl+d", - "when": "editorTextFocus && vim.active && !inDebugRepl" + "key": "down", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+alt+down", - "linux": "shift+alt+down", - "mac": "cmd+alt+down", - "command": "extension.vim_cmd+alt+down", - "when": "editorTextFocus && vim.active && !inDebugRepl" + "key": "ctrl+Escape", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+alt+up", - "linux": "shift+alt+up", - "mac": "cmd+alt+up", - "command": "extension.vim_cmd+alt+up", - "when": "editorTextFocus && vim.active && !inDebugRepl" + "key": "Escape", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "Backspace", - "command": "extension.vim_backspace", - "when": "editorTextFocus && vim.active && !inDebugRepl" + "key": "ctrl+backspace", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "shift+backspace", - "command": "extension.vim_shift+backspace", - "when": "editorTextFocus && vim.active && vim.use && vim.mode == 'SearchInProgressMode' && !inDebugRepl" + "key": "backspace", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "Delete", - "command": "extension.vim_delete", - "when": "editorTextFocus && vim.active && vim.mode != 'Insert' && !inDebugRepl" + "key": "ctrl+tab", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { "key": "tab", - "command": "extension.vim_tab", - "when": "editorFocus && vim.active && vim.mode != 'Insert' && !inDebugRepl" + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+r", - "command": "extension.vim_ctrl+r", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+enter", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+f", - "command": "extension.vim_ctrl+f", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "enter", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+0", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+7", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+n", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+$", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl++", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+.", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+_", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+:", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { "key": "ctrl+b", - "command": "extension.vim_ctrl+b", - "when": "editorTextFocus && vim.active && vim.use && vim.mode != 'Insert' && !inDebugRepl" + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+e", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { "key": "ctrl+j", - "command": "extension.vim_ctrl+j", - "when": "editorTextFocus && vim.active && vim.use && vim.mode != 'Insert' && !inDebugRepl" + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+k", - "command": "extension.vim_ctrl+k", - "when": "editorTextFocus && vim.active && vim.use && vim.mode != 'Insert' && !inDebugRepl" + "key": "ctrl+|", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+^", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+(", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+3", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+1", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+8", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+!", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+~", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { "key": "ctrl+h", - "command": "extension.vim_ctrl+h", - "when": "editorTextFocus && vim.active && vim.use && vim.mode == 'Insert' && !inDebugRepl" + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+e", - "command": "extension.vim_ctrl+e", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+5", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+y", - "command": "extension.vim_ctrl+y", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+*", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+u", - "command": "extension.vim_ctrl+u", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+6", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+o", - "command": "extension.vim_ctrl+o", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+4", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+q", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { "key": "ctrl+i", - "command": "extension.vim_ctrl+i", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+v", - "command": "extension.vim_ctrl+v", - "when": "editorTextFocus && vim.active && vim.use && vim.mode != 'Insert' && !inDebugRepl" + "key": "ctrl+m", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "cmd+v", - "command": "extension.vim_cmd+v", - "when": "editorTextFocus && vim.active && vim.use && vim.mode == 'SearchInProgressMode' && !inDebugRepl" + "key": "ctrl+g", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+[", - "command": "extension.vim_ctrl+[", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+s", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+w h", - "command": "extension.vim_navigateLeft", - "when": "vim.use && vim.active && !editorTextFocus" + "key": "ctrl+z", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+w l", - "command": "extension.vim_navigateRight", - "when": "vim.use && vim.active && !editorTextFocus" + "key": "ctrl+t", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+w ctrl+w", - "command": "extension.vim_navigateGroups", - "when": "vim.use && vim.active && !editorTextFocus" + "key": "ctrl+>", + "command": "vim.>", + "when": "vim.use_>", + "vimKey": ">" }, { - "key": "ctrl+w j", - "command": "extension.vim_navigateDown", - "when": "vim.use && vim.active && !editorTextFocus" + "key": "ctrl+l", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+w k", - "command": "extension.vim_navigateUp", - "when": "vim.use && vim.active && !editorTextFocus" + "key": "ctrl+\"", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+w", - "command": "extension.vim_ctrl+w", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+a", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+c", - "command": "extension.vim_ctrl+c", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl && vim.overrideCtrlC" + "key": "ctrl+u", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "cmd+c", - "command": "extension.vim_cmd+c", - "when": "editorTextFocus && vim.active && vim.use && vim.overrideCopy && !inDebugRepl" + "key": "ctrl+<", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+a", - "command": "extension.vim_ctrl+a", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+f", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+n", - "command": "extension.vim_ctrl+n", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+`", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+#", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { "key": "ctrl+p", - "command": "extension.vim_ctrl+p", - "when": "suggestWidgetVisible && vim.active && vim.use" + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+x", - "command": "extension.vim_ctrl+x", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+2", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+shift+2", - "command": "extension.vim_ctrl+shift+2", - "when": "editorTextFocus && vim.active && vim.use" + "key": "ctrl+,", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+t", - "command": "extension.vim_ctrl+t", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+9", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+pagedown", - "command": "extension.vim_ctrl+pagedown", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+'", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+pageup", - "command": "extension.vim_ctrl+pageup", - "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl" + "key": "ctrl+?", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "left", - "command": "extension.vim_left", - "when": "editorTextFocus && vim.active && !inDebugRepl" + "key": "ctrl+o", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "right", - "command": "extension.vim_right", - "when": "editorTextFocus && vim.active && !inDebugRepl" + "key": "ctrl+}", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "up", - "command": "extension.vim_up", - "when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !suggestWidgetMultipleSuggestions" + "key": "ctrl+-", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "down", - "command": "extension.vim_down", - "when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !suggestWidgetMultipleSuggestions" + "key": "ctrl+)", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+]", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+c", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+v", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "j", - "command": "list.focusDown", - "when": "listFocus" + "key": "ctrl+x", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "k", - "command": "list.focusUp", - "when": "listFocus" + "key": "ctrl+%", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "o", - "command": "list.toggleExpand", - "when": "listFocus" + "key": "ctrl+y", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "g g", - "command": "list.focusFirst", - "when": "listFocus" + "key": "ctrl+;", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "shift+G", - "command": "list.focusLast", - "when": "listFocus" + "key": "ctrl+\\", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { "key": "ctrl+d", - "command": "list.focusPageDown", - "when": "listFocus" + "command": "vim.", + "when": "vim.use_", + "vimKey": "" }, { - "key": "ctrl+u", - "command": "list.focusPageUp", - "when": "listFocus" - } - ], - "configuration": { - "title": "Vim Configuration", - "type": "object", - "properties": { - "vim.otherModesKeyBindings": { - "type": "array", - "description": "Remapped keys in normal mode. Allows mapping to vim commands or vscode actions. See README for more." - }, - "vim.otherModesKeyBindingsNonRecursive": { - "type": "array", - "description": "Non-recursive remapped keys in normal mode. Allows mapping to vim commands or vscode actions. See README for more." - }, - "vim.useCtrlKeys": { - "type": "boolean", - "description": "Enable some vim ctrl key commands that override otherwise common operations, like ctrl+c", - "default": true - }, - "vim.leader": { - "type": "string", - "description": "What key should map to in remappings?", - "default": "\\" - }, - "vim.searchHighlightColor": { - "type": "string", - "description": "Color of the search highlight.", - "default": "rgba(150, 150, 255, 0.3)" - }, - "vim.useSystemClipboard": { - "type": "boolean", - "description": "Use system clipboard for unnamed register.", - "default": false - }, - "vim.overrideCopy": { - "type": "boolean", - "description": "Override VSCode's copy command with our own copy command, which works better with VSCodeVim. Turn this off if copying is not working.", - "default": true - }, - "vim.insertModeKeyBindings": { - "type": "array", - "description": "Remapped keys in insert mode. Allows mapping to vim commands or vscode actions. See README for more." - }, - "vim.insertModeKeyBindingsNonRecursive": { - "type": "array", - "description": "Non-recursive keybinding overrides to use for insert mode. Allows mapping to vim commands or vscode actions. See README for more." - }, - "vim.textwidth": { - "type": "number", - "description": "Width to word-wrap to when using gq.", - "default": 80 - }, - "vim.timeout": { - "type": "number", - "description": "Timeout in milliseconds for remapped commands", - "default": 1000 - }, - "vim.scroll": { - "type": "number", - "description": "Number of lines to scroll with CTRL-U and CTRL-D commands.", - "default": 20 - }, - "vim.showcmd": { - "type": "boolean", - "description": "Show the text of any command you are in the middle of writing.", - "default": true - }, - "vim.iskeyword": { - "type": "string", - "description": "keywords contain alphanumeric characters and '_'", - "default": "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-" - }, - "vim.ignorecase": { - "type": "boolean", - "description": "Ignore case in search patterns.", - "default": true - }, - "vim.smartcase": { - "type": "boolean", - "description": "Override the 'ignorecase' option if the search pattern contains upper case characters.", - "default": true - }, - "vim.easymotion": { - "type": "boolean", - "description": "Enable the EasyMotion plugin for Vim.", - "default": false - }, - "vim.easymotionMarkerBackgroundColor": { - "type": "string", - "description": "Set a custom background color for EasyMotion markers.", - "default": "#000000" - }, - "vim.easymotionMarkerForegroundColorOneChar": { - "type": "string", - "description": "Set a custom color for the text on one character long markers.", - "default": "#ff0000" - }, - "vim.easymotionMarkerForegroundColorTwoChar": { - "type": "string", - "description": "Set a custom color for the text on two character long markers.", - "default": "#ffa500" - }, - "vim.easymotionMarkerWidthPerChar": { - "type": "number", - "description": "Set the width (in pixels) allocated to each character in the match.", - "default": 8 - }, - "vim.easymotionMarkerHeight": { - "type": "number", - "description": "Set the height of the marker.", - "default": 14 - }, - "vim.easymotionMarkerFontFamily": { - "type": "string", - "description": "Set the font family of the marker text.", - "default": "Consolas" - }, - "vim.easymotionMarkerFontSize": { - "type": "string", - "description": "Set the font size of the marker text.", - "default": "14" - }, - "vim.easymotionMarkerFontWeight": { - "type": "string", - "description": "Set the font weight of the marker text.", - "default": "normal" - }, - "vim.easymotionMarkerYOffset": { - "type": "number", - "description": "Set the Y offset of the marker text (the distance from the top).", - "default": 11 - }, - "vim.surround": { - "type": "boolean", - "description": "Enable the Surround plugin for Vim.", - "default": true - }, - "vim.hlsearch": { - "type": "boolean", - "description": "Show all matches of the most recent search pattern", - "default": false - }, - "vim.incsearch": { - "type": "boolean", - "description": "Show where a / search matches as you type it.", - "default": true - }, - "vim.history": { - "type": "number", - "description": "How much search or command history should be remembered", - "default": 50 - }, - "vim.autoindent": { - "type": "boolean", - "description": "Indent code automatically.", - "default": true - }, - "vim.startInInsertMode": { - "type": "boolean", - "description": "Start in Insert Mode." - }, - "vim.handleKeys": { - "type": "object", - "description": "Option to delegate certain key combinations back to VSCode to be handled natively" - }, - "vim.statusBarColorControl": { - "type": "boolean", - "description": "Allow VSCodeVim to change status bar color based on mode" - }, - "vim.statusBarColors": { - "type": "object", - "description": "Customize colors per mode when VSCodeVim controls status bar colors" - }, - "vim.visualstar": { - "type": "boolean", - "description": "In visual mode, start a search with * or # using the current selection", - "default": false - }, - "vim.foldfix": { - "type": "boolean", - "description": "Uses a hack to move around folds properly", - "default": false - }, - "vim.enableNeovim": { - "type": "boolean", - "description": "Use neovim on backend. (only works for Ex commands right now). You should restart VScode after enable/disabling this for the changes to take effect. NOTE: Neovim must be installed (v0.2.0) and neovimPath must be set the executable in order for this setting to work. Otherwise, vscodevim will crash.", - "default": false - }, - "vim.neovimPath": { - "type": "string", - "description": "Path to run neovim executable. For example, /usr/bin/nvim, or C:\\Program Files\\Neovim\\bin\\nvim.exe", - "default": "nvim" - }, - "vim.disableAnnoyingNeovimMessage": { - "type": "boolean", - "description": "Get rid of that annoying message that shows up everytime you make a new file", - "default": false - } + "key": "ctrl+r", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+w", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+[", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+=", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+@", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+{", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+/", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+&", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" + }, + { + "key": "ctrl+k", + "command": "vim.", + "when": "vim.use_", + "vimKey": "" } - } - }, - "scripts": { - "vscode:prepublish": "tsc -p ./", - "compile": "tsc -watch -p ./", - "test": "node ./node_modules/vscode/bin/test", - "postinstall": "node ./node_modules/vscode/bin/install && gulp init" + ] }, "dependencies": { + "@types/msgpack-lite": "^0.1.5", + "@types/winston": "^2.3.6", + "aurelia-task-queue": "^1.2.1", "clipboardy": "^1.1.4", "diff-match-patch": "^1.0.0", - "lodash": "^4.12.0", - "neovim-client": "^2.1.0", - "promised-neovim-client": "^2.0.2", + "lodash": "^4.17.4", + "neovim": "3.5.0", + "tmp": "0.0.33", "untildify": "^3.0.2" }, + "description": "Vim emulation for Visual Studio Code", "devDependencies": { - "@types/mocha": "^2.2.39", - "@types/node": "^6.0.41", + "@types/mocha": "^2.2.41", + "@types/node": "^8.0.47", "gulp": "^3.9.1", - "gulp-bump": "^2.1.0", + "gulp-bump": "^2.8.0", "gulp-git": "^2.4.1", "gulp-inject-string": "^1.1.0", - "gulp-shell": "^0.5.2", + "gulp-shell": "^0.6.3", "gulp-tag-version": "^1.3.0", - "gulp-tslint": "^7.0.1", - "gulp-typings": "^2.0.0", - "merge-stream": "^1.0.0", - "mocha": "^3.2.0", - "tslint": "^4.3.1", - "typescript": "^2.3.2", - "vscode": "^1.0.5" - } -} + "gulp-tslint": "^8.1.1", + "gulp-typings": "^2.0.4", + "merge-stream": "^1.0.1", + "mocha": "^3.4.2", + "tslint": "^5.5.0", + "typescript": "^2.5.3", + "vscode": "^1.1.6" + }, + "displayName": "Vim", + "engines": { + "vscode": "^1.13.0" + }, + "galleryBanner": { + "color": "#e3f4ff", + "theme": "light" + }, + "homepage": "https://github.com/VSCodeVim/Vim", + "icon": "images/icon.png", + "keywords": [ + "vim", + "vi", + "vscodevim" + ], + "license": "MIT", + "main": "./out/extension", + "name": "vim", + "publisher": "vscodevim", + "repository": { + "type": "git", + "url": "https://github.com/VSCodeVim/Vim.git" + }, + "scripts": { + "compile": "tsc -watch -p ./", + "postinstall": "node ./node_modules/vscode/bin/install && gulp init", + "test": "node ./node_modules/vscode/bin/test", + "vscode:prepublish": "tsc -p ./" + }, + "version": "1.0.0" +} \ No newline at end of file diff --git a/scripts/keybind_gen.py b/scripts/keybind_gen.py new file mode 100644 index 00000000000..8c3706d39b7 --- /dev/null +++ b/scripts/keybind_gen.py @@ -0,0 +1,57 @@ +import json +package = open('../package.json').read() +package = json.loads(package) +keysToBind = ['space', 'left', 'right', 'up', 'down', 'esc', 'bs', 'tab', 'cr'] +vimKeyToVS = {'esc': 'Escape', 'bs': 'backspace', 'cr': 'enter'} +keybindings = [] +for key in keysToBind: + vsKey = key + if key in vimKeyToVS: + vsKey = vimKeyToVS[key] + for modifier in ['ctrl']: + modKey = '{0}+{1}'.format(modifier, vsKey) + vimKey = '<{0}-{1}>'.format(modifier[0], key) + keybind = {'key': modKey, + 'command': 'vim.{0}'.format(vimKey), + 'when': 'vim.use_{0}'.format(vimKey), + 'vimKey': vimKey} + keybindings.append(keybind) + if len(key) > 1: + key = '<{0}>'.format(key) + keybind = {'key': vsKey, 'command': 'vim.{0}'.format( + key), 'when': 'vim.use_{0}'.format(key), 'vimKey': key} + keybindings.append(keybind) + +keysToBind = [] +for i in range(ord('!'), ord('~') + 1): + keysToBind.append(chr(i).lower()) + +keysToBind = list(set(keysToBind)) + +for key in keysToBind: + vsKey = key + if key in vimKeyToVS: + vsKey = vimKeyToVS[key] + modifier = 'ctrl' + modKey = '{0}+{1}'.format(modifier, vsKey) + vimKey = '<{0}-{1}>'.format(modifier[0], key) + keybind = {'key': modKey, + 'command': 'vim.{0}'.format(vimKey), + 'when': 'vim.use_{0}'.format(vimKey), + 'vimKey': vimKey} + keybindings.append(keybind) + + +package['contributes']['keybindings'] = keybindings +open('../package.json', 'w').write(json.dumps(package, indent=2, sort_keys=False)) +# keybind[] +# // let vimToVSMap: {[key: string]: string[]} = { +# // }; +# // let vimToVSMap: {[key: string]: string} = { +# // esc: 'escape', +# // }; +# for (let i='!'.charCodeAt(0); i <= '~'.charCodeAt(0); i + +) { +# keysToBind.push(String.fromCharCode(i));} +# for (let key of keysToBind) { +# for (let modifier of['c', 's']) { +# const modKey = `${modifier} -${key}`; diff --git a/src/actions/base.ts b/src/actions/base.ts deleted file mode 100644 index a10effe4026..00000000000 --- a/src/actions/base.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { VimState } from './../mode/modeHandler'; -import { ModeName } from './../mode/mode'; -import { Configuration } from './../configuration/configuration'; - -const is2DArray = function (x: any): x is T[][] { - return Array.isArray(x[0]); -}; - -export let compareKeypressSequence = function (one: string[] | string[][], two: string[]): boolean { - if (is2DArray(one)) { - for (const sequence of one) { - if (compareKeypressSequence(sequence, two)) { - return true; - } - } - - return false; - } - - if (one.length !== two.length) { - return false; - } - - const isSingleNumber = (s: string): boolean => { - return s.length === 1 && "1234567890".indexOf(s) > -1; - }; - - const containsControlKey = (s: string): boolean => { - // We count anything starting with < (e.g. ) as a control key, but we - // exclude the first 3 because it's more convenient to do so. - - return s.toUpperCase() !== "" && - s.toUpperCase() !== "" && - s.toUpperCase() !== "" && - s.startsWith("<") && - s.length > 1; - }; - - for (let i = 0, j = 0; i < one.length; i++ , j++) { - const left = one[i], right = two[j]; - - if (left === "") { continue; } - if (right === "") { continue; } - - if (left === "" && isSingleNumber(right)) { continue; } - if (right === "" && isSingleNumber(left)) { continue; } - - if (left === "" && !containsControlKey(right)) { continue; } - if (right === "" && !containsControlKey(left)) { continue; } - - if (left === "" && right === Configuration.leader) { continue; } - if (right === "" && left === Configuration.leader) { continue; } - - if (left === Configuration.leader) { return false; } - if (right === Configuration.leader) { return false; } - - if (left !== right) { return false; } - } - - return true; -}; - -export class BaseAction { - /** - * Can this action be paired with an operator (is it like w in dw)? All - * BaseMovements can be, and some more sophisticated commands also can be. - */ - public isMotion = false; - - public canBeRepeatedWithDot = false; - - /** - * Modes that this action can be run in. - */ - public modes: ModeName[]; - - /** - * The sequence of keys you use to trigger the action, or a list of such sequences. - */ - public keys: string[] | string[][]; - - public mustBeFirstKey = false; - - public isOperator = false; - - /** - * The keys pressed at the time that this action was triggered. - */ - public keysPressed: string[] = []; - - /** - * Is this action valid in the current Vim state? - */ - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - if (this.modes.indexOf(vimState.currentMode) === -1) { return false; } - if (!compareKeypressSequence(this.keys, keysPressed)) { return false; } - if (vimState.recordedState.getCurrentCommandWithoutCountPrefix().length - keysPressed.length > 0 && - this.mustBeFirstKey) { return false; } - - return true; - } - - /** - * Could the user be in the process of doing this action. - */ - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - if (this.modes.indexOf(vimState.currentMode) === -1) { return false; } - if (!compareKeypressSequence(this.keys.slice(0, keysPressed.length), keysPressed)) { return false; } - if (vimState.recordedState.getCurrentCommandWithoutCountPrefix().length - keysPressed.length > 0 && - this.mustBeFirstKey) { return false; } - - - return true; - } - - - public toString(): string { - return this.keys.join(""); - } -} - -export enum KeypressState { - WaitingOnKeys, - NoPossibleMatch -} - - -export class Actions { - - /** - * Every Vim action will be added here with the @RegisterAction decorator. - */ - public static allActions: { type: typeof BaseAction, action: BaseAction }[] = []; - - /** - * Gets the action that should be triggered given a key - * sequence. - * - * If there is a definitive action that matched, returns that action. - * - * If an action could potentially match if more keys were to be pressed, returns true. (e.g. - * you pressed "g" and are about to press "g" action to make the full action "gg".) - * - * If no action could ever match, returns false. - */ - public static getRelevantAction(keysPressed: string[], vimState: VimState): BaseAction |KeypressState { - let couldPotentiallyHaveMatch = false; - - for (const thing of Actions.allActions) { - const { type, action } = thing!; - - // It's an action that can't be called directly. - if (action.keys === undefined) { - continue; - } - if (action.doesActionApply(vimState, keysPressed)) { - const result = new type(); - - result.keysPressed = vimState.recordedState.actionKeys.slice(0); - - return result; - } - - if (action.couldActionApply(vimState, keysPressed)) { - couldPotentiallyHaveMatch = true; - } - } - - return couldPotentiallyHaveMatch ? KeypressState.WaitingOnKeys : KeypressState.NoPossibleMatch; - } -} - -export function RegisterAction(action: typeof BaseAction): void { - Actions.allActions.push({ type: action, action: new action() }); -} \ No newline at end of file diff --git a/src/actions/commands/actions.ts b/src/actions/commands/actions.ts deleted file mode 100644 index 32aadbe4451..00000000000 --- a/src/actions/commands/actions.ts +++ /dev/null @@ -1,3209 +0,0 @@ -import { RecordedState, VimState } from './../../mode/modeHandler'; -import { SearchState, SearchDirection } from './../../state/searchState'; -import { ReplaceState } from './../../state/replaceState'; -import { VisualBlockMode } from './../../mode/modeVisualBlock'; -import { ModeName } from './../../mode/mode'; -import { Range } from './../../common/motion/range'; -import { TextEditor, EditorScrollByUnit, EditorScrollDirection } from './../../textEditor'; -import { Register, RegisterMode } from './../../register/register'; -import { NumericString } from './../../common/number/numericString'; -import { Position, PositionDiff } from './../../common/motion/position'; -import { Tab, TabCommand } from './../../cmd_line/commands/tab'; -import { Configuration } from './../../configuration/configuration'; -import { allowVSCodeToPropagateCursorUpdatesAndReturnThem } from '../../util'; -import { isTextTransformation } from './../../transformations/transformations'; -import { FileCommand } from './../../cmd_line/commands/file'; -import { QuitCommand } from './../../cmd_line/commands/quit'; -import * as vscode from 'vscode'; -import * as util from './../../util'; -import { RegisterAction } from './../base'; -import * as operator from './../operator'; -import { BaseAction } from './../base'; - -export class DocumentContentChangeAction extends BaseAction { - contentChanges: { - positionDiff: PositionDiff; - textDiff: vscode.TextDocumentContentChangeEvent; - }[] = []; - - public async exec(position: Position, vimState: VimState): Promise { - if (this.contentChanges.length === 0) { - return vimState; - } - - let originalLeftBoundary: vscode.Position; - - if (this.contentChanges[0].textDiff.text === "" && this.contentChanges[0].textDiff.rangeLength === 1) { - originalLeftBoundary = this.contentChanges[0].textDiff.range.end; - } else { - originalLeftBoundary = this.contentChanges[0].textDiff.range.start; - } - - let rightBoundary: vscode.Position = position; - let newStart: vscode.Position | undefined; - let newEnd: vscode.Position | undefined; - - for (let i = 0; i < this.contentChanges.length; i++) { - let contentChange = this.contentChanges[i].textDiff; - - if (contentChange.range.start.line < originalLeftBoundary.line) { - // This change should be ignored - let linesEffected = contentChange.range.end.line - contentChange.range.start.line + 1; - let resultLines = contentChange.text.split("\n").length; - originalLeftBoundary = originalLeftBoundary.with(originalLeftBoundary.line + resultLines - linesEffected); - continue; - } - - if (contentChange.range.start.line === originalLeftBoundary.line) { - newStart = position.with(position.line, position.character + contentChange.range.start.character - originalLeftBoundary.character); - - if (contentChange.range.end.line === originalLeftBoundary.line) { - newEnd = position.with(position.line, position.character + contentChange.range.end.character - originalLeftBoundary.character); - } else { - newEnd = position.with(position.line + contentChange.range.end.line - originalLeftBoundary.line, - contentChange.range.end.character); - } - } else { - newStart = position.with(position.line + contentChange.range.start.line - originalLeftBoundary.line, - contentChange.range.start.character); - newEnd = position.with(position.line + contentChange.range.end.line - originalLeftBoundary.line, - contentChange.range.end.character); - } - - if (newStart.isAfter(rightBoundary)) { - // This change should be ignored as it's out of boundary - continue; - } - - // Calculate new right boundary - let newLineCount = contentChange.text.split('\n').length; - let newRightBoundary: vscode.Position; - - if (newLineCount === 1) { - newRightBoundary = newStart.with(newStart.line, newStart.character + contentChange.text.length); - } else { - newRightBoundary = new vscode.Position(newStart.line + newLineCount - 1, contentChange.text.split('\n').pop()!.length); - } - - if (newRightBoundary.isAfter(rightBoundary)) { - rightBoundary = newRightBoundary; - } - - vimState.editor.selection = new vscode.Selection(newStart, newEnd); - - if (newStart.isEqual(newEnd)) { - await TextEditor.insert(contentChange.text, Position.FromVSCodePosition(newStart)); - } else { - await TextEditor.replace(vimState.editor.selection, contentChange.text); - } - } - - /** - * We're making an assumption here that content changes are always in order, and I'm not sure - * we're guaranteed that, but it seems to work well enough in practice. - */ - if (newStart && newEnd) { - const last = this.contentChanges[this.contentChanges.length - 1]; - - vimState.cursorStartPosition = Position.FromVSCodePosition(newStart) - .advancePositionByText(last.textDiff.text) - .add(last.positionDiff); - vimState.cursorPosition = Position.FromVSCodePosition(newEnd) - .advancePositionByText(last.textDiff.text) - .add(last.positionDiff); - } - - vimState.currentMode = ModeName.Insert; - return vimState; - } -} - -/** - * A command is something like , :, v, i, etc. - */ -export abstract class BaseCommand extends BaseAction { - /** - * If isCompleteAction is true, then triggering this command is a complete action - - * that means that we'll go and try to run it. - */ - isCompleteAction = true; - - multicursorIndex: number | undefined = undefined; - - /** - * In multi-cursor mode, do we run this command for every cursor, or just once? - */ - public runsOnceForEveryCursor(): boolean { - return true; - } - - /** - * If true, exec() will get called N times where N is the count. - * - * If false, exec() will only be called once, and you are expected to - * handle count prefixes (e.g. the 3 in 3w) yourself. - */ - runsOnceForEachCountPrefix = false; - - canBeRepeatedWithDot = false; - - /** - * Run the command a single time. - */ - public async exec(position: Position, vimState: VimState): Promise { - throw new Error("Not implemented!"); - } - - /** - * Run the command the number of times VimState wants us to. - */ - public async execCount(position: Position, vimState: VimState): Promise { - if (!this.runsOnceForEveryCursor()) { - let timesToRepeat = this.runsOnceForEachCountPrefix ? vimState.recordedState.count || 1 : 1; - - for (let i = 0; i < timesToRepeat; i++) { - vimState = await this.exec(position, vimState); - } - - for (const transformation of vimState.recordedState.transformations) { - if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { - transformation.cursorIndex = 0; - } - } - - return vimState; - } - - let timesToRepeat = this.runsOnceForEachCountPrefix ? vimState.recordedState.count || 1 : 1; - let resultingCursors: Range[] = []; - let i = 0; - - const cursorsToIterateOver = vimState.allCursors - .map(x => new Range(x.start, x.stop)) - .sort((a, b) => a.start.line > b.start.line || (a.start.line === b.start.line && a.start.character > b.start.character) ? 1 : -1); - - for (const { start, stop } of cursorsToIterateOver) { - this.multicursorIndex = i++; - - vimState.cursorPosition = stop; - vimState.cursorStartPosition = start; - - for (let j = 0; j < timesToRepeat; j++) { - vimState = await this.exec(stop, vimState); - } - - resultingCursors.push(new Range( - vimState.cursorStartPosition, - vimState.cursorPosition, - )); - - for (const transformation of vimState.recordedState.transformations) { - if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { - transformation.cursorIndex = this.multicursorIndex; - } - } - } - - vimState.allCursors = resultingCursors; - - return vimState; - } -} - -// begin actions - -@RegisterAction -export class CommandNumber extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = [""]; - isCompleteAction = false; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - const number = parseInt(this.keysPressed[0], 10); - - vimState.recordedState.count = vimState.recordedState.count * 10 + number; - - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const isZero = keysPressed[0] === "0"; - - return super.doesActionApply(vimState, keysPressed) && - ((isZero && vimState.recordedState.count > 0) || !isZero); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const isZero = keysPressed[0] === "0"; - - return super.couldActionApply(vimState, keysPressed) && - ((isZero && vimState.recordedState.count > 0) || !isZero); - } -} - -@RegisterAction -export class CommandRegister extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["\"", ""]; - isCompleteAction = false; - - public async exec(position: Position, vimState: VimState): Promise { - const register = this.keysPressed[1]; - vimState.recordedState.registerName = register; - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return super.doesActionApply(vimState, keysPressed) && Register.isValidRegister(register); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return super.couldActionApply(vimState, keysPressed) && Register.isValidRegister(register); - } -} - -@RegisterAction -class CommandInsertRegisterContentInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = ["", ""]; - isCompleteAction = false; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.recordedState.registerName = this.keysPressed[1]; - const register = await Register.get(vimState); - let text: string; - - if (register.text instanceof Array) { - text = (register.text as string []).join("\n"); - } else if (register.text instanceof RecordedState) { - let keyStrokes: string[] = []; - - for (let action of register.text.actionsRun) { - keyStrokes = keyStrokes.concat(action.keysPressed); - } - - text = keyStrokes.join("\n"); - } else { - text = register.text; - } - - if (register.registerMode === RegisterMode.LineWise) { - text += "\n"; - } - - const searchState = vimState.globalState.searchState!; - searchState.searchString += text; - return vimState; - } -} - -@RegisterAction -class CommandRecordMacro extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["q", ""]; - - public async exec(position: Position, vimState: VimState): Promise { - const register = this.keysPressed[1]; - vimState.recordedMacro = new RecordedState(); - vimState.recordedMacro.registerName = register.toLocaleLowerCase(); - - if (!/^[A-Z]+$/.test(register) || !Register.has(register)) { - // If register name is upper case, it means we are appending commands to existing register instead of overriding. - let newRegister = new RecordedState(); - newRegister.registerName = register; - Register.putByKey(newRegister, register); - } - - vimState.isRecordingMacro = true; - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = this.keysPressed[1]; - - return super.doesActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = this.keysPressed[1]; - - return super.couldActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register); - } -} - -@RegisterAction -export class CommandQuitRecordMacro extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["q"]; - - public async exec(position: Position, vimState: VimState): Promise { - let existingMacro = (await Register.getByKey(vimState.recordedMacro.registerName)).text as RecordedState; - existingMacro.actionsRun = existingMacro.actionsRun.concat(vimState.recordedMacro.actionsRun); - vimState.isRecordingMacro = false; - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && vimState.isRecordingMacro; - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.couldActionApply(vimState, keysPressed) && vimState.isRecordingMacro; - } -} - -@RegisterAction -class CommandExecuteMacro extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["@", ""]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const register = this.keysPressed[1]; - vimState.recordedState.transformations.push({ - type: "macro", - register: register, - replay: "contentChange" - }); - - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return super.doesActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return super.couldActionApply(vimState, keysPressed) && Register.isValidRegisterForMacro(register); - } -} - -@RegisterAction -class CommandExecuteLastMacro extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["@", "@"]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - let lastInvokedMacro = vimState.historyTracker.lastInvokedMacro; - - if (lastInvokedMacro) { - vimState.recordedState.transformations.push({ - type: "macro", - register: lastInvokedMacro.registerName, - replay: "contentChange" - }); - } - - return vimState; - } -} - -@RegisterAction -class CommandEsc extends BaseCommand { - modes = [ - ModeName.Visual, - ModeName.VisualLine, - ModeName.VisualBlock, - ModeName.Normal, - ModeName.SearchInProgressMode, - ModeName.SurroundInputMode, - ModeName.EasyMotionMode - ]; - keys = [ - [""], - [""], - [""], - ]; - - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - if (vimState.currentMode === ModeName.Normal && !vimState.isMultiCursor) { - // If there's nothing to do on the vim side, we might as well call some - // of vscode's default "close notification" actions. I think we should - // just add to this list as needed. - await vscode.commands.executeCommand('closeReferenceSearchEditor'); - return vimState; - } - - if (vimState.currentMode !== ModeName.Visual && - vimState.currentMode !== ModeName.VisualLine && - vimState.currentMode !== ModeName.EasyMotionMode) { - - // Normally, you don't have to iterate over all cursors, - // as that is handled for you by the state machine. ESC is - // a special case since runsOnceForEveryCursor is false. - - vimState.allCursors = vimState.allCursors.map(x => x.withNewStop(x.stop.getLeft())); - } - - if (vimState.currentMode === ModeName.SearchInProgressMode) { - if (vimState.globalState.searchState) { - vimState.cursorPosition = vimState.globalState.searchState.searchCursorStartPosition; - } - } - - if (vimState.currentMode === ModeName.Normal && vimState.isMultiCursor) { - vimState.isMultiCursor = false; - } - - if (vimState.currentMode === ModeName.EasyMotionMode) { - // Escape or other termination keys were pressed, exit mode - vimState.easyMotion.clearDecorations(); - vimState.currentMode = ModeName.Normal; - } - - // Abort surround operation - if (vimState.currentMode === ModeName.SurroundInputMode) { - vimState.surround = undefined; - } - - vimState.currentMode = ModeName.Normal; - - if (!vimState.isMultiCursor) { - vimState.allCursors = [ vimState.allCursors[0] ]; - } - - return vimState; - } -} - -@RegisterAction -class CommandEscReplaceMode extends BaseCommand { - modes = [ModeName.Replace]; - keys = [ - [""], - [""], - ]; - - public async exec(position: Position, vimState: VimState): Promise { - const timesToRepeat = vimState.replaceState!.timesToRepeat; - let textToAdd = ""; - - for (let i = 1; i < timesToRepeat; i++) { - textToAdd += vimState.replaceState!.newChars.join(""); - } - - vimState.recordedState.transformations.push({ - type : "insertText", - text : textToAdd, - position: position, - diff : new PositionDiff(0, -1), - }); - - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class CommandCtrlOpenBracket extends CommandEsc { - modes = [ - ModeName.Insert, - ModeName.Visual, - ModeName.VisualLine, - ModeName.VisualBlock, - ModeName.Replace - ]; - keys = [[""]]; -} - -abstract class CommandEditorScroll extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - runsOnceForEachCountPrefix = false; - keys: string[]; - to: EditorScrollDirection; - by: EditorScrollByUnit; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - - vimState.postponedCodeViewChanges.push({ - command: "editorScroll", - args: { - to: this.to, - by: this.by, - value: timesToRepeat, - revealCursor: true, - select: [ModeName.Visual, ModeName.VisualBlock, ModeName.VisualLine].indexOf(vimState.currentMode) >= 0 - } - }); - return vimState; - } -} - -@RegisterAction -class CommandCtrlE extends CommandEditorScroll { - keys = [""]; - to: EditorScrollDirection = "down"; - by: EditorScrollByUnit = "line"; -} - -@RegisterAction -class CommandCtrlY extends CommandEditorScroll { - keys = [""]; - to: EditorScrollDirection = "up"; - by: EditorScrollByUnit = "line"; -} - -@RegisterAction -class CommandMoveFullPageUp extends CommandEditorScroll { - keys = [""]; - to: EditorScrollDirection = "up"; - by: EditorScrollByUnit = "page"; -} - -@RegisterAction -class CommandMoveFullPageDown extends CommandEditorScroll { - keys = [""]; - to: EditorScrollDirection = "down"; - by: EditorScrollByUnit = "page"; -} - -@RegisterAction -class CommandMoveHalfPageDown extends CommandEditorScroll { - keys = [""]; - to: EditorScrollDirection = "down"; - by: EditorScrollByUnit = "halfPage"; -} - -@RegisterAction -class CommandMoveHalfPageUp extends CommandEditorScroll { - keys = [""]; - to: EditorScrollDirection = "up"; - by: EditorScrollByUnit = "halfPage"; -} - -@RegisterAction -export class CommandInsertAtCursor extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["i"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - // Only allow this command to be prefixed with a count or nothing, no other - // actions or operators before - let previousActionsNumbers = true; - for (const prevAction of vimState.recordedState.actionsRun) { - if (!(prevAction instanceof CommandNumber)) { - previousActionsNumbers = false; - break; - } - } - - if (vimState.recordedState.actionsRun.length === 0 || previousActionsNumbers) { - return super.couldActionApply(vimState, keysPressed); - } - return false; - } -} - -@RegisterAction -class CommandReplaceAtCursor extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["R"]; - runsOnceForEachCountPrefix = false; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - - vimState.currentMode = ModeName.Replace; - vimState.replaceState = new ReplaceState(position, timesToRepeat); - - return vimState; - } -} - -@RegisterAction -class CommandReplaceInReplaceMode extends BaseCommand { - modes = [ModeName.Replace]; - keys = [""]; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const char = this.keysPressed[0]; - const replaceState = vimState.replaceState!; - - if (char === "") { - if (position.isBeforeOrEqual(replaceState.replaceCursorStartPosition)) { - // If you backspace before the beginning of where you started to replace, - // just move the cursor back. - - vimState.cursorPosition = position.getLeft(); - vimState.cursorStartPosition = position.getLeft(); - } else if (position.line > replaceState.replaceCursorStartPosition.line || - position.character > replaceState.originalChars.length) { - - vimState.recordedState.transformations.push({ - type: "deleteText", - position: position, - }); - } else { - vimState.recordedState.transformations.push({ - type: "replaceText", - text: replaceState.originalChars[position.character - 1], - start: position.getLeft(), - end: position, - diff: new PositionDiff(0, -1), - }); - } - - replaceState.newChars.pop(); - } else { - if (!position.isLineEnd() && char !== "\n") { - vimState.recordedState.transformations.push({ - type: "replaceText", - text: char, - start: position, - end: position.getRight(), - diff: new PositionDiff(0, 1), - }); - } else { - vimState.recordedState.transformations.push({ - type: "insertText", - text: char, - position: position, - }); - } - - replaceState.newChars.push(char); - } - - vimState.currentMode = ModeName.Replace; - return vimState; - } -} - -@RegisterAction -class CommandInsertInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = [[""], - [""], - [""]]; - runsOnceForEveryCursor() { return this.keysPressed[0] === '\n'; } - - public async exec(position: Position, vimState: VimState): Promise { - const key = this.keysPressed[0]; - const searchState = vimState.globalState.searchState!; - const prevSearchList = vimState.globalState.searchStatePrevious!; - - // handle special keys first - if (key === "" || key === "") { - searchState.searchString = searchState.searchString.slice(0, -1); - } else if (key === "\n") { - vimState.currentMode = vimState.globalState.searchState!.previousMode; - - // Repeat the previous search if no new string is entered - if (searchState.searchString === "") { - if (prevSearchList.length > 0) { - searchState.searchString = prevSearchList[prevSearchList.length - 1].searchString; - } - } - - // Store this search if different than previous - if (vimState.globalState.searchStatePrevious.length !== 0) { - let previousSearchState = vimState.globalState.searchStatePrevious; - if (searchState.searchString !== previousSearchState[previousSearchState.length - 1]!.searchString) { - previousSearchState.push(searchState); - } - } else { - vimState.globalState.searchStatePrevious.push(searchState); - } - - // Make sure search history does not exceed configuration option - if (vimState.globalState.searchStatePrevious.length > Configuration.history) { - vimState.globalState.searchStatePrevious.splice(0, 1); - } - - // Update the index to the end of the search history - vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length - 1; - - // Move cursor to next match - vimState.cursorPosition = searchState.getNextSearchMatchPosition(vimState.cursorPosition).pos; - - return vimState; - } else if (key === "") { - vimState.globalState.searchStateIndex -= 1; - - // Clamp the history index to stay within bounds of search history length - vimState.globalState.searchStateIndex = Math.max(vimState.globalState.searchStateIndex, 0); - - if (prevSearchList[vimState.globalState.searchStateIndex] !== undefined) { - searchState.searchString = prevSearchList[vimState.globalState.searchStateIndex].searchString; - } - } else if (key === "") { - vimState.globalState.searchStateIndex += 1; - - // If past the first history item, allow user to enter their own search string (not using history) - if (vimState.globalState.searchStateIndex > vimState.globalState.searchStatePrevious.length - 1) { - searchState.searchString = ""; - vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length; - return vimState; - } - - if (prevSearchList[vimState.globalState.searchStateIndex] !== undefined) { - searchState.searchString = prevSearchList[vimState.globalState.searchStateIndex].searchString; - } - } else { - searchState.searchString += this.keysPressed[0]; - } - - return vimState; - } -} - -@RegisterAction -class CommandEscInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = [""]; - runsOnceForEveryCursor() { return this.keysPressed[0] === '\n'; } - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Normal; - vimState.globalState.searchState = undefined; - - return vimState; - } -} - -@RegisterAction -class CommandCtrlVInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = [""]; - runsOnceForEveryCursor() { return this.keysPressed[0] === '\n'; } - - public async exec(position: Position, vimState: VimState): Promise { - const searchState = vimState.globalState.searchState!; - const textFromClipboard = util.clipboardPaste(); - - searchState.searchString += textFromClipboard; - return vimState; - } -} - -@RegisterAction -class CommandCmdVInSearchMode extends BaseCommand { - modes = [ModeName.SearchInProgressMode]; - keys = [""]; - runsOnceForEveryCursor() { return this.keysPressed[0] === '\n'; } - - public async exec(position: Position, vimState: VimState): Promise { - const searchState = vimState.globalState.searchState!; - const textFromClipboard = util.clipboardPaste(); - - searchState.searchString += textFromClipboard; - return vimState; - } -} - -/** - * Our Vim implementation selects up to but not including the final character - * of a visual selection, instead opting to render a block cursor on the final - * character. This works for everything except VSCode's native copy command, - * which loses the final character because it's not selected. We override that - * copy command here by default to include the final character. - */ -@RegisterAction -class CommandOverrideCopy extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock, ModeName.Insert, ModeName.Normal]; - keys = [""]; // A special key - see ModeHandler - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - let text = ""; - - if (vimState.currentMode === ModeName.Visual || - vimState.currentMode === ModeName.Normal || - vimState.currentMode === ModeName.Insert) { - text = vimState.allCursors.map(range => { - const start = Position.EarlierOf(range.start, range.stop); - const stop = Position.LaterOf(range.start, range.stop); - - // Insert selecting from the left to the right is a special case that - // selects one more on the right. - // The order of the if statements is to check for the case where start=stop - if (vimState.currentMode !== ModeName.Insert || start.isEqual(range.stop)) { - return vimState.editor.document.getText(new vscode.Range(start, stop.getRight())); - } else { - return vimState.editor.document.getText(new vscode.Range(start, stop)); - } - }).join("\n"); - } else if (vimState.currentMode === ModeName.VisualLine) { - text = vimState.allCursors.map(range => { - return vimState.editor.document.getText(new vscode.Range( - Position.EarlierOf(range.start.getLineBegin(), range.stop.getLineBegin()), - Position.LaterOf(range.start.getLineEnd(), range.stop.getLineEnd()) - )); - }).join("\n"); - } else if (vimState.currentMode === ModeName.VisualBlock) { - for ( const { line } of Position.IterateLine(vimState)) { - text += line + '\n'; - } - } - - util.clipboardCopy(text); - - return vimState; - } -} - -@RegisterAction -class CommandCmdA extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.cursorStartPosition = new Position(0, vimState.desiredColumn); - vimState.cursorPosition = new Position(TextEditor.getLineCount() - 1, vimState.desiredColumn); - vimState.currentMode = ModeName.VisualLine; - - return vimState; - } -} - -function searchCurrentWord(position: Position, vimState: VimState, direction: SearchDirection, isExact: boolean) { - const currentWord = TextEditor.getWord(position); - - // If the search is going left then use `getWordLeft()` on position to start - // at the beginning of the word. This ensures that any matches happen - // outside of the currently selected word. - const searchStartCursorPosition = direction === SearchDirection.Backward - ? vimState.cursorPosition.getWordLeft(true) - : vimState.cursorPosition; - - return createSearchStateAndMoveToMatch({ - needle: currentWord, - vimState, - direction, - isExact, - searchStartCursorPosition - }); -} - -function searchCurrentSelection (vimState: VimState, direction: SearchDirection) { - const selection = TextEditor.getSelection(); - const end = new Position(selection.end.line, selection.end.character); - const currentSelection = TextEditor.getText(selection.with(selection.start, end)); - - // Go back to Normal mode, otherwise the selection grows to the next match. - vimState.currentMode = ModeName.Normal; - - // If the search is going left then use `getLeft()` on the selection start. - // If going right then use `getRight()` on the selection end. This ensures - // that any matches happen outside of the currently selected word. - const searchStartCursorPosition = direction === SearchDirection.Backward - ? vimState.lastVisualSelectionStart.getLeft() - : vimState.lastVisualSelectionEnd.getRight(); - - return createSearchStateAndMoveToMatch({ - needle: currentSelection, - vimState, - direction, - isExact: false, - searchStartCursorPosition - }); -} - -function createSearchStateAndMoveToMatch(args: { - needle?: string | undefined, - vimState: VimState, - direction: SearchDirection, - isExact: boolean, - searchStartCursorPosition: Position -}) { - const { - needle, - vimState, - isExact - } = args; - - if (needle === undefined || needle.length === 0) { - return vimState; - } - - const searchString = isExact - ? `\\b${needle}\\b` - : needle; - - // Start a search for the given term. - vimState.globalState.searchState = new SearchState( - args.direction, vimState.cursorPosition, searchString, { isRegex: isExact }, vimState.currentMode - ); - - vimState.cursorPosition = vimState.globalState.searchState.getNextSearchMatchPosition(args.searchStartCursorPosition).pos; - - // Turn one of the highlighting flags back on (turned off with :nohl) - vimState.globalState.hl = true; - - return vimState; -} - -@RegisterAction -class CommandSearchCurrentWordExactForward extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["*"]; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentWord(position, vimState, SearchDirection.Forward, true); - } -} - -@RegisterAction -class CommandSearchCurrentWordForward extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["g", "*"]; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentWord(position, vimState, SearchDirection.Forward, false); - } -} - -@RegisterAction -class CommandSearchVisualForward extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ["*"]; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentSelection(vimState, SearchDirection.Forward); - } -} - -@RegisterAction -class CommandSearchCurrentWordExactBackward extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["#"]; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentWord(position, vimState, SearchDirection.Backward, true); - } -} - -@RegisterAction -class CommandSearchCurrentWordBackward extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["g", "#"]; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentWord(position, vimState, SearchDirection.Backward, false); - } -} - -@RegisterAction -class CommandSearchVisualBackward extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ["#"]; - isMotion = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - return searchCurrentSelection(vimState, SearchDirection.Backward); - } -} - -@RegisterAction -export class CommandSearchForwards extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["/"]; - isMotion = true; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.globalState.searchState = new SearchState(SearchDirection.Forward, - vimState.cursorPosition, "", { isRegex: true }, vimState.currentMode); - vimState.currentMode = ModeName.SearchInProgressMode; - - // Reset search history index - vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length; - - vimState.globalState.hl = true; - - return vimState; - } -} - -@RegisterAction -export class CommandSearchBackwards extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["?"]; - isMotion = true; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.globalState.searchState = new SearchState(SearchDirection.Backward, - vimState.cursorPosition, "", { isRegex: true }, vimState.currentMode); - vimState.currentMode = ModeName.SearchInProgressMode; - - // Reset search history index - vimState.globalState.searchStateIndex = vimState.globalState.searchStatePrevious.length; - - vimState.globalState.hl = true; - - return vimState; - } -} - - -@RegisterAction -export class MarkCommand extends BaseCommand { - keys = ["m", ""]; - modes = [ModeName.Normal]; - - public async exec(position: Position, vimState: VimState): Promise { - const markName = this.keysPressed[1]; - - vimState.historyTracker.addMark(position, markName); - - return vimState; - } -} - -@RegisterAction -export class PutCommand extends BaseCommand { - keys = ["p"]; - modes = [ModeName.Normal]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - constructor(multicursorIndex?: number) { - super(); - this.multicursorIndex = multicursorIndex; - } - public static async GetText(vimState: VimState, multicursorIndex: number | undefined = undefined): Promise { - const register = await Register.get(vimState); - - if (vimState.isMultiCursor) { - if (multicursorIndex === undefined) { - console.log("ERROR: no multi cursor index when calling PutCommand#getText"); - - throw new Error("Bad!"); - } - - if (vimState.isMultiCursor && typeof register.text === "object") { - return register.text[multicursorIndex]; - } - } - - return register.text as string; - } - - public async exec(position: Position, vimState: VimState, after: boolean = false, adjustIndent: boolean = false): Promise { - const register = await Register.get(vimState); - const dest = after ? position : position.getRight(); - - if (register.text instanceof RecordedState) { - /** - * Paste content from recordedState. This one is actually complex as - * Vim has internal key code for key strokes.For example, Backspace - * is stored as `<80>kb`. So if you replay a macro, which is stored - * in a register as `a1<80>kb2`, youshall just get `2` inserted as - * `a` represents entering Insert Mode, `<80>bk` represents - * Backspace. However here, we shall - * insert the plain text content of the register, which is `a1<80>kb2`. - */ - vimState.recordedState.transformations.push({ - type: "macro", - register: vimState.recordedState.registerName, - replay: "keystrokes" - }); - return vimState; - } else if (typeof register.text === "object" && vimState.currentMode === ModeName.VisualBlock) { - return await this.execVisualBlockPaste(register.text, position, vimState, after); - } - - let text = await PutCommand.GetText(vimState, this.multicursorIndex); - - let textToAdd: string; - let whereToAddText: Position; - let diff = new PositionDiff(0, 0); - - if (register.registerMode === RegisterMode.CharacterWise) { - textToAdd = text; - whereToAddText = dest; - } else if (vimState.currentMode === ModeName.Visual && - register.registerMode === RegisterMode.LineWise) { - // in the specific case of linewise register data during visual mode, - // we need extra newline feeds - textToAdd = "\n" + text + "\n"; - whereToAddText = dest; - } else { - if (adjustIndent) { - // Adjust indent to current line - let indentationWidth = TextEditor.getIndentationLevel(TextEditor.getLineAt(position).text); - let firstLineIdentationWidth = TextEditor.getIndentationLevel(text.split('\n')[0]); - - text = text.split('\n').map(line => { - let currentIdentationWidth = TextEditor.getIndentationLevel(line); - let newIndentationWidth = currentIdentationWidth - firstLineIdentationWidth + indentationWidth; - - return TextEditor.setIndentationLevel(line, newIndentationWidth); - }).join('\n'); - } - - if (after) { - // P insert before current line - textToAdd = text + "\n"; - whereToAddText = dest.getLineBegin(); - } else { - // p paste after current line - textToAdd = "\n" + text; - whereToAddText = dest.getLineEnd(); - } - } - - // More vim weirdness: If the thing you're pasting has a newline, the cursor - // stays in the same place. Otherwise, it moves to the end of what you pasted. - - const numNewlines = text.split("\n").length - 1; - const currentLineLength = TextEditor.getLineAt(position).text.length; - - if (register.registerMode === RegisterMode.LineWise) { - const check = text.match(/^\s*/); - let numWhitespace = 0; - - if (check) { - numWhitespace = check[0].length; - } - - if (after) { - diff = PositionDiff.NewBOLDiff(-numNewlines - 1, numWhitespace); - } else { - diff = PositionDiff.NewBOLDiff(currentLineLength > 0 ? 1 : -numNewlines, numWhitespace); - } - } else { - if (text.indexOf("\n") === -1) { - if (!position.isLineEnd()) { - if (after) { - diff = new PositionDiff(0, -1); - } else { - diff = new PositionDiff(0, textToAdd.length); - } - } - } else { - if (position.isLineEnd()) { - diff = PositionDiff.NewBOLDiff(-numNewlines, position.character); - } else { - if (after) { - diff = PositionDiff.NewBOLDiff(-numNewlines, position.character); - } else { - diff = new PositionDiff(0, 1); - } - } - } - } - - vimState.recordedState.transformations.push({ - type : "insertText", - text : textToAdd, - position: whereToAddText, - diff : diff, - }); - - vimState.currentRegisterMode = register.registerMode; - return vimState; - } - - private async execVisualBlockPaste(block: string[], position: Position, vimState: VimState, after: boolean): Promise { - if (after) { - position = position.getRight(); - } - - // Add empty lines at the end of the document, if necessary. - let linesToAdd = Math.max(0, block.length - (TextEditor.getLineCount() - position.line) + 1); - - if (linesToAdd > 0) { - await TextEditor.insertAt(Array(linesToAdd).join("\n"), - new Position( - TextEditor.getLineCount() - 1, - TextEditor.getLineAt(new Position(TextEditor.getLineCount() - 1, 0)).text.length - ) - ); - } - - // paste the entire block. - for (let lineIndex = position.line; lineIndex < position.line + block.length; lineIndex++) { - const line = block[lineIndex - position.line]; - const insertPos = new Position( - lineIndex, - Math.min(position.character, TextEditor.getLineAt(new Position(lineIndex, 0)).text.length) - ); - - await TextEditor.insertAt(line, insertPos); - } - - vimState.currentRegisterMode = RegisterMode.FigureItOutFromCurrentMode; - return vimState; - } - - public async execCount(position: Position, vimState: VimState): Promise { - const result = await super.execCount(position, vimState); - - if (vimState.effectiveRegisterMode() === RegisterMode.LineWise && - vimState.recordedState.count > 0) { - const numNewlines = (await PutCommand.GetText(vimState, this.multicursorIndex)).split("\n").length * vimState.recordedState.count; - - result.recordedState.transformations.push({ - type : "moveCursor", - diff : new PositionDiff(-numNewlines + 1, 0), - cursorIndex: this.multicursorIndex - }); - } - - return result; - } -} - -@RegisterAction -export class GPutCommand extends BaseCommand { - keys = ["g", "p"]; - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const result = await new PutCommand().exec(position, vimState); - - return result; - } - - public async execCount(position: Position, vimState: VimState): Promise { - const register = await Register.get(vimState); - let addedLinesCount: number; - - if (register.text instanceof RecordedState) { - vimState.recordedState.transformations.push({ - type: "macro", - register: vimState.recordedState.registerName, - replay: "keystrokes" - }); - - return vimState; - } - if (typeof register.text === "object") { // visual block mode - addedLinesCount = register.text.length * vimState.recordedState.count; - } else { - addedLinesCount = register.text.split('\n').length; - } - - const result = await super.execCount(position, vimState); - - if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { - const line = TextEditor.getLineAt(position).text; - const addAnotherLine = line.length > 0 && addedLinesCount > 1; - - result.recordedState.transformations.push({ - type : "moveCursor", - diff : PositionDiff.NewBOLDiff(1 + (addAnotherLine ? 1 : 0), 0), - cursorIndex: this.multicursorIndex - }); - } - - return result; - } -} - -@RegisterAction -export class PutWithIndentCommand extends BaseCommand { - keys = ["]", "p"]; - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const result = await new PutCommand().exec(position, vimState, false, true); - return result; - } - - public async execCount(position: Position, vimState: VimState): Promise { - return await super.execCount(position, vimState); - } -} - -@RegisterAction -export class PutCommandVisual extends BaseCommand { - keys = [ - ["p"], - ["P"], - ]; - modes = [ModeName.Visual, ModeName.VisualLine]; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState, after: boolean = false): Promise { - let start = vimState.cursorStartPosition; - let end = vimState.cursorPosition; - const isLineWise = vimState.currentMode === ModeName.VisualLine; - if (start.isAfter(end)) { - [start, end] = [end, start]; - } - - // If the to be inserted text is linewise we have a seperate logic delete the - // selection first than insert - let register = await Register.get(vimState); - if (register.registerMode === RegisterMode.LineWise) { - let result = await new operator.DeleteOperator(this.multicursorIndex).run(vimState, start, end, false); - // to ensure, that the put command nows this is - // an linewise register insertion in visual mode - let oldMode = result.currentMode; - result.currentMode = ModeName.Visual; - result = await new PutCommand().exec(start, result, true); - result.currentMode = oldMode; - return result; - } - - // The reason we need to handle Delete and Yank separately is because of - // linewise mode. If we're in visualLine mode, then we want to copy - // linewise but not necessarily delete linewise. - let result = await new PutCommand(this.multicursorIndex).exec(start, vimState, true); - result.currentRegisterMode = isLineWise ? RegisterMode.LineWise : RegisterMode.CharacterWise; - result.recordedState.registerName = Configuration.useSystemClipboard ? '*' : '"'; - result = await new operator.YankOperator(this.multicursorIndex).run(result, start, end); - result.currentRegisterMode = RegisterMode.CharacterWise; - result = await new operator.DeleteOperator(this.multicursorIndex).run(result, start, end.getLeftIfEOL(), false); - result.currentRegisterMode = RegisterMode.FigureItOutFromCurrentMode; - return result; - } - - // TODO - execWithCount -} - -@RegisterAction -export class PutBeforeCommand extends BaseCommand { - public keys = ["P"]; - public modes = [ModeName.Normal]; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - const command = new PutCommand(); - command.multicursorIndex = this.multicursorIndex; - - const result = await command.exec(position, vimState, true); - - return result; - } -} - -@RegisterAction -export class GPutBeforeCommand extends BaseCommand { - keys = ["g", "P"]; - modes = [ModeName.Normal]; - - public async exec(position: Position, vimState: VimState): Promise { - const result = await new PutCommand().exec(position, vimState, true); - const register = await Register.get(vimState); - let addedLinesCount: number; - - if (register.text instanceof RecordedState) { - vimState.recordedState.transformations.push({ - type: "macro", - register: vimState.recordedState.registerName, - replay: "keystrokes" - }); - - return vimState; - } else if (typeof register.text === "object") { // visual block mode - addedLinesCount = register.text.length * vimState.recordedState.count; - } else { - addedLinesCount = register.text.split('\n').length; - } - - if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { - const line = TextEditor.getLineAt(position).text; - const addAnotherLine = line.length > 0 && addedLinesCount > 1; - - result.recordedState.transformations.push({ - type : "moveCursor", - diff : PositionDiff.NewBOLDiff(1 + (addAnotherLine ? 1 : 0), 0), - cursorIndex: this.multicursorIndex - }); - } - - return result; - } -} - -@RegisterAction -export class PutBeforeWithIndentCommand extends BaseCommand { - keys = ["[", "p"]; - modes = [ModeName.Normal]; - - public async exec(position: Position, vimState: VimState): Promise { - const result = await new PutCommand().exec(position, vimState, true, true); - - if (vimState.effectiveRegisterMode() === RegisterMode.LineWise) { - result.cursorPosition = result.cursorPosition.getPreviousLineBegin().getFirstLineNonBlankChar(); - } - - return result; - } -} - -@RegisterAction -class CommandShowCommandLine extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = [":"]; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - vimState.recordedState.transformations.push({ - type: "showCommandLine" - }); - - if (vimState.currentMode === ModeName.Normal) { - vimState.commandInitialText = ""; - } else { - vimState.commandInitialText = "'<,'>"; - } - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class CommandDot extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["."]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.recordedState.transformations.push({ - type: "dot" - }); - - return vimState; - } -} - -abstract class CommandFold extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - commandName: string; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand(this.commandName); - vimState.currentMode = ModeName.Normal; - return vimState; - } -} - -@RegisterAction -class CommandCloseFold extends CommandFold { - keys = ["z", "c"]; - commandName = "editor.fold"; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - await vscode.commands.executeCommand("editor.fold", {levels: timesToRepeat, direction: "up"}); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - return vimState; - } -} - -@RegisterAction -class CommandCloseAllFolds extends CommandFold { - keys = ["z", "M"]; - commandName = "editor.foldAll"; -} - -@RegisterAction -class CommandOpenFold extends CommandFold { - keys = ["z", "o"]; - commandName = "editor.unfold"; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - await vscode.commands.executeCommand("editor.unfold", {levels: timesToRepeat, direction: "up"}); - - return vimState; - } -} - -@RegisterAction -class CommandOpenAllFolds extends CommandFold { - keys = ["z", "R"]; - commandName = "editor.unfoldAll"; -} - -@RegisterAction -class CommandCloseAllFoldsRecursively extends CommandFold { - modes = [ModeName.Normal]; - keys = ["z", "C"]; - commandName = "editor.foldRecursively"; -} - -@RegisterAction -class CommandOpenAllFoldsRecursively extends CommandFold { - modes = [ModeName.Normal]; - keys = ["z", "O"]; - commandName = "editor.unFoldRecursively"; -} - -@RegisterAction -class CommandCenterScroll extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["z", "z"]; - - public async exec(position: Position, vimState: VimState): Promise { - // In these modes you want to center on the cursor position - vimState.editor.revealRange( - new vscode.Range(vimState.cursorPosition, - vimState.cursorPosition), - vscode.TextEditorRevealType.InCenter); - - return vimState; - } -} - -@RegisterAction -class CommandCenterScrollFirstChar extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["z", "."]; - - public async exec(position: Position, vimState: VimState): Promise { - // In these modes you want to center on the cursor position - // This particular one moves cursor to first non blank char though - vimState.editor.revealRange( - new vscode.Range(vimState.cursorPosition, vimState.cursorPosition), - vscode.TextEditorRevealType.InCenter); - - // Move cursor to first char of line - vimState.cursorPosition = vimState.cursorPosition.getFirstLineNonBlankChar(); - - return vimState; - } -} - -@RegisterAction -class CommandTopScroll extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["z", "t"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: "revealLine", - args: { - lineNumber: position.line, - at: "top" - } - }); - return vimState; - } -} - -@RegisterAction -class CommandTopScrollFirstChar extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["z", "\n"]; - - public async exec(position: Position, vimState: VimState): Promise { - // In these modes you want to center on the cursor position - // This particular one moves cursor to first non blank char though - vimState.postponedCodeViewChanges.push({ - command: "revealLine", - args: { - lineNumber: position.line, - at: "top" - } - }); - - // Move cursor to first char of line - vimState.cursorPosition = vimState.cursorPosition.getFirstLineNonBlankChar(); - - return vimState; - } -} - -@RegisterAction -class CommandBottomScroll extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["z", "b"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: "revealLine", - args: { - lineNumber: position.line, - at: "bottom" - } - }); - return vimState; - } -} - -@RegisterAction -class CommandBottomScrollFirstChar extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["z", "-"]; - - public async exec(position: Position, vimState: VimState): Promise { - // In these modes you want to center on the cursor position - // This particular one moves cursor to first non blank char though - vimState.postponedCodeViewChanges.push({ - command: "revealLine", - args: { - lineNumber: position.line, - at: "bottom" - } - }); - - // Move cursor to first char of line - vimState.cursorPosition = vimState.cursorPosition.getFirstLineNonBlankChar(); - - return vimState; - } -} - -@RegisterAction -class CommandGoToOtherEndOfHighlightedText extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ["o"]; - - public async exec(position: Position, vimState: VimState): Promise { - [vimState.cursorStartPosition, vimState.cursorPosition] = - [vimState.cursorPosition, vimState.cursorStartPosition]; - - return vimState; - } -} - -@RegisterAction -class CommandUndo extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["u"]; - runsOnceForEveryCursor() { return false; } - mustBeFirstKey = true; - - public async exec(position: Position, vimState: VimState): Promise { - const newPositions = await vimState.historyTracker.goBackHistoryStep(); - - if (newPositions !== undefined) { - vimState.allCursors = newPositions.map(x => new Range(x, x)); - } - - vimState.alteredHistory = true; - return vimState; - } -} - -@RegisterAction -class CommandRedo extends BaseCommand { - modes = [ModeName.Normal]; - keys = [""]; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - const newPositions = await vimState.historyTracker.goForwardHistoryStep(); - - if (newPositions !== undefined) { - vimState.allCursors = newPositions.map(x => new Range(x, x)); - } - - vimState.alteredHistory = true; - return vimState; - } -} - -@RegisterAction -class CommandDeleteToLineEnd extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["D"]; - canBeRepeatedWithDot = true; - runsOnceForEveryCursor() { return true; } - - public async exec(position: Position, vimState: VimState): Promise { - if (position.isLineEnd()) { - return vimState; - } - - return await new operator.DeleteOperator(this.multicursorIndex).run(vimState, position, position.getLineEnd().getLeft()); - } -} - -@RegisterAction -export class CommandYankFullLine extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["Y"]; - - public async exec(position: Position, vimState: VimState): Promise { - const linesDown = (vimState.recordedState.count || 1) - 1; - const start = position.getLineBegin(); - const end = new Position(position.line + linesDown, 0).getLineEnd().getLeft(); - - vimState.currentRegisterMode = RegisterMode.LineWise; - - return await new operator.YankOperator().run(vimState, start, end); - } -} - -@RegisterAction -class CommandChangeToLineEnd extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["C"]; - runsOnceForEachCountPrefix = false; - mustBeFirstKey = true; - - public async exec(position: Position, vimState: VimState): Promise { - let count = vimState.recordedState.count || 1; - - return new operator.ChangeOperator().run(vimState, position, position.getDownByCount(Math.max(0, count - 1)).getLineEnd().getLeft()); - } -} - -@RegisterAction -class CommandClearLine extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["S"]; - runsOnceForEachCountPrefix = false; - - public async exec(position: Position, vimState: VimState): Promise { - let count = vimState.recordedState.count || 1; - let end = position.getDownByCount(Math.max(0, count - 1)).getLineEnd().getLeft(); - return new operator.ChangeOperator().run(vimState, position.getLineBeginRespectingIndent(), end); - } -} - -@RegisterAction -class CommandExitVisualMode extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ["v"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class CommandVisualMode extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["v"]; - isCompleteAction = false; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Visual; - - return vimState; - } -} - -@RegisterAction -class CommandReselectVisual extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["g", "v"]; - - public async exec(position: Position, vimState: VimState): Promise { - // Try to restore selection only if valid - if (vimState.lastVisualSelectionEnd !== undefined && - vimState.lastVisualSelectionStart !== undefined && - vimState.lastVisualMode !== undefined) { - - if (vimState.lastVisualSelectionEnd.line <= (TextEditor.getLineCount() - 1)) { - vimState.currentMode = vimState.lastVisualMode; - vimState.cursorStartPosition = vimState.lastVisualSelectionStart; - vimState.cursorPosition = vimState.lastVisualSelectionEnd.getLeft(); - } - - } - return vimState; - } -} - -@RegisterAction -class CommandVisualBlockMode extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - if (vimState.currentMode === ModeName.VisualBlock) { - vimState.currentMode = ModeName.Normal; - } else { - vimState.currentMode = ModeName.VisualBlock; - } - - return vimState; - } -} - -@RegisterAction -class CommandVisualLineMode extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ["V"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.VisualLine; - - return vimState; - } -} - -@RegisterAction -class CommandExitVisualLineMode extends BaseCommand { - modes = [ModeName.VisualLine]; - keys = ["V"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class CommandOpenFile extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ["g", "f"]; - - public async exec(position: Position, vimState: VimState): Promise { - - let fullFilePath: string = ""; - - if (vimState.currentMode === ModeName.Visual) { - const selection = TextEditor.getSelection(); - const end = new Position(selection.end.line, selection.end.character + 1); - fullFilePath = TextEditor.getText(selection.with(selection.start, end)); - } else { - const start = position.getFilePathLeft(true); - const end = position.getFilePathRight(); - const range = new vscode.Range(start, end); - - fullFilePath = TextEditor.getText(range).trim(); - } - const fileInfo = fullFilePath.match(/(.*?(?=:[0-9]+)|.*):?([0-9]*)$/); - if (fileInfo) { - const filePath = fileInfo[1]; - const lineNumber = parseInt(fileInfo[2], 10); - const fileCommand = new FileCommand({name: filePath, lineNumber: lineNumber}); - fileCommand.execute(); - } - - return vimState; - } -} - -@RegisterAction -class CommandGoToDefinition extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["g", "d"]; - - public async exec(position: Position, vimState: VimState): Promise { - const oldActiveEditor = vimState.editor; - - await vscode.commands.executeCommand("editor.action.goToDeclaration"); - - if (oldActiveEditor === vimState.editor) { - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - } - - return vimState; - } -} - -@RegisterAction -class CommandGoBackInChangelist extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["g", ";"]; - - public async exec(position: Position, vimState: VimState): Promise { - const originalIndex = vimState.historyTracker.changelistIndex; - const prevPos = vimState.historyTracker.getChangePositionAtindex(originalIndex); - - if (prevPos !== undefined) { - vimState.cursorPosition = prevPos[0]; - if (vimState.historyTracker.getChangePositionAtindex(originalIndex - 1) !== undefined) { - vimState.historyTracker.changelistIndex = originalIndex - 1; - } - } - - return vimState; - } -} - -@RegisterAction -class CommandGoForwardInChangelist extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["g", ","]; - - public async exec(position: Position, vimState: VimState): Promise { - let originalIndex = vimState.historyTracker.changelistIndex; - - // Preincrement if on boundary to prevent seeing the same index twice - if (originalIndex === 1) { - originalIndex += 1; - } - - const nextPos = vimState.historyTracker.getChangePositionAtindex(originalIndex); - - if (nextPos !== undefined) { - vimState.cursorPosition = nextPos[0]; - if (vimState.historyTracker.getChangePositionAtindex(originalIndex + 1) !== undefined) { - vimState.historyTracker.changelistIndex = originalIndex + 1; - } - } - - return vimState; - } -} - -@RegisterAction -export class CommandInsertAtFirstCharacter extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ["I"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.cursorPosition = position.getFirstLineNonBlankChar(); - - return vimState; - } -} - -@RegisterAction -class CommandInsertAtLineBegin extends BaseCommand { - modes = [ModeName.Normal]; - mustBeFirstKey = true; - keys = ["g", "I"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.cursorPosition = position.getLineBegin(); - - return vimState; - } -} - -@RegisterAction -export class CommandInsertAfterCursor extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["a"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.cursorPosition = position.getRight(); - - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - // Only allow this command to be prefixed with a count or nothing, no other - // actions or operators before - let previousActionsNumbers = true; - for (const prevAction of vimState.recordedState.actionsRun) { - if (!(prevAction instanceof CommandNumber)) { - previousActionsNumbers = false; - break; - } - } - - if (vimState.recordedState.actionsRun.length === 0 || previousActionsNumbers) { - return super.couldActionApply(vimState, keysPressed); - } - return false; - } -} - -@RegisterAction -export class CommandInsertAtLineEnd extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ["A"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.cursorPosition = position.getLineEnd(); - - return vimState; - } -} - -@RegisterAction -class CommandInsertNewLineAbove extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["O"]; - runsOnceForEveryCursor() { return false; } - - public async execCount(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - let count = vimState.recordedState.count || 1; - // Why do we do this? Who fucking knows??? If the cursor is at the - // beginning of the line, then editor.action.insertLineBefore does some - // weird things with following paste command. Refer to - // https://github.com/VSCodeVim/Vim/pull/1663#issuecomment-300573129 for - // more details. - const tPos = Position.FromVSCodePosition(vscode.window.activeTextEditor!.selection.active).getRight(); - vscode.window.activeTextEditor!.selection = new vscode.Selection(tPos, tPos); - for (let i = 0; i < count; i++) { - await vscode.commands.executeCommand('editor.action.insertLineBefore'); - } - - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - for (let i = 0; i < count; i++) { - const newPos = new Position(vimState.allCursors[0].start.line + i, vimState.allCursors[0].start.character); - vimState.allCursors.push(new Range(newPos, newPos)); - } - vimState.allCursors = vimState.allCursors.reverse(); - vimState.isFakeMultiCursor = true; - vimState.isMultiCursor = true; - return vimState; - } -} - -@RegisterAction -class CommandInsertNewLineBefore extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["o"]; - runsOnceForEveryCursor() { return false; } - - public async execCount(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - let count = vimState.recordedState.count || 1; - - for (let i = 0; i < count; i++) { - await vscode.commands.executeCommand('editor.action.insertLineAfter'); - } - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - for (let i = 1; i < count; i++) { - const newPos = new Position(vimState.allCursors[0].start.line - i, vimState.allCursors[0].start.character); - vimState.allCursors.push(new Range(newPos, newPos)); - - // Ahhhhhh. We have to manually set cursor position here as we need text - // transformations AND to set multiple cursors. - vimState.recordedState.transformations.push({ - type: "insertText", - text: TextEditor.setIndentationLevel("", newPos.character), - position: newPos, - cursorIndex: i, - manuallySetCursorPositions: true - }); - } - vimState.allCursors = vimState.allCursors.reverse(); - vimState.isFakeMultiCursor = true; - vimState.isMultiCursor = true; - return vimState; - } -} - -@RegisterAction -class CommandNavigateBack extends BaseCommand { - modes = [ModeName.Normal]; - keys = [""]; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - const oldActiveEditor = vimState.editor; - - await vscode.commands.executeCommand('workbench.action.navigateBack'); - - if (oldActiveEditor === vimState.editor) { - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - } - - return vimState; - } -} - -@RegisterAction -class CommandNavigateForward extends BaseCommand { - modes = [ModeName.Normal]; - keys = [""]; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - const oldActiveEditor = vimState.editor; - - await vscode.commands.executeCommand('workbench.action.navigateForward'); - - if (oldActiveEditor === vimState.editor) { - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - } - - return vimState; - } -} - -@RegisterAction -class CommandQuit extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["", "q"]; - - public async exec(position: Position, vimState: VimState): Promise { - new QuitCommand({}).execute(); - - return vimState; - } -} - -@RegisterAction -class MoveToRightPane extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [["", "l"], ["", ""], [""]]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: "workbench.action.navigateRight", - args: {} - }); - - return vimState; - } -} - -@RegisterAction -class MoveToLowerPane extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [["", "j"], ["", ""], [""]]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: "workbench.action.navigateDown", - args: {} - }); - - return vimState; - } -} - -@RegisterAction -class MoveToUpperPane extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [["", "k"], ["", ""], [""]]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: "workbench.action.navigateUp", - args: {} - }); - - return vimState; - } -} - -@RegisterAction -class MoveToLeftPane extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [["", "h"], ["", ""], [""]]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: "workbench.action.navigateLeft", - args: {} - }); - - return vimState; - } -} - - -@RegisterAction -class CycleThroughPanes extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = [["", ""], ["", "w"]]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: "workbench.action.navigateEditorGroups", - args: {} - }); - - return vimState; - } -} - -class BaseTabCommand extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - runsOnceForEachCountPrefix = true; -} - -@RegisterAction -class VerticalSplit extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["", "v"]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.postponedCodeViewChanges.push({ - command: "workbench.action.splitEditor", - args: {} - }); - - return vimState; - } -} - -@RegisterAction -class CommandTabNext extends BaseTabCommand { - keys = [ - ["g", "t"], - [""], - ]; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - (new TabCommand({ - tab: Tab.Next, - count: vimState.recordedState.count - })).execute(); - - return vimState; - } -} - -@RegisterAction -class CommandTabPrevious extends BaseTabCommand { - keys = [ - ["g", "T"], - [""], - ]; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - (new TabCommand({ - tab: Tab.Previous, - count: 1 - })).execute(); - - return vimState; - } -} - -@RegisterAction -class ActionDeleteChar extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["x"]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - // If line is empty, do nothing - if (TextEditor.getLineAt(position).text.length < 1) { - return vimState; - } - - const state = await new operator.DeleteOperator(this.multicursorIndex).run(vimState, position, position); - - state.currentMode = ModeName.Normal; - - return state; - } -} - -@RegisterAction -class ActionDeleteCharWithDeleteKey extends BaseCommand { - modes = [ModeName.Normal]; - keys = [""]; - runsOnceForEachCountPrefix = true; - canBeRepeatedWithDot = true; - - public async execCount(position: Position, vimState: VimState): Promise { - // If has a count in front of it, then deletes a character - // off the count. Therefore, 100x, would apply 'x' 10 times. - // http://vimdoc.sourceforge.net/htmldoc/change.html# - if (vimState.recordedState.count !== 0) { - vimState.recordedState.count = Math.floor(vimState.recordedState.count / 10); - vimState.recordedState.actionKeys = vimState.recordedState.count.toString().split(""); - vimState.recordedState.commandList = vimState.recordedState.count.toString().split(""); - this.isCompleteAction = false; - return vimState; - } - return await new ActionDeleteChar().execCount(position, vimState); - } -} - -@RegisterAction -class ActionDeleteLastChar extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["X"]; - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - if (position.character === 0) { - return vimState; - } - - return await new operator.DeleteOperator(this.multicursorIndex).run(vimState, position.getLeft(), position.getLeft()); - } -} - -@RegisterAction -class ActionJoin extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["J"]; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = false; - - private firstNonWhitespaceIndex(str: string): number { - for (let i = 0, len = str.length; i < len; i++) { - let chCode = str.charCodeAt(i); - if (chCode !== 32 /** space */ && chCode !== 9 /** tab */) { - return i; - } - } - return -1; - } - - public async execJoinLines(startPosition: Position, position: Position, vimState: VimState, count: number): Promise { - count = count - 1 || 1; - - let startLineNumber: number, - startColumn: number, - endLineNumber: number, - endColumn: number, - columnDeltaOffset: number = 0; - - if (startPosition.isEqual(position) || startPosition.line === position.line) { - if (position.line + 1 < TextEditor.getLineCount()) { - startLineNumber = position.line; - startColumn = 0; - endLineNumber = startLineNumber + count; - endColumn = TextEditor.getLineMaxColumn(endLineNumber); - } else { - startLineNumber = position.line; - startColumn = 0; - endLineNumber = position.line; - endColumn = TextEditor.getLineMaxColumn(endLineNumber); - } - } else { - startLineNumber = startPosition.line; - startColumn = 0; - endLineNumber = position.line; - endColumn = TextEditor.getLineMaxColumn(endLineNumber); - } - - let trimmedLinesContent = TextEditor.getLineAt(startPosition).text; - - for (let i = startLineNumber + 1; i <= endLineNumber; i++) { - let lineText = TextEditor.getLineAt(new Position(i, 0)).text; - - let firstNonWhitespaceIdx = this.firstNonWhitespaceIndex(lineText); - - if (firstNonWhitespaceIdx >= 0) { - let insertSpace = true; - - if (trimmedLinesContent === '' || - trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === ' ' || - trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === '\t') { - insertSpace = false; - } - - let lineTextWithoutIndent = lineText.substr(firstNonWhitespaceIdx); - - if (lineTextWithoutIndent.charAt(0) === ')') { - insertSpace = false; - } - - trimmedLinesContent += (insertSpace ? ' ' : '') + lineTextWithoutIndent; - - if (insertSpace) { - columnDeltaOffset = lineTextWithoutIndent.length + 1; - } else { - columnDeltaOffset = lineTextWithoutIndent.length; - } - } else { - if (trimmedLinesContent === '' || - trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === ' ' || - trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === '\t') { - columnDeltaOffset += 0; - } else { - trimmedLinesContent += ' '; - columnDeltaOffset += 1; - } - } - } - - let deleteStartPosition = new Position(startLineNumber, startColumn); - let deleteEndPosition = new Position(endLineNumber, endColumn); - - if (!deleteStartPosition.isEqual(deleteEndPosition)) { - if (startPosition.isEqual(position)) { - vimState.recordedState.transformations.push({ - type : "replaceText", - text : trimmedLinesContent, - start : deleteStartPosition, - end : deleteEndPosition, - diff : new PositionDiff(0, trimmedLinesContent.length - columnDeltaOffset - position.character), - }); - } else { - vimState.recordedState.transformations.push({ - type: "replaceText", - text: trimmedLinesContent, - start: deleteStartPosition, - end: deleteEndPosition, - manuallySetCursorPositions: true - }); - - vimState.cursorPosition = new Position(startPosition.line, trimmedLinesContent.length - columnDeltaOffset); - vimState.cursorStartPosition = vimState.cursorPosition; - vimState.currentMode = ModeName.Normal; - } - } - - return vimState; - } - - public async execCount(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - let resultingCursors: Range[] = []; - let i = 0; - - const cursorsToIterateOver = vimState.allCursors - .map(x => new Range(x.start, x.stop)) - .sort((a, b) => a.start.line > b.start.line || (a.start.line === b.start.line && a.start.character > b.start.character) ? 1 : -1); - - for (const { start, stop } of cursorsToIterateOver) { - this.multicursorIndex = i++; - - vimState.cursorPosition = stop; - vimState.cursorStartPosition = start; - - vimState = await this.execJoinLines(start, stop, vimState, timesToRepeat); - - resultingCursors.push(new Range( - vimState.cursorStartPosition, - vimState.cursorPosition, - )); - - for (const transformation of vimState.recordedState.transformations) { - if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { - transformation.cursorIndex = this.multicursorIndex; - } - } - } - - vimState.allCursors = resultingCursors; - - return vimState; - } -} - -@RegisterAction -class ActionJoinVisualMode extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ["J"]; - - public async exec(position: Position, vimState: VimState): Promise { - let actionJoin = new ActionJoin(); - let start = Position.FromVSCodePosition(vimState.editor.selection.start); - let end = Position.FromVSCodePosition(vimState.editor.selection.end); - - if (start.isAfter(end)) { - [start, end] = [end, start]; - } - - /** - * For joining lines, Visual Line behaves the same as Visual so we align the register mode here. - */ - vimState.currentRegisterMode = RegisterMode.CharacterWise; - vimState = await actionJoin.execJoinLines(start, end, vimState, 1); - - return vimState; - } - - -} - - -@RegisterAction -class ActionJoinNoWhitespace extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["g", "J"]; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = true; - - // gJ is essentially J without the edge cases. ;-) - - public async exec(position: Position, vimState: VimState): Promise { - if (position.line === TextEditor.getLineCount() - 1) { - return vimState; // TODO: bell - } - - let lineOne = TextEditor.getLineAt(position).text; - let lineTwo = TextEditor.getLineAt(position.getNextLineBegin()).text; - - lineTwo = lineTwo.substring(position.getNextLineBegin().getFirstLineNonBlankChar().character); - - let resultLine = lineOne + lineTwo; - - let newState = await new operator.DeleteOperator(this.multicursorIndex).run( - vimState, - position.getLineBegin(), - lineTwo.length > 0 ? - position.getNextLineBegin().getLineEnd().getLeft() : - position.getLineEnd() - ); - - vimState.recordedState.transformations.push({ - type : "insertText", - text : resultLine, - position : position, - diff : new PositionDiff(0, -lineTwo.length), - }); - - newState.cursorPosition = new Position(position.line, lineOne.length); - - return newState; - } -} - -@RegisterAction -class ActionJoinNoWhitespaceVisualMode extends BaseCommand { - modes = [ModeName.Visual]; - keys = ["g", "J"]; - - public async exec(position: Position, vimState: VimState): Promise { - let actionJoin = new ActionJoinNoWhitespace(); - let start = Position.FromVSCodePosition(vimState.editor.selection.start); - let end = Position.FromVSCodePosition(vimState.editor.selection.end); - - if (start.line === end.line) { - return vimState; - } - - if (start.isAfter(end)) { - [start, end] = [end, start]; - } - - for (let i = start.line; i < end.line; i++) { - vimState = await actionJoin.exec(start, vimState); - } - - return vimState; - } -} - -@RegisterAction -class ActionReplaceCharacter extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["r", ""]; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = false; - - public async exec(position: Position, vimState: VimState): Promise { - let timesToRepeat = vimState.recordedState.count || 1; - const toReplace = this.keysPressed[1]; - - /** - * includes , and but not any control keys, - * so we ignore the former two keys and have a special handle for . - */ - - if (['', ''].indexOf(toReplace.toUpperCase()) >= 0) { - return vimState; - } - - if (position.character + timesToRepeat > position.getLineEnd().character) { - return vimState; - } - - let endPos = new Position(position.line, position.character + timesToRepeat); - - // Return if tried to repeat longer than linelength - if (endPos.character > TextEditor.getLineAt(endPos).text.length) { - return vimState; - } - - // If last char (not EOL char), add 1 so that replace selection is complete - if (endPos.character > TextEditor.getLineAt(endPos).text.length) { - endPos = new Position(endPos.line, endPos.character + 1); - } - - if (toReplace === '') { - vimState.recordedState.transformations.push({ - type: "deleteRange", - range: new Range(position, endPos) - }); - vimState.recordedState.transformations.push({ - type: "tab", - cursorIndex: this.multicursorIndex, - diff: new PositionDiff(0, -1) - }); - } else { - vimState.recordedState.transformations.push({ - type : "replaceText", - text : toReplace.repeat(timesToRepeat), - start : position, - end : endPos, - diff : new PositionDiff(0, timesToRepeat - 1), - }); - } - return vimState; - } - - public async execCount(position: Position, vimState: VimState): Promise { - - return super.execCount(position, vimState); - } -} - -@RegisterAction -class ActionReplaceCharacterVisual extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ["r", ""]; - runsOnceForEveryCursor() { return false; } - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const toInsert = this.keysPressed[1]; - - let visualSelectionOffset = 1; - let start = vimState.cursorStartPosition; - let end = vimState.cursorPosition; - - // If selection is reversed, reorganize it so that the text replace logic always works - if (end.isBeforeOrEqual(start)) { - [start, end] = [end, start]; - } - - // Limit to not replace EOL - const textLength = TextEditor.getLineAt(end).text.length; - if (textLength <= 0) { - visualSelectionOffset = 0; - } - end = new Position(end.line, Math.min(end.character, textLength > 0 ? textLength - 1 : 0)); - - // Iterate over every line in the current selection - for (var lineNum = start.line; lineNum <= end.line; lineNum++) { - - // Get line of text - const lineText = TextEditor.getLineAt(new Position(lineNum, 0)).text; - - if (start.line === end.line) { - // This is a visual section all on one line, only replace the part within the selection - vimState.recordedState.transformations.push({ - type: "replaceText", - text: Array(end.character - start.character + 2).join(toInsert), - start: start, - end: new Position(end.line, end.character + 1), - manuallySetCursorPositions : true - }); - } else if (lineNum === start.line) { - // This is the first line of the selection so only replace after the cursor - vimState.recordedState.transformations.push({ - type: "replaceText", - text: Array(lineText.length - start.character + 1).join(toInsert), - start: start, - end: new Position(start.line, lineText.length), - manuallySetCursorPositions : true - }); - } else if (lineNum === end.line) { - // This is the last line of the selection so only replace before the cursor - vimState.recordedState.transformations.push({ - type: "replaceText", - text: Array(end.character + 1 + visualSelectionOffset).join(toInsert), - start: new Position(end.line, 0), - end: new Position(end.line, end.character + visualSelectionOffset), - manuallySetCursorPositions : true - }); - } else { - // Replace the entire line length since it is in the middle of the selection - vimState.recordedState.transformations.push({ - type: "replaceText", - text: Array(lineText.length + 1).join(toInsert), - start: new Position(lineNum, 0), - end: new Position(lineNum, lineText.length), - manuallySetCursorPositions : true - }); - } - } - - vimState.cursorPosition = start; - vimState.cursorStartPosition = start; - vimState.currentMode = ModeName.Normal; - return vimState; - } -} - -@RegisterAction -class ActionReplaceCharacterVisualBlock extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ["r", ""]; - runsOnceForEveryCursor() { return false; } - canBeRepeatedWithDot = true; - - public async exec(position: Position, vimState: VimState): Promise { - const toInsert = this.keysPressed[1]; - for (const { start, end } of Position.IterateLine(vimState)) { - - if (end.isBeforeOrEqual(start)) { - continue; - } - - vimState.recordedState.transformations.push({ - type: "replaceText", - text: Array(end.character - start.character + 1).join(toInsert), - start: start, - end: end, - manuallySetCursorPositions: true - }); - } - - const topLeft = VisualBlockMode.getTopLeftPosition(vimState.cursorPosition, vimState.cursorStartPosition); - vimState.allCursors = [ new Range(topLeft, topLeft) ]; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class ActionXVisualBlock extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ["x"]; - canBeRepeatedWithDot = true; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - for (const { start, end } of Position.IterateLine(vimState)) { - vimState.recordedState.transformations.push({ - type : "deleteRange", - range : new Range(start, end), - manuallySetCursorPositions: true, - }); - } - - const topLeft = VisualBlockMode.getTopLeftPosition(vimState.cursorPosition, vimState.cursorStartPosition); - - vimState.allCursors = [ new Range(topLeft, topLeft) ]; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class ActionDVisualBlock extends ActionXVisualBlock { - modes = [ModeName.VisualBlock]; - keys = ["d"]; - canBeRepeatedWithDot = true; - runsOnceForEveryCursor() { return false; } -} - -@RegisterAction -class ActionShiftDVisualBlock extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ["D"]; - canBeRepeatedWithDot = true; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - for (const { start } of Position.IterateLine(vimState)) { - vimState.recordedState.transformations.push({ - type : "deleteRange", - range : new Range(start, start.getLineEnd()), - manuallySetCursorPositions: true, - }); - } - - const topLeft = VisualBlockMode.getTopLeftPosition(vimState.cursorPosition, vimState.cursorStartPosition); - - vimState.allCursors = [ new Range(topLeft, topLeft) ]; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class ActionGoToInsertVisualBlockMode extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ["I"]; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.isMultiCursor = true; - vimState.isFakeMultiCursor = true; - - - for (const {line, start} of Position.IterateLine(vimState)) { - if (line === "" && start.character !== 0) { - continue; - } - vimState.allCursors.push(new Range(start, start)); - } - vimState.allCursors = vimState.allCursors.slice(1); - return vimState; - } -} - -@RegisterAction -class ActionChangeInVisualBlockMode extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = [["c"], ["s"]]; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - for (const { start, end } of Position.IterateLine(vimState)) { - vimState.recordedState.transformations.push({ - type : "deleteRange", - range : new Range(start, end), - manuallySetCursorPositions: true, - }); - } - - vimState.currentMode = ModeName.Insert; - vimState.isMultiCursor = true; - vimState.isFakeMultiCursor = true; - - for (const {start} of Position.IterateLine(vimState)) { - vimState.allCursors.push(new Range(start, start)); - } - vimState.allCursors = vimState.allCursors.slice(1); - - return vimState; - } -} - -@RegisterAction -class ActionChangeToEOLInVisualBlockMode extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = [["C"], ["S"]]; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - for (const { start } of Position.IterateLine(vimState)) { - vimState.recordedState.transformations.push({ - type: "deleteRange", - range: new Range(start, start.getLineEnd()), - collapseRange: true - }); - } - - vimState.currentMode = ModeName.Insert; - vimState.isMultiCursor = true; - vimState.isFakeMultiCursor = true; - - for (const {end} of Position.IterateLine(vimState)) { - vimState.allCursors.push(new Range(end, end)); - } - vimState.allCursors = vimState.allCursors.slice(1); - - return vimState; - } -} - -@RegisterAction -class ActionGoToInsertVisualBlockModeAppend extends BaseCommand { - modes = [ModeName.VisualBlock]; - keys = ["A"]; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - vimState.currentMode = ModeName.Insert; - vimState.isMultiCursor = true; - vimState.isFakeMultiCursor = true; - - for (const {line, end} of Position.IterateLine(vimState)) { - if (line.trim() === "") { - vimState.recordedState.transformations.push({ - type: "replaceText", - text: TextEditor.setIndentationLevel(line, end.character), - start: new Position(end.line, 0), - end: new Position(end.line, end.character), - position: new Position(end.line, 0) - }); - } - vimState.allCursors.push(new Range(end, end)); - } - vimState.allCursors = vimState.allCursors.slice(1); - return vimState; - } -} - - - -@RegisterAction -class ActionDeleteLineVisualMode extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ["X"]; - - public async exec(position: Position, vimState: VimState): Promise { - if (vimState.currentMode === ModeName.Visual) { - return await new operator.DeleteOperator(this.multicursorIndex) - .run(vimState, - vimState.cursorStartPosition.getLineBegin(), - vimState.cursorPosition.getLineEnd()); - } else { - return await new operator.DeleteOperator(this.multicursorIndex).run(vimState, position.getLineBegin(), position.getLineEnd()); - } - } -} - -@RegisterAction -class ActionChangeLineVisualMode extends BaseCommand { - modes = [ModeName.Visual]; - keys = ["C"]; - - public async exec(position: Position, vimState: VimState): Promise { - return await new operator.DeleteOperator(this.multicursorIndex) - .run(vimState, - vimState.cursorStartPosition.getLineBegin(), - vimState.cursorPosition.getLineEndIncludingEOL()); - } -} - -@RegisterAction -class ActionRemoveLineVisualMode extends BaseCommand { - modes = [ModeName.Visual]; - keys = ["R"]; - - public async exec(position: Position, vimState: VimState): Promise { - return await new operator.DeleteOperator(this.multicursorIndex) - .run(vimState, - vimState.cursorStartPosition.getLineBegin(), - vimState.cursorPosition.getLineEndIncludingEOL()); - } -} - -@RegisterAction -class ActionChangeChar extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["s"]; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - const state = await new operator.ChangeOperator().run(vimState, position, position); - - state.currentMode = ModeName.Insert; - - return state; - } - - // Don't clash with surround mode! - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && - !vimState.recordedState.operator; - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && - !vimState.recordedState.operator; - } -} - -@RegisterAction -class ToggleCaseAndMoveForward extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["~"]; - canBeRepeatedWithDot = true; - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - await new operator.ToggleCaseOperator().run(vimState, vimState.cursorPosition, vimState.cursorPosition); - - vimState.cursorPosition = vimState.cursorPosition.getRight(); - return vimState; - } -} - -abstract class IncrementDecrementNumberAction extends BaseCommand { - modes = [ModeName.Normal]; - canBeRepeatedWithDot = true; - offset: number; - - public async exec(position: Position, vimState: VimState): Promise { - const text = TextEditor.getLineAt(position).text; - - // Start looking to the right for the next word to increment, unless we're - // already on a word to increment, in which case start at the beginning of - // that word. - const whereToStart = text[position.character].match(/\s/) ? position : position.getWordLeft(true); - - for (let { start, end, word } of Position.IterateWords(whereToStart)) { - // '-' doesn't count as a word, but is important to include in parsing - // the number - if (text[start.character - 1] === '-') { - start = start.getLeft(); - word = text[start.character] + word; - } - // Strict number parsing so "1a" doesn't silently get converted to "1" - do { - const num = NumericString.parse(word); - if (num !== null && position.character < start.character + num.prefix.length + num.value.toString().length) { - vimState.cursorPosition = await this.replaceNum(num, this.offset * (vimState.recordedState.count || 1), start, end); - vimState.cursorPosition = vimState.cursorPosition.getLeftByCount(num.suffix.length); - return vimState; - } else if (num !== null) { - word = word.slice(num.prefix.length + num.value.toString().length); - start = new Position(start.line, start.character + num.prefix.length + num.value.toString().length); - } else { - break; - } - } while (true); - } - // No usable numbers, return the original position - return vimState; - } - - public async replaceNum(start: NumericString, offset: number, startPos: Position, endPos: Position): Promise { - const oldWidth = start.toString().length; - start.value += offset; - const newNum = start.toString(); - - const range = new vscode.Range(startPos, endPos.getRight()); - - if (oldWidth === newNum.length) { - await TextEditor.replace(range, newNum); - } else { - // Can't use replace, since new number is a different width than old - await TextEditor.delete(range); - await TextEditor.insertAt(newNum, startPos); - // Adjust end position according to difference in width of number-string - endPos = new Position(endPos.line, endPos.character + (newNum.length - oldWidth)); - } - - return endPos; - } -} - -@RegisterAction -class IncrementNumberAction extends IncrementDecrementNumberAction { - keys = [""]; - offset = +1; -} - -@RegisterAction -class DecrementNumberAction extends IncrementDecrementNumberAction { - keys = [""]; - offset = -1; -} - -@RegisterAction -class ActionTriggerHover extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["g", "h"]; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand("editor.action.showHover"); - - return vimState; - } -} - -/** - * Multi-Cursor Command Overrides - * - * We currently have to override the vscode key commands that get us into multi-cursor mode. - * - * Normally, we'd just listen for another cursor to be added in order to go into multi-cursor - * mode rather than rewriting each keybinding one-by-one. We can't currently do that because - * Visual Block Mode also creates additional cursors, but will get confused if you're in - * multi-cursor mode. - */ - -@RegisterAction -class ActionOverrideCmdD extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = [ - [""], - ["g", "b"] - ]; - runsOnceForEveryCursor() { return false; } - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand('editor.action.addSelectionToNextFindMatch'); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - - // If this is the first cursor, select 1 character less - // so that only the word is selected, no extra character - vimState.allCursors = vimState.allCursors.map(x => x.withNewStop(x.stop.getLeft())); - - vimState.currentMode = ModeName.Visual; - - return vimState; - } -} - - @RegisterAction - class ActionOverrideCmdDInsert extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - runsOnceForEveryCursor() { return false; } - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - // Since editor.action.addSelectionToNextFindMatch uses the selection to - // determine where to add a word, we need to do a hack and manually set the - // selections to the word boundaries before we make the api call. - vscode.window.activeTextEditor!.selections = - vscode.window.activeTextEditor!.selections.map((x, idx) => { - const curPos = Position.FromVSCodePosition(x.active); - if (idx === 0) { - return new vscode.Selection(curPos.getWordLeft(false), - curPos.getLeft().getCurrentWordEnd(true).getRight()); - } else { - // Since we're adding the selections ourselves, we need to make sure - // that our selection is actually over what our original word is - const matchWordPos = Position.FromVSCodePosition(vscode.window.activeTextEditor!.selections[0].active); - const matchWordLength = matchWordPos.getLeft().getCurrentWordEnd(true).getRight().character - - matchWordPos.getWordLeft(false).character; - const wordBegin = curPos.getLeftByCount(matchWordLength); - return new vscode.Selection(wordBegin, curPos); - } - }); - await vscode.commands.executeCommand('editor.action.addSelectionToNextFindMatch'); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - return vimState; - } - } - -@RegisterAction -class ActionOverrideCmdAltDown extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = [ - [""], // OSX - [""], // Windows - ]; - runsOnceForEveryCursor() { return false; } - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand('editor.action.insertCursorBelow'); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - - return vimState; - } -} - -@RegisterAction -class ActionOverrideCmdAltUp extends BaseCommand { - modes = [ModeName.Normal, ModeName.Visual]; - keys = [ - [""], // OSX - [""], // Windows - ]; - runsOnceForEveryCursor() { return false; } - runsOnceForEachCountPrefix = true; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand('editor.action.insertCursorAbove'); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - - return vimState; - } -} - diff --git a/src/actions/commands/insert.ts b/src/actions/commands/insert.ts deleted file mode 100644 index 040eb45f6b1..00000000000 --- a/src/actions/commands/insert.ts +++ /dev/null @@ -1,472 +0,0 @@ -import * as vscode from 'vscode'; -import { RecordedState, VimState } from './../../mode/modeHandler'; -import { Register, RegisterMode } from './../../register/register'; -import { Position, PositionDiff } from './../../common/motion/position'; -import { Range } from './../../common/motion/range'; -import { ModeName } from './../../mode/mode'; -import { Configuration } from './../../configuration/configuration'; -import { TextEditor } from './../../textEditor'; -import { RegisterAction } from './../base'; -import { ArrowsInInsertMode } from './../motion'; -import { - BaseCommand, DocumentContentChangeAction, CommandInsertAtCursor, - CommandInsertAfterCursor, CommandInsertAtLineEnd, - CommandInsertAtFirstCharacter -} from './actions'; - -@RegisterAction -class CommandEscInsertMode extends BaseCommand { - modes = [ - ModeName.Insert - ]; - keys = [ - [""], - [""], - [""], - ]; - - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - vimState.allCursors = vimState.allCursors.map(x => x.withNewStop(x.stop.getLeft())); - - // only remove leading spaces inserted by vscode. - // vscode only inserts them when user enter a new line, - // ie, o/O in Normal mode or \n in Insert mode. - for (let i = 0; i < vimState.allCursors.length; i++) { - const lastActionBeforeEsc = vimState.keyHistory[vimState.keyHistory.length - 2]; - if (['o', 'O', '\n'].indexOf(lastActionBeforeEsc) > -1 && - vimState.editor.document.languageId !== 'plaintext' && - /^\s+$/.test(TextEditor.getLineAt(vimState.allCursors[i].stop).text)) { - vimState.recordedState.transformations.push({ - type: "deleteRange", - range: new Range(vimState.allCursors[i].stop.getLineBegin(), vimState.allCursors[i].stop.getLineEnd()) - }); - vimState.allCursors[i] = vimState.allCursors[i].withNewStop(vimState.allCursors[i].stop.getLineBegin()); - } - } - vimState.currentMode = ModeName.Normal; - - // If we wanted to repeat this insert (only for i and a), now is the time to do it. Insert - // count amount of these strings before returning back to normal mode - const typeOfInsert = vimState.recordedState.actionsRun[vimState.recordedState.actionsRun.length - 3]; - const isTypeToRepeatInsert = typeOfInsert instanceof CommandInsertAtCursor || - typeOfInsert instanceof CommandInsertAfterCursor || - typeOfInsert instanceof CommandInsertAtLineEnd || - typeOfInsert instanceof CommandInsertAtFirstCharacter; - - // If this is the type to repeat insert, do this now - if (vimState.recordedState.count > 1 && isTypeToRepeatInsert) { - const changeAction = vimState.recordedState.actionsRun[vimState.recordedState.actionsRun.length - 2] as DocumentContentChangeAction; - const changesArray = changeAction.contentChanges; - let docChanges: vscode.TextDocumentContentChangeEvent[] = []; - - for (let i = 0; i < changesArray.length; i++) { - docChanges.push(changesArray[i].textDiff); - } - - let positionDiff = new PositionDiff(0, 0); - // Add count amount of inserts in the case of 4i= - for (let i = 0; i < (vimState.recordedState.count - 1); i++) { - // If this is the last transform, move cursor back one character - if (i === (vimState.recordedState.count - 2)) { - positionDiff = new PositionDiff(0, -1); - } - - // Add a transform containing the change - vimState.recordedState.transformations.push({ - type: "contentChange", - changes: docChanges, - diff: positionDiff - }); - } - } - - if (vimState.historyTracker.currentContentChanges.length > 0) { - vimState.historyTracker.lastContentChanges = vimState.historyTracker.currentContentChanges; - vimState.historyTracker.currentContentChanges = []; - } - - if (vimState.isFakeMultiCursor) { - vimState.allCursors = [vimState.allCursors[0]]; - vimState.isMultiCursor = false; - vimState.isFakeMultiCursor = false; - } - return vimState; - } -} - -@RegisterAction -export class CommandInsertPreviousText extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - let actions = ((await Register.getByKey('.')).text as RecordedState).actionsRun.slice(0); - // let actions = Register.lastContentChange.actionsRun.slice(0); - // The first action is entering Insert Mode, which is not necessary in this case - actions.shift(); - // The last action is leaving Insert Mode, which is not necessary in this case - // actions.pop(); - - if (actions.length > 0 && actions[0] instanceof ArrowsInInsertMode) { - // Note, arrow keys are the only Insert action command that can't be repeated here as far as @rebornix knows. - actions.shift(); - } - - for (let action of actions) { - if (action instanceof BaseCommand) { - vimState = await action.execCount(vimState.cursorPosition, vimState); - } - - if (action instanceof DocumentContentChangeAction) { - vimState = await action.exec(vimState.cursorPosition, vimState); - } - } - - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.end); - vimState.cursorStartPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - vimState.currentMode = ModeName.Insert; - return vimState; - } -} - -@RegisterAction -class CommandInsertPreviousTextAndQuit extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; // - - public async exec(position: Position, vimState: VimState): Promise { - vimState = await new CommandInsertPreviousText().exec(position, vimState); - vimState.currentMode = ModeName.Normal; - return vimState; - } -} - -@RegisterAction -class CommandInsertBelowChar extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - if (TextEditor.isLastLine(position)) { - return vimState; - } - - const charBelowCursorPosition = position.getDownByCount(1); - - if (charBelowCursorPosition.isLineEnd()) { - return vimState; - } - - const char = TextEditor.getText(new vscode.Range(charBelowCursorPosition, charBelowCursorPosition.getRight())); - await TextEditor.insert(char, position); - - vimState.cursorStartPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - - return vimState; - } -} - -@RegisterAction -class CommandInsertIndentInCurrentLine extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - const originalText = TextEditor.getLineAt(position).text; - const indentationWidth = TextEditor.getIndentationLevel(originalText); - const tabSize = Configuration.tabstop || Number(vimState.editor.options.tabSize); - const newIndentationWidth = (indentationWidth / tabSize + 1) * tabSize; - - TextEditor.replaceText( - vimState, TextEditor.setIndentationLevel(originalText, newIndentationWidth), - position.getLineBegin(), position.getLineEnd(), - new PositionDiff(0, newIndentationWidth - indentationWidth) - ); - - return vimState; - } -} - -// Upon thinking about it some more, I'm not really sure how to fix this -// elegantly. Tab is just used for so many things in the VSCode editor, and all -// of them happen to be overloaded. Sometimes tab does a tab, sometimes it does -// an emmet completion, sometimes a snippet completion, etc. -// @RegisterAction -// export class CommandInsertTabInInsertMode extends BaseCommand { -// modes = [ModeName.Insert]; -// keys = [""]; -// runsOnceForEveryCursor() { return false; } - -// public async exec(position: Position, vimState: VimState): Promise { -// vimState.recordedState.transformations.push({ -// type: "tab" -// }); -// return vimState; -// } -// } - -@RegisterAction -export class CommandInsertInInsertMode extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - const char = this.keysPressed[this.keysPressed.length - 1]; - const line = TextEditor.getLineAt(position).text; - - if (char === "") { - const selection = TextEditor.getSelection(); - - // Check if a selection is active - if (!selection.isEmpty) { - vimState.recordedState.transformations.push({ - type: "deleteRange", - range: new Range(selection.start as Position, selection.end as Position), - }); - } else { - if (line.length > 0 && line.match(/^ +$/) && Configuration.expandtab) { - // If the line is empty except whitespace, backspace should return to - // the next lowest level of indentation. - - const tabSize = vimState.editor.options.tabSize as number; - const desiredLineLength = Math.floor((position.character - 1) / tabSize) * tabSize; - - vimState.recordedState.transformations.push({ - type: "deleteRange", - range: new Range(new Position(position.line, desiredLineLength), new Position(position.line, line.length)) - }); - } else { - if (position.line !== 0 || position.character !== 0) { - vimState.recordedState.transformations.push({ - type: "deleteText", - position: position, - }); - } - } - } - - vimState.cursorPosition = vimState.cursorPosition.getLeft(); - vimState.cursorStartPosition = vimState.cursorStartPosition.getLeft(); - } else { - if (vimState.isMultiCursor) { - vimState.recordedState.transformations.push({ - type: "insertText", - text: char, - position: vimState.cursorPosition, - }); - } else { - vimState.recordedState.transformations.push({ - type: "insertTextVSCode", - text: char, - }); - } - } - - return vimState; - } - - public toString(): string { - return this.keysPressed[this.keysPressed.length - 1]; - } -} - -@RegisterAction -class CommandInsertRegisterContent extends BaseCommand { - modes = [ModeName.Insert]; - keys = ["", ""]; - isCompleteAction = false; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.recordedState.registerName = this.keysPressed[1]; - const register = await Register.get(vimState); - let text: string; - - if (register.text instanceof Array) { - text = (register.text as string[]).join("\n"); - } else if (register.text instanceof RecordedState) { - vimState.recordedState.transformations.push({ - type: "macro", - register: vimState.recordedState.registerName, - replay: "keystrokes" - }); - - return vimState; - } else { - text = register.text; - } - - if (register.registerMode === RegisterMode.LineWise) { - text += "\n"; - } - - await TextEditor.insertAt(text, position); - vimState.currentMode = ModeName.Insert; - vimState.cursorStartPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return super.doesActionApply(vimState, keysPressed) && Register.isValidRegister(register); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const register = keysPressed[1]; - - return super.couldActionApply(vimState, keysPressed) && Register.isValidRegister(register); - } - -} - -@RegisterAction -export class CommandOneNormalCommandInInsertMode extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.returnToInsertAfterCommand = true; - return await new CommandEscInsertMode().exec( - position.character === 0 ? position : position.getRight(), - vimState); - } -} -@RegisterAction -class CommandCtrlW extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - let wordBegin; - if (position.isInLeadingWhitespace()) { - wordBegin = position.getLineBegin(); - } else if (position.isLineBeginning()) { - wordBegin = position.getPreviousLineBegin().getLineEnd(); - } else { - wordBegin = position.getWordLeft(); - } - - await TextEditor.delete(new vscode.Range(wordBegin, position)); - - vimState.cursorPosition = wordBegin; - - return vimState; - } -} - - -@RegisterAction -class CommandDeleteIndentInCurrentLine extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - const originalText = TextEditor.getLineAt(position).text; - const indentationWidth = TextEditor.getIndentationLevel(originalText); - - if (indentationWidth === 0) { - return vimState; - } - - const tabSize = Configuration.tabstop; - const newIndentationWidth = (indentationWidth / tabSize - 1) * tabSize; - - await TextEditor.replace(new vscode.Range(position.getLineBegin(), position.getLineEnd()), - TextEditor.setIndentationLevel(originalText, newIndentationWidth < 0 ? 0 : newIndentationWidth)); - - const cursorPosition = Position.FromVSCodePosition(position.with(position.line, - position.character + (newIndentationWidth - indentationWidth) / tabSize)); - vimState.cursorPosition = cursorPosition; - vimState.cursorStartPosition = cursorPosition; - vimState.currentMode = ModeName.Insert; - return vimState; - } -} - - -@RegisterAction -class CommandInsertAboveChar extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - if (TextEditor.isFirstLine(position)) { - return vimState; - } - - const charAboveCursorPosition = position.getUpByCount(1); - - if (charAboveCursorPosition.isLineEnd()) { - return vimState; - } - - const char = TextEditor.getText(new vscode.Range(charAboveCursorPosition, charAboveCursorPosition.getRight())); - await TextEditor.insert(char, position); - - vimState.cursorStartPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - vimState.cursorPosition = Position.FromVSCodePosition(vimState.editor.selection.start); - - return vimState; - } -} - -@RegisterAction -class CommandCtrlHInInsertMode extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - vimState.recordedState.transformations.push({ - type: "deleteText", - position: position, - }); - - return vimState; - } -} - - -@RegisterAction -class CommandCtrlUInInsertMode extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - const start = position.getLineBegin(); - const stop = position.getLineEnd(); - await TextEditor.delete(new vscode.Range(start, stop)); - vimState.cursorPosition = start; - vimState.cursorStartPosition = start; - return vimState; - } -} - - -@RegisterAction -class CommandCtrlN extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand("selectNextSuggestion"); - - return vimState; - } -} - -@RegisterAction -class CommandCtrlP extends BaseCommand { - modes = [ModeName.Insert]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand("selectPrevSuggestion"); - - return vimState; - } -} diff --git a/src/actions/motion.ts b/src/actions/motion.ts deleted file mode 100644 index 838478363bd..00000000000 --- a/src/actions/motion.ts +++ /dev/null @@ -1,1739 +0,0 @@ -import * as vscode from 'vscode'; -import { ModeName } from './../mode/mode'; -import { Position, PositionDiff } from './../common/motion/position'; -import { Configuration } from './../configuration/configuration'; -import { TextEditor, CursorMovePosition, CursorMoveByUnit } from './../textEditor'; -import { VimState } from './../mode/modeHandler'; -import { RegisterMode } from './../register/register'; -import { PairMatcher } from './../common/matching/matcher'; -import { ReplaceState } from './../state/replaceState'; -import { QuoteMatcher } from './../common/matching/quoteMatcher'; -import { TagMatcher } from './../common/matching/tagMatcher'; -import { RegisterAction } from './base'; -import { ChangeOperator } from './operator'; -import { BaseAction } from './base'; - -export function isIMovement(o: IMovement | Position): o is IMovement { - return (o as IMovement).start !== undefined && - (o as IMovement).stop !== undefined; -} - -/** - * The result of a (more sophisticated) Movement. - */ -export interface IMovement { - start: Position; - stop: Position; - - /** - * Whether this motion succeeded. Some commands, like fx when 'x' can't be found, - * will not move the cursor. Furthermore, dfx won't delete *anything*, even though - * deleting to the current character would generally delete 1 character. - */ - failed?: boolean; - - diff?: PositionDiff; - - // It /so/ annoys me that I have to put this here. - registerMode?: RegisterMode; -} - -/** - * A movement is something like 'h', 'k', 'w', 'b', 'gg', etc. - */ -export abstract class BaseMovement extends BaseAction { - modes = [ - ModeName.Normal, - ModeName.Visual, - ModeName.VisualLine, - ModeName.VisualBlock, - ]; - - isMotion = true; - - canBePrefixedWithCount = false; - - /** - * Whether we should change lastRepeatableMovement in VimState. - */ - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return false; - } - - /** - * Whether we should change desiredColumn in VimState. - */ - public doesntChangeDesiredColumn = false; - - /** - * This is for commands like $ which force the desired column to be at - * the end of even the longest line. - */ - public setsDesiredColumnToEOL = false; - - /** - * Run the movement a single time. - * - * Generally returns a new Position. If necessary, it can return an IMovement instead. - * Note: If returning an IMovement, make sure that repeated actions on a - * visual selection work. For example, V}} - */ - public async execAction(position: Position, vimState: VimState): Promise { - throw new Error("Not implemented!"); - } - - /** - * Run the movement in an operator context a single time. - * - * Some movements operate over different ranges when used for operators. - */ - public async execActionForOperator(position: Position, vimState: VimState): Promise { - return await this.execAction(position, vimState); - } - - /** - * Run a movement count times. - * - * count: the number prefix the user entered, or 0 if they didn't enter one. - */ - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - let recordedState = vimState.recordedState; - let result: Position | IMovement = new Position(0, 0); // bogus init to satisfy typechecker - - if (count < 1) { - count = 1; - } else if (count > 99999) { - count = 99999; - } - - for (let i = 0; i < count; i++) { - const firstIteration = (i === 0); - const lastIteration = (i === count - 1); - const temporaryResult = (recordedState.operator && lastIteration) ? - await this.execActionForOperator(position, vimState) : - await this.execAction(position, vimState); - - if (temporaryResult instanceof Position) { - result = temporaryResult; - position = temporaryResult; - } else if (isIMovement(temporaryResult)) { - if (result instanceof Position) { - result = { - start: new Position(0, 0), - stop: new Position(0, 0), - failed: false - }; - } - - result.failed = result.failed || temporaryResult.failed; - - if (firstIteration) { - (result as IMovement).start = temporaryResult.start; - } - - if (lastIteration) { - (result as IMovement).stop = temporaryResult.stop; - } else { - position = temporaryResult.stop.getRightThroughLineBreaks(); - } - - result.registerMode = temporaryResult.registerMode; - } - } - - return result; - } -} - -abstract class MoveByScreenLine extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - movementType: CursorMovePosition; - by: CursorMoveByUnit; - value: number = 1; - - public async execAction(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand("cursorMove", { - to: this.movementType, - select: vimState.currentMode !== ModeName.Normal, - by: this.by, - value: this.value - }); - - if (vimState.currentMode === ModeName.Normal) { - return Position.FromVSCodePosition(vimState.editor.selection.active); - } else { - /** - * cursorMove command is handling the selection for us. - * So we are not following our design principal (do no real movement inside an action) here. - */ - let start = Position.FromVSCodePosition(vimState.editor.selection.start); - let stop = Position.FromVSCodePosition(vimState.editor.selection.end); - let curPos = Position.FromVSCodePosition(vimState.editor.selection.active); - - // We want to swap the cursor start stop positions based on which direction we are moving, up or down - if (start.isEqual(curPos)) { - position = start; - [start, stop] = [stop, start]; - start = start.getLeft(); - } - - return { start, stop }; - } - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - await vscode.commands.executeCommand("cursorMove", { - to: this.movementType, - select: true, - by: this.by, - value: this.value - }); - - return { - start: Position.FromVSCodePosition(vimState.editor.selection.start), - stop: Position.FromVSCodePosition(vimState.editor.selection.end) - }; - } -} - -abstract class MoveByScreenLineMaintainDesiredColumn extends MoveByScreenLine { - doesntChangeDesiredColumn = true; - public async execAction(position: Position, vimState: VimState): Promise < Position | IMovement > { - let prevDesiredColumn = vimState.desiredColumn; - let prevLine = vimState.editor.selection.active.line; - - await vscode.commands.executeCommand("cursorMove", { - to: this.movementType, - select: vimState.currentMode !== ModeName.Normal, - by: this.by, - value: this.value - }); - - if (vimState.currentMode === ModeName.Normal) { - let returnedPos = Position.FromVSCodePosition(vimState.editor.selection.active); - if (prevLine !== returnedPos.line) { - returnedPos = returnedPos.setLocation(returnedPos.line, prevDesiredColumn); - } - return returnedPos; - } else { - /** - * cursorMove command is handling the selection for us. - * So we are not following our design principal (do no real movement inside an action) here. - */ - let start = Position.FromVSCodePosition(vimState.editor.selection.start); - let stop = Position.FromVSCodePosition(vimState.editor.selection.end); - let curPos = Position.FromVSCodePosition(vimState.editor.selection.active); - - // We want to swap the cursor start stop positions based on which direction we are moving, up or down - if (start.isEqual(curPos)) { - position = start; - [start, stop] = [stop, start]; - start = start.getLeft(); - } - - return { start, stop }; - - } - } -} - -class MoveDownByScreenLineMaintainDesiredColumn extends MoveByScreenLineMaintainDesiredColumn { - movementType: CursorMovePosition = "down"; - by: CursorMoveByUnit = "wrappedLine"; - value = 1; -} - -class MoveDownFoldFix extends MoveByScreenLineMaintainDesiredColumn { - movementType: CursorMovePosition = "down"; - by: CursorMoveByUnit = "line"; - value = 1; - - public async execAction(position: Position, vimState: VimState): Promise < Position | IMovement > { - if (position.line === TextEditor.getLineCount() - 1) { - return position; - } - let t: Position; - let count = 0; - const prevDesiredColumn = vimState.desiredColumn; - do { - t = (await new MoveDownByScreenLine().execAction(position, vimState)); - count += 1; - } while (t.line === position.line); - if (t.line > position.line + 1) { - return t; - } - while (count > 0) { - t = await new MoveUpByScreenLine().execAction(position, vimState); - count--; - } - vimState.desiredColumn = prevDesiredColumn; - return await position.getDown(vimState.desiredColumn); - } -} - -@RegisterAction -class MoveDown extends BaseMovement { - keys = ["j"]; - doesntChangeDesiredColumn = true; - - - public async execAction(position: Position, vimState: VimState): Promise { - if (Configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) { - return new MoveDownFoldFix().execAction(position, vimState); - } - return position.getDown(vimState.desiredColumn); - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - vimState.currentRegisterMode = RegisterMode.LineWise; - return position.getDown(position.getLineEnd().character); - } - -} - -@RegisterAction -class MoveDownArrow extends MoveDown { - keys = [""]; -} - -class MoveUpByScreenLineMaintainDesiredColumn extends MoveByScreenLineMaintainDesiredColumn { - movementType: CursorMovePosition = "up"; - by: CursorMoveByUnit = "wrappedLine"; - value = 1; -} - -@RegisterAction -class MoveUp extends BaseMovement { - keys = ["k"]; - doesntChangeDesiredColumn = true; - - public async execAction(position: Position, vimState: VimState): Promise { - if (Configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) { - return new MoveUpFoldFix().execAction(position, vimState); - } - return position.getUp(vimState.desiredColumn); - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - vimState.currentRegisterMode = RegisterMode.LineWise; - return position.getUp(position.getLineEnd().character); - } -} - -@RegisterAction -class MoveUpFoldFix extends MoveByScreenLineMaintainDesiredColumn { - movementType: CursorMovePosition = "up"; - by: CursorMoveByUnit = "line"; - value = 1; - - public async execAction(position: Position, vimState: VimState): Promise < Position | IMovement > { - if (position.line === 0) { - return position; - } - let t: Position; - const prevDesiredColumn = vimState.desiredColumn; - let count = 0; - - do { - t = (await new MoveUpByScreenLineMaintainDesiredColumn().execAction(position, vimState)); - count += 1; - } while (t.line === position.line); - vimState.desiredColumn = prevDesiredColumn; - if (t.line < position.line - 1) { - return t; - } - while (count > 0) { - t = await new MoveDownByScreenLine().execAction(position, vimState); - count--; - } - vimState.desiredColumn = prevDesiredColumn; - return await position.getUp(vimState.desiredColumn); - } -} - -@RegisterAction -class MoveUpArrow extends MoveUp { - keys = [""]; -} - -@RegisterAction -class ArrowsInReplaceMode extends BaseMovement { - modes = [ModeName.Replace]; - keys = [ - [""], - [""], - [""], - [""], - ]; - - public async execAction(position: Position, vimState: VimState): Promise { - let newPosition: Position = position; - - switch (this.keysPressed[0]) { - case "": - newPosition = (await new MoveUpArrow().execAction(position, vimState)); - break; - case "": - newPosition = (await new MoveDownArrow().execAction(position, vimState)); - break; - case "": - newPosition = await new MoveLeftArrow().execAction(position, vimState); - break; - case "": - newPosition = await new MoveRightArrow().execAction(position, vimState); - break; - default: - break; - } - vimState.replaceState = new ReplaceState(newPosition); - return newPosition; - } -} - -@RegisterAction -class UpArrowInReplaceMode extends ArrowsInReplaceMode { - keys = [[""]]; -} - -@RegisterAction -class DownArrowInReplaceMode extends ArrowsInReplaceMode { - keys = [[""]]; -} - -@RegisterAction -class LeftArrowInReplaceMode extends ArrowsInReplaceMode { - keys = [[""]]; -} - -@RegisterAction -class RightArrowInReplaceMode extends ArrowsInReplaceMode { - keys = [[""]]; -} - -@RegisterAction -class CommandNextSearchMatch extends BaseMovement { - keys = ["n"]; - - public async execAction(position: Position, vimState: VimState): Promise { - const searchState = vimState.globalState.searchState; - - if (!searchState || searchState.searchString === "") { - return position; - } - // Turn one of the highlighting flags back on (turned off with :nohl) - vimState.globalState.hl = true; - - if (vimState.cursorPosition.getRight().isEqual(vimState.cursorPosition.getLineEnd())) { - return searchState.getNextSearchMatchPosition(vimState.cursorPosition.getRight()).pos; - } - - // Turn one of the highlighting flags back on (turned off with :nohl) - - return searchState.getNextSearchMatchPosition(vimState.cursorPosition).pos; - } -} - -@RegisterAction -class CommandPreviousSearchMatch extends BaseMovement { - keys = ["N"]; - - public async execAction(position: Position, vimState: VimState): Promise { - const searchState = vimState.globalState.searchState; - - if (!searchState || searchState.searchString === "") { - return position; - } - - // Turn one of the highlighting flags back on (turned off with :nohl) - vimState.globalState.hl = true; - - return searchState.getNextSearchMatchPosition(vimState.cursorPosition, -1).pos; - } -} - -@RegisterAction -export class MarkMovementBOL extends BaseMovement { - keys = ["'", ""]; - - public async execAction(position: Position, vimState: VimState): Promise { - const markName = this.keysPressed[1]; - const mark = vimState.historyTracker.getMark(markName); - - vimState.currentRegisterMode = RegisterMode.LineWise; - - return mark.position.getFirstLineNonBlankChar(); - } -} - -@RegisterAction -export class MarkMovement extends BaseMovement { - keys = ["`", ""]; - - public async execAction(position: Position, vimState: VimState): Promise { - const markName = this.keysPressed[1]; - const mark = vimState.historyTracker.getMark(markName); - - return mark.position; - } -} - - -@RegisterAction -export class MoveLeft extends BaseMovement { - keys = ["h"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLeft(); - } -} - -@RegisterAction -class MoveLeftArrow extends MoveLeft { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = [""]; -} - -@RegisterAction -class BackSpaceInNormalMode extends BaseMovement { - modes = [ModeName.Normal]; - keys = [""]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLeftThroughLineBreaks(); - } -} - -@RegisterAction -class MoveRight extends BaseMovement { - keys = ["l"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return new Position(position.line, position.character + 1); - } -} - -@RegisterAction -class MoveRightArrow extends MoveRight { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = [""]; -} - -@RegisterAction -class MoveRightWithSpace extends BaseMovement { - keys = [" "]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getRightThroughLineBreaks(); - } -} - - -@RegisterAction -class MoveDownNonBlank extends BaseMovement { - keys = ["+"]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - return position.getDownByCount(Math.max(count, 1)) - .getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveUpNonBlank extends BaseMovement { - keys = ["-"]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - return position.getUpByCount(Math.max(count, 1)) - .getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveDownUnderscore extends BaseMovement { - keys = ["_"]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - return position.getDownByCount(Math.max(count - 1, 0)) - .getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveToColumn extends BaseMovement { - keys = ["|"]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - return new Position(position.line, Math.max(0, count - 1)); - } -} - -@RegisterAction -class MoveFindForward extends BaseMovement { - keys = ["f", ""]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - count = count || 1; - const toFind = this.keysPressed[1]; - let result = position.findForwards(toFind, count); - - if (!result) { - return { start: position, stop: position, failed: true }; - } - - if (vimState.recordedState.operator) { - result = result.getRight(); - } - - return result; - } - - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); - } -} - -@RegisterAction -class MoveFindBackward extends BaseMovement { - keys = ["F", ""]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - count = count || 1; - const toFind = this.keysPressed[1]; - let result = position.findBackwards(toFind, count); - - if (!result) { - return { start: position, stop: position, failed: true }; - } - - return result; - } - - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); - } -} - - -@RegisterAction -class MoveTilForward extends BaseMovement { - keys = ["t", ""]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - count = count || 1; - const toFind = this.keysPressed[1]; - let result = position.tilForwards(toFind, count); - - if (!result) { - return { start: position, stop: position, failed: true }; - } - - if (vimState.recordedState.operator) { - result = result.getRight(); - } - - return result; - } - - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); - } -} - -@RegisterAction -class MoveTilBackward extends BaseMovement { - keys = ["T", ""]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - count = count || 1; - const toFind = this.keysPressed[1]; - let result = position.tilBackwards(toFind, count); - - if (!result) { - return { start: position, stop: position, failed: true }; - } - - return result; - } - - public canBeRepeatedWithSemicolon(vimState: VimState, result: Position | IMovement) { - return !vimState.recordedState.operator || !(isIMovement(result) && result.failed); - } -} - -@RegisterAction -class MoveRepeat extends BaseMovement { - keys = [";"]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - const movement = VimState.lastRepeatableMovement; - if (movement) { - const result = await movement.execActionWithCount(position, vimState, count); - /** - * For t and T commands vim executes ; as 2; - * This way the cursor will get to the next instance of - */ - if (result instanceof Position && position.isEqual(result) && count <= 1) { - return await movement.execActionWithCount(position, vimState, 2); - } - return result; - } - return position; - } -} - - -@RegisterAction -class MoveRepeatReversed extends BaseMovement { - keys = [","]; - static reverseMotionMapping: Map BaseMovement> = new Map([ - [MoveFindForward, () => new MoveFindBackward()], - [MoveFindBackward, () => new MoveFindForward()], - [MoveTilForward, () => new MoveTilBackward()], - [MoveTilBackward, () => new MoveTilForward()] - ]); - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - const movement = VimState.lastRepeatableMovement; - if (movement) { - const reverse = MoveRepeatReversed.reverseMotionMapping.get(movement.constructor)!(); - reverse.keysPressed = [(reverse.keys as string[])[0], movement.keysPressed[1]]; - - let result = await reverse.execActionWithCount(position, vimState, count); - // For t and T commands vim executes ; as 2; - if (result instanceof Position && position.isEqual(result) && count <= 1) { - result = await reverse.execActionWithCount(position, vimState, 2); - } - return result; - } - return position; - } -} - -@RegisterAction -class MoveLineEnd extends BaseMovement { - keys = [ - ["$"], - [""], - [""]]; - setsDesiredColumnToEOL = true; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLineEnd(); - } -} - -@RegisterAction -class MoveLineBegin extends BaseMovement { - keys = [["0"], - [""], - [""]]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLineBegin(); - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && - vimState.recordedState.count === 0; - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.couldActionApply(vimState, keysPressed) && - vimState.recordedState.count === 0; - } -} - -@RegisterAction -class MoveScreenLineBegin extends MoveByScreenLine { - keys = ["g", "0"]; - movementType: CursorMovePosition = "wrappedLineStart"; -} - -@RegisterAction -class MoveScreenNonBlank extends MoveByScreenLine { - keys = ["g", "^"]; - movementType: CursorMovePosition = "wrappedLineFirstNonWhitespaceCharacter"; -} - -@RegisterAction -class MoveScreenLineEnd extends MoveByScreenLine { - keys = ["g", "$"]; - movementType: CursorMovePosition = "wrappedLineEnd"; -} - -@RegisterAction -class MoveScreenLineEndNonBlank extends MoveByScreenLine { - keys = ["g", "_"]; - movementType: CursorMovePosition = "wrappedLineLastNonWhitespaceCharacter"; - canBePrefixedWithCount = true; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - count = count || 1; - const pos = await this.execAction(position, vimState); - const newPos: Position | IMovement = pos as Position; - - // If in visual, return a selection - if (pos instanceof Position) { - return pos.getDownByCount(count - 1); - } else if (isIMovement(pos)) { - return { start: pos.start, stop: pos.stop.getDownByCount(count - 1).getLeft() }; - } - - return newPos.getDownByCount(count - 1); - } -} - -@RegisterAction -class MoveScreenLineCenter extends MoveByScreenLine { - keys = ["g", "m"]; - movementType: CursorMovePosition = "wrappedLineColumnCenter"; -} - -@RegisterAction -export class MoveUpByScreenLine extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual]; - keys = [["g", "k"], - ["g", ""]]; - movementType: CursorMovePosition = "up"; - by: CursorMoveByUnit = "wrappedLine"; - value = 1; -} - -@RegisterAction -class MoveDownByScreenLine extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual]; - keys = [["g", "j"], - ["g", ""]]; - movementType: CursorMovePosition = "down"; - by: CursorMoveByUnit = "wrappedLine"; - value = 1; -} - - -// Because we can't support moving by screen line when in visualLine mode, -// we change to moving by regular line in visualLine mode. We can't move by -// screen line is that our ranges only support a start and stop attribute, -// and moving by screen line just snaps us back to the original position. -// Check PR #1600 for discussion. -@RegisterAction -class MoveUpByScreenLineVisualLine extends MoveByScreenLine { - modes = [ModeName.VisualLine]; - keys = [["g", "k"], - ["g", ""]]; - movementType: CursorMovePosition = "up"; - by: CursorMoveByUnit = "line"; - value = 1; -} - -@RegisterAction -class MoveDownByScreenLineVisualLine extends MoveByScreenLine { - modes = [ModeName.VisualLine]; - keys = [["g", "j"], - ["g", ""]]; - movementType: CursorMovePosition = "down"; - by: CursorMoveByUnit = "line"; - value = 1; -} - -@RegisterAction -class MoveScreenToRight extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["z", "h"]; - movementType: CursorMovePosition = "right"; - by: CursorMoveByUnit = "character"; - value = 1; -} - -@RegisterAction -class MoveScreenToLeft extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["z", "l"]; - movementType: CursorMovePosition = "left"; - by: CursorMoveByUnit = "character"; - value = 1; -} - -@RegisterAction -class MoveScreenToRightHalf extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["z", "H"]; - movementType: CursorMovePosition = "right"; - by: CursorMoveByUnit = "halfLine"; - value = 1; -} - -@RegisterAction -class MoveScreenToLeftHalf extends MoveByScreenLine { - modes = [ModeName.Insert, ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["z", "L"]; - movementType: CursorMovePosition = "left"; - by: CursorMoveByUnit = "halfLine"; - value = 1; -} - -@RegisterAction -class MoveToLineFromViewPortTop extends MoveByScreenLine { - keys = ["H"]; - movementType: CursorMovePosition = "viewPortTop"; - by: CursorMoveByUnit = "line"; - value = 1; - canBePrefixedWithCount = true; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - this.value = count < 1 ? 1 : count; - return await this.execAction(position, vimState); - } -} - -@RegisterAction -class MoveToLineFromViewPortBottom extends MoveByScreenLine { - keys = ["L"]; - movementType: CursorMovePosition = "viewPortBottom"; - by: CursorMoveByUnit = "line"; - value = 1; - canBePrefixedWithCount = true; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - this.value = count < 1 ? 1 : count; - return await this.execAction(position, vimState); - } -} - -@RegisterAction -class MoveToMiddleLineInViewPort extends MoveByScreenLine { - keys = ["M"]; - movementType: CursorMovePosition = "viewPortCenter"; - by: CursorMoveByUnit = "line"; -} - -@RegisterAction -class MoveNonBlank extends BaseMovement { - keys = ["^"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveNextLineNonBlank extends BaseMovement { - keys = ["\n"]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - vimState.currentRegisterMode = RegisterMode.LineWise; - - // Count === 0 if just pressing enter in normal mode, need to still go down 1 line - if (count === 0) { - count++; - } - - return position.getDownByCount(count).getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveNonBlankFirst extends BaseMovement { - keys = ["g", "g"]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - if (count === 0) { - return position.getDocumentBegin().getFirstLineNonBlankChar(); - } - - return new Position(count - 1, 0).getFirstLineNonBlankChar(); - } -} - -@RegisterAction -class MoveNonBlankLast extends BaseMovement { - keys = ["G"]; - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - let stop: Position; - - if (count === 0) { - stop = new Position(TextEditor.getLineCount() - 1, 0); - } else { - stop = new Position(Math.min(count, TextEditor.getLineCount()) - 1, 0); - } - - return { - start: vimState.cursorStartPosition, - stop: stop, - registerMode: RegisterMode.LineWise - }; - } -} - -@RegisterAction -export class MoveWordBegin extends BaseMovement { - keys = ["w"]; - - public async execAction(position: Position, vimState: VimState, isLastIteration: boolean = false): Promise { - if (isLastIteration && vimState.recordedState.operator instanceof ChangeOperator) { - if (TextEditor.getLineAt(position).text.length < 1) { - return position; - } - - const line = TextEditor.getLineAt(position).text; - const char = line[position.character]; - - /* - From the Vim manual: - - Special case: "cw" and "cW" are treated like "ce" and "cE" if the cursor is - on a non-blank. This is because "cw" is interpreted as change-word, and a - word does not include the following white space. - */ - - if (" \t".indexOf(char) >= 0) { - return position.getWordRight(); - } else { - return position.getCurrentWordEnd(true).getRight(); - } - } else { - return position.getWordRight(); - } - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - const result = await this.execAction(position, vimState, true); - - /* - From the Vim documentation: - - Another special case: When using the "w" motion in combination with an - operator and the last word moved over is at the end of a line, the end of - that word becomes the end of the operated text, not the first word in the - next line. - */ - - if (result.line > position.line + 1 || (result.line === position.line + 1 && result.isFirstWordOfLine())) { - return position.getLineEnd(); - } - - if (result.isLineEnd()) { - return new Position(result.line, result.character + 1); - } - - return result; - } -} - -@RegisterAction -class MoveFullWordBegin extends BaseMovement { - keys = ["W"]; - - public async execAction(position: Position, vimState: VimState): Promise { - if (vimState.recordedState.operator instanceof ChangeOperator) { - // TODO use execForOperator? Or maybe dont? - - // See note for w - return position.getCurrentBigWordEnd().getRight(); - } else { - return position.getBigWordRight(); - } - } -} - -@RegisterAction -class MoveWordEnd extends BaseMovement { - keys = ["e"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getCurrentWordEnd(); - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - let end = position.getCurrentWordEnd(); - - return new Position(end.line, end.character + 1); - } -} - -@RegisterAction -class MoveFullWordEnd extends BaseMovement { - keys = ["E"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getCurrentBigWordEnd(); - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - return position.getCurrentBigWordEnd().getRight(); - } -} - -@RegisterAction -class MoveLastWordEnd extends BaseMovement { - keys = ["g", "e"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLastWordEnd(); - } -} - -@RegisterAction -class MoveLastFullWordEnd extends BaseMovement { - keys = ["g", "E"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getLastBigWordEnd(); - } -} - -@RegisterAction -class MoveBeginningWord extends BaseMovement { - keys = ["b"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getWordLeft(); - } -} - -@RegisterAction -class MoveBeginningFullWord extends BaseMovement { - keys = ["B"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getBigWordLeft(); - } -} - -@RegisterAction -class MovePreviousSentenceBegin extends BaseMovement { - keys = ["("]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getSentenceBegin({forward: false}); - } -} - -@RegisterAction -class MoveNextSentenceBegin extends BaseMovement { - keys = [")"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getSentenceBegin({forward: true}); - } -} - -@RegisterAction -class MoveParagraphEnd extends BaseMovement { - keys = ["}"]; - - public async execAction(position: Position, vimState: VimState): Promise { - const isLineWise = position.isLineBeginning() && vimState.currentMode === ModeName.Normal && vimState.recordedState.operator; - let paragraphEnd = position.getCurrentParagraphEnd(); - vimState.currentRegisterMode = isLineWise ? RegisterMode.LineWise : RegisterMode.FigureItOutFromCurrentMode; - return (isLineWise ? paragraphEnd.getLeftThroughLineBreaks(true) : paragraphEnd); - } -} - -@RegisterAction -class MoveParagraphBegin extends BaseMovement { - keys = ["{"]; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getCurrentParagraphBeginning(); - } -} - -abstract class MoveSectionBoundary extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - boundary: string; - forward: boolean; - - public async execAction(position: Position, vimState: VimState): Promise { - return position.getSectionBoundary({ - forward: this.forward, - boundary: this.boundary - }); - } -} - -@RegisterAction -class MoveNextSectionBegin extends MoveSectionBoundary { - keys = ["]", "]"]; - boundary = "{"; - forward = true; -} - -@RegisterAction -class MoveNextSectionEnd extends MoveSectionBoundary { - keys = ["]", "["]; - boundary = "}"; - forward = true; -} - -@RegisterAction -class MovePreviousSectionBegin extends MoveSectionBoundary { - keys = ["[", "["]; - boundary = "{"; - forward = false; -} - -@RegisterAction -class MovePreviousSectionEnd extends MoveSectionBoundary { - keys = ["[", "]"]; - boundary = "}"; - forward = false; -} - -@RegisterAction -class MoveToMatchingBracket extends BaseMovement { - keys = ["%"]; - - public async execAction(position: Position, vimState: VimState): Promise { - position = position.getLeftIfEOL(); - - const text = TextEditor.getLineAt(position).text; - const charToMatch = text[position.character]; - const toFind = PairMatcher.pairings[charToMatch]; - const failure = { start: position, stop: position, failed: true }; - - if (!toFind || !toFind.matchesWithPercentageMotion) { - // If we're not on a match, go right until we find a - // pairable character or hit the end of line. - - for (let i = position.character; i < text.length; i++) { - if (PairMatcher.pairings[text[i]]) { - // We found an opening char, now move to the matching closing char - const openPosition = new Position(position.line, i); - const result = PairMatcher.nextPairedChar(openPosition, text[i], true); - - if (!result) { return failure; } - return result; - } - } - - return failure; - } - - const result = PairMatcher.nextPairedChar(position, charToMatch, true); - if (!result) { return failure; } - return result; - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - const result = await this.execAction(position, vimState); - - if (isIMovement(result)) { - if (result.failed) { - return result; - } else { - throw new Error("Did not ever handle this case!"); - } - } - - if (position.compareTo(result) > 0) { - return { - start: result, - stop: position.getRight(), - }; - } else { - return result.getRight(); - } - } - - public async execActionWithCount(position: Position, vimState: VimState, count: number): Promise { - // % has a special mode that lets you use it to jump to a percentage of the file - // However, some other bracket motions inherit from this so only do this behavior for % explicitly - if (Object.getPrototypeOf(this) === MoveToMatchingBracket.prototype) { - if (count === 0) { - if (vimState.recordedState.operator) { - return this.execActionForOperator(position, vimState); - } else { - return this.execAction(position, vimState); - } - } - - // Check to make sure this is a valid percentage - if (count < 0 || count > 100) { - return { start: position, stop: position, failed: true }; - } - - const targetLine = Math.round((count * TextEditor.getLineCount()) / 100); - return new Position(targetLine - 1, 0).getFirstLineNonBlankChar(); - } else { - return super.execActionWithCount(position, vimState, count); - } - } -} - -export abstract class MoveInsideCharacter extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - protected charToMatch: string; - protected includeSurrounding = false; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const text = TextEditor.getLineAt(position).text; - const closingChar = PairMatcher.pairings[this.charToMatch].match; - const closedMatch = text[position.character] === closingChar; - - // First, search backwards for the opening character of the sequence - let startPos = PairMatcher.nextPairedChar(position, closingChar, closedMatch); - if (startPos === undefined) { return failure; } - - let startPlusOne: Position; - - if (startPos.isAfterOrEqual(startPos.getLineEnd().getLeft())) { - startPlusOne = new Position(startPos.line + 1, 0); - } else { - startPlusOne = new Position(startPos.line, startPos.character + 1); - } - - let endPos = PairMatcher.nextPairedChar(startPlusOne, this.charToMatch, false); - if (endPos === undefined) { return failure; } - - if (this.includeSurrounding) { - if (vimState.currentMode !== ModeName.Visual) { - endPos = new Position(endPos.line, endPos.character + 1); - } - } else { - startPos = startPlusOne; - if (vimState.currentMode === ModeName.Visual) { - endPos = endPos.getLeftThroughLineBreaks(); - } - } - - // If the closing character is the first on the line, don't swallow it. - if (!this.includeSurrounding) { - if (endPos.getLeft().isInLeadingWhitespace()) { - endPos = endPos.getLineBegin(); - } - } - - if (position.isBefore(startPos)) { - vimState.recordedState.operatorPositionDiff = startPos.subtract(position); - } - - return { - start: startPos, - stop: endPos, - diff: new PositionDiff(0, startPos === position ? 1 : 0) - }; - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - const result = await this.execAction(position, vimState); - if (isIMovement(result)) { - if (result.failed) { - vimState.recordedState.hasRunOperator = false; - vimState.recordedState.actionsRun = []; - } - } - return result; - } -} - -@RegisterAction -class MoveIParentheses extends MoveInsideCharacter { - keys = ["i", "("]; - charToMatch = "("; -} - -@RegisterAction -class MoveIClosingParentheses extends MoveInsideCharacter { - keys = ["i", ")"]; - charToMatch = "("; -} - -@RegisterAction -class MoveIClosingParenthesesBlock extends MoveInsideCharacter { - keys = ["i", "b"]; - charToMatch = "("; -} - -@RegisterAction -export class MoveAParentheses extends MoveInsideCharacter { - keys = ["a", "("]; - charToMatch = "("; - includeSurrounding = true; -} - -@RegisterAction -class MoveAClosingParentheses extends MoveInsideCharacter { - keys = ["a", ")"]; - charToMatch = "("; - includeSurrounding = true; -} - -@RegisterAction -class MoveAParenthesesBlock extends MoveInsideCharacter { - keys = ["a", "b"]; - charToMatch = "("; - includeSurrounding = true; -} - -@RegisterAction -class MoveICurlyBrace extends MoveInsideCharacter { - keys = ["i", "{"]; - charToMatch = "{"; -} - -@RegisterAction -class MoveIClosingCurlyBrace extends MoveInsideCharacter { - keys = ["i", "}"]; - charToMatch = "{"; -} - -@RegisterAction -class MoveIClosingCurlyBraceBlock extends MoveInsideCharacter { - keys = ["i", "B"]; - charToMatch = "{"; -} - -@RegisterAction -export class MoveACurlyBrace extends MoveInsideCharacter { - keys = ["a", "{"]; - charToMatch = "{"; - includeSurrounding = true; -} - -@RegisterAction -export class MoveAClosingCurlyBrace extends MoveInsideCharacter { - keys = ["a", "}"]; - charToMatch = "{"; - includeSurrounding = true; -} - -@RegisterAction -class MoveAClosingCurlyBraceBlock extends MoveInsideCharacter { - keys = ["a", "B"]; - charToMatch = "{"; - includeSurrounding = true; -} - -@RegisterAction -class MoveICaret extends MoveInsideCharacter { - keys = ["i", "<"]; - charToMatch = "<"; -} - -@RegisterAction -class MoveIClosingCaret extends MoveInsideCharacter { - keys = ["i", ">"]; - charToMatch = "<"; -} - -@RegisterAction -export class MoveACaret extends MoveInsideCharacter { - keys = ["a", "<"]; - charToMatch = "<"; - includeSurrounding = true; -} - -@RegisterAction -class MoveAClosingCaret extends MoveInsideCharacter { - keys = ["a", ">"]; - charToMatch = "<"; - includeSurrounding = true; -} - -@RegisterAction -class MoveISquareBracket extends MoveInsideCharacter { - keys = ["i", "["]; - charToMatch = "["; -} - -@RegisterAction -class MoveIClosingSquareBraket extends MoveInsideCharacter { - keys = ["i", "]"]; - charToMatch = "["; -} - -@RegisterAction -export class MoveASquareBracket extends MoveInsideCharacter { - keys = ["a", "["]; - charToMatch = "["; - includeSurrounding = true; -} - -@RegisterAction -class MoveAClosingSquareBracket extends MoveInsideCharacter { - keys = ["a", "]"]; - charToMatch = "["; - includeSurrounding = true; -} - -export abstract class MoveQuoteMatch extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; - protected charToMatch: string; - protected includeSurrounding = false; - - public async execAction(position: Position, vimState: VimState): Promise { - const text = TextEditor.getLineAt(position).text; - const quoteMatcher = new QuoteMatcher(this.charToMatch, text); - const start = quoteMatcher.findOpening(position.character); - const end = quoteMatcher.findClosing(start + 1); - - if (start === -1 || end === -1 || end === start || end < position.character) { - return { - start: position, - stop: position, - failed: true - }; - } - - let startPos = new Position(position.line, start); - let endPos = new Position(position.line, end); - - if (!this.includeSurrounding) { - startPos = startPos.getRight(); - endPos = endPos.getLeft(); - } - - if (position.isBefore(startPos)) { - vimState.recordedState.operatorPositionDiff = startPos.subtract(position); - } - - return { - start: startPos, - stop: endPos - }; - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - const result = await this.execAction(position, vimState); - if (isIMovement(result)) { - if (result.failed) { - vimState.recordedState.hasRunOperator = false; - vimState.recordedState.actionsRun = []; - } else { - result.stop = result.stop.getRight(); - } - } - return result; - } -} - -@RegisterAction -class MoveInsideSingleQuotes extends MoveQuoteMatch { - keys = ["i", "'"]; - charToMatch = "'"; - includeSurrounding = false; -} - -@RegisterAction -export class MoveASingleQuotes extends MoveQuoteMatch { - keys = ["a", "'"]; - charToMatch = "'"; - includeSurrounding = true; -} - -@RegisterAction -class MoveInsideDoubleQuotes extends MoveQuoteMatch { - keys = ["i", "\""]; - charToMatch = "\""; - includeSurrounding = false; -} - -@RegisterAction -export class MoveADoubleQuotes extends MoveQuoteMatch { - keys = ["a", "\""]; - charToMatch = "\""; - includeSurrounding = true; -} - -@RegisterAction -class MoveInsideBacktick extends MoveQuoteMatch { - keys = ["i", "`"]; - charToMatch = "`"; - includeSurrounding = false; -} - -@RegisterAction -export class MoveABacktick extends MoveQuoteMatch { - keys = ["a", "`"]; - charToMatch = "`"; - includeSurrounding = true; -} - -@RegisterAction -class MoveToUnclosedRoundBracketBackward extends MoveToMatchingBracket { - keys = ["[", "("]; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const charToMatch = ")"; - const result = PairMatcher.nextPairedChar(position.getLeftThroughLineBreaks(), charToMatch, false); - - if (!result) { return failure; } - return result; - } -} - -@RegisterAction -class MoveToUnclosedRoundBracketForward extends MoveToMatchingBracket { - keys = ["]", ")"]; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const charToMatch = "("; - const result = PairMatcher.nextPairedChar(position.getRightThroughLineBreaks(), charToMatch, false); - - if (!result) { return failure; } - return result; - } -} - -@RegisterAction -class MoveToUnclosedCurlyBracketBackward extends MoveToMatchingBracket { - keys = ["[", "{"]; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const charToMatch = "}"; - const result = PairMatcher.nextPairedChar(position.getLeftThroughLineBreaks(), charToMatch, false); - - if (!result) { return failure; } - return result; - } -} - -@RegisterAction -class MoveToUnclosedCurlyBracketForward extends MoveToMatchingBracket { - keys = ["]", "}"]; - - public async execAction(position: Position, vimState: VimState): Promise { - const failure = { start: position, stop: position, failed: true }; - const charToMatch = "{"; - const result = PairMatcher.nextPairedChar(position.getRightThroughLineBreaks(), charToMatch, false); - - if (!result) { return failure; } - return result; - } -} - - -abstract class MoveTagMatch extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; - protected includeTag = false; - - public async execAction(position: Position, vimState: VimState): Promise { - const editorText = TextEditor.getText(); - const offset = TextEditor.getOffsetAt(position); - const tagMatcher = new TagMatcher(editorText, offset); - const start = tagMatcher.findOpening(this.includeTag); - const end = tagMatcher.findClosing(this.includeTag); - - if (start === undefined || end === undefined) { - return { - start: position, - stop: position, - failed: true - }; - } - - - let startPosition = start ? TextEditor.getPositionAt(start) : position; - let endPosition = end ? TextEditor.getPositionAt(end) : position; - - - - if (position.isAfter(endPosition)) { - vimState.recordedState.transformations.push({ - type: "moveCursor", - diff: endPosition.subtract(position) - }); - } else if (position.isBefore(startPosition)) { - vimState.recordedState.transformations.push({ - type: "moveCursor", - diff: startPosition.subtract(position) - }); - } - if (start === end) { - if (vimState.recordedState.operator instanceof ChangeOperator) { - vimState.currentMode = ModeName.Insert; - } - return { - start: startPosition, - stop: startPosition, - failed: true - }; - } - return { - start: startPosition, - stop: endPosition.getLeftThroughLineBreaks(true) - }; - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - const result = await this.execAction(position, vimState); - if (isIMovement(result)) { - if (result.failed) { - vimState.recordedState.hasRunOperator = false; - vimState.recordedState.actionsRun = []; - } else { - result.stop = result.stop.getRight(); - } - } - return result; - } -} - -@RegisterAction -export class MoveInsideTag extends MoveTagMatch { - keys = ["i", "t"]; - includeTag = false; -} - -@RegisterAction -export class MoveAroundTag extends MoveTagMatch { - keys = ["a", "t"]; - includeTag = true; -} - -export class ArrowsInInsertMode extends BaseMovement { - modes = [ModeName.Insert]; - keys: string[]; - canBePrefixedWithCount = true; - - public async execAction(position: Position, vimState: VimState): Promise { - // we are in Insert Mode and arrow keys will clear all other actions except the first action, which enters Insert Mode. - // Please note the arrow key movement can be repeated while using `.` but it can't be repeated when using `` in Insert Mode. - vimState.recordedState.actionsRun = [vimState.recordedState.actionsRun.shift()!, vimState.recordedState.actionsRun.pop()!]; - let newPosition: Position = position; - - switch (this.keys[0]) { - case "": - newPosition = (await new MoveUpArrow().execAction(position, vimState)); - break; - case "": - newPosition = (await new MoveDownArrow().execAction(position, vimState)); - break; - case "": - newPosition = await new MoveLeftArrow().execAction(position, vimState); - break; - case "": - newPosition = await new MoveRightArrow().execAction(position, vimState); - break; - default: - break; - } - vimState.replaceState = new ReplaceState(newPosition); - return newPosition; - } -} - -@RegisterAction -class UpArrowInInsertMode extends ArrowsInInsertMode { - keys = [""]; -} - -@RegisterAction -class DownArrowInInsertMode extends ArrowsInInsertMode { - keys = [""]; -} - -@RegisterAction -class LeftArrowInInsertMode extends ArrowsInInsertMode { - keys = [""]; -} - -@RegisterAction -class RightArrowInInsertMode extends ArrowsInInsertMode { - keys = [""]; -} \ No newline at end of file diff --git a/src/actions/operator.ts b/src/actions/operator.ts deleted file mode 100644 index 929939295e8..00000000000 --- a/src/actions/operator.ts +++ /dev/null @@ -1,871 +0,0 @@ -import * as vscode from 'vscode'; -import { VimState } from './../mode/modeHandler'; -import { Register, RegisterMode } from './../register/register'; -import { Position, PositionDiff } from './../common/motion/position'; -import { Range } from './../common/motion/range'; -import { ModeName } from './../mode/mode'; -import { TextEditor } from './../textEditor'; -import { Configuration } from './../configuration/configuration'; -import { TextObjectMovement } from './textobject'; -import { - BaseAction, RegisterAction, compareKeypressSequence -} from './base'; -import { CommandNumber } from "./commands/actions"; - -export class BaseOperator extends BaseAction { - constructor(multicursorIndex?: number) { - super(); - this.multicursorIndex = multicursorIndex; - } - canBeRepeatedWithDot = true; - isOperator = true; - - /** - * If this is being run in multi cursor mode, the index of the cursor - * this operator is being applied to. - */ - multicursorIndex: number | undefined = undefined; - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - if (this.doesRepeatedOperatorApply(vimState, keysPressed)) { - return true; - } - if (this.modes.indexOf(vimState.currentMode) === -1) { return false; } - if (!compareKeypressSequence(this.keys, keysPressed)) { return false; } - if (vimState.recordedState.getCurrentCommandWithoutCountPrefix().length - keysPressed.length > 0 && - this.mustBeFirstKey) { return false; } - if (this instanceof BaseOperator && vimState.recordedState.operator) { return false; } - - return true; - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - if (this.modes.indexOf(vimState.currentMode) === -1) { return false; } - if (!compareKeypressSequence(this.keys.slice(0, keysPressed.length), keysPressed)) { return false; } - if (vimState.recordedState.getCurrentCommandWithoutCountPrefix().length - keysPressed.length > 0 && - this.mustBeFirstKey) { return false; } - if (this instanceof BaseOperator && vimState.recordedState.operator) { return false; } - - return true; - } - - public doesRepeatedOperatorApply(vimState: VimState, keysPressed: string[]) { - const nonCountActions = vimState.recordedState.actionsRun.filter(x => !(x instanceof CommandNumber)); - const prevAction = nonCountActions[nonCountActions.length - 1]; - return this.isOperator && keysPressed.length === 1 && prevAction - && this.modes.indexOf(vimState.currentMode) !== -1 - // The previous action is the same as the one we're testing - && prevAction.constructor === this.constructor - // The key pressed is the same as the previous action's last key. - && compareKeypressSequence(prevAction.keysPressed.slice(-1), keysPressed); - } - - /** - * Run this operator on a range, returning the new location of the cursor. - */ - run(vimState: VimState, start: Position, stop: Position): Promise { - throw new Error("You need to override this!"); - } - - runRepeat(vimState: VimState, position: Position, count: number): Promise { - vimState.currentRegisterMode = RegisterMode.LineWise; - return this.run(vimState, position.getLineBegin(), position.getDownByCount(Math.max(0, count - 1)).getLineEnd()); - } -} - - -@RegisterAction -export class DeleteOperator extends BaseOperator { - public keys = ["d"]; - public modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - - /** - * Deletes from the position of start to 1 past the position of end. - */ - public async delete(start: Position, end: Position, currentMode: ModeName, - registerMode: RegisterMode, vimState: VimState, yank = true): Promise { - if (registerMode === RegisterMode.LineWise) { - start = start.getLineBegin(); - end = end.getLineEnd(); - } - - end = new Position(end.line, end.character + 1); - - const isOnLastLine = end.line === TextEditor.getLineCount() - 1; - - // Vim does this weird thing where it allows you to select and delete - // the newline character, which it places 1 past the last character - // in the line. Here we interpret a character position 1 past the end - // as selecting the newline character. Don't allow this in visual block mode - if (vimState.currentMode !== ModeName.VisualBlock) { - if (end.character === TextEditor.getLineAt(end).text.length + 1) { - end = end.getDown(0); - } - } - - let text = vimState.editor.document.getText(new vscode.Range(start, end)); - - // If we delete linewise to the final line of the document, we expect the line - // to be removed. This is actually a special case because the newline - // character we've selected to delete is the newline on the end of the document, - // but we actually delete the newline on the second to last line. - - // Just writing about this is making me more confused. -_- - - // rebornix: johnfn's description about this corner case is perfectly correct. The only catch is - // that we definitely don't want to put the EOL in the register. So here we run the `getText` - // expression first and then update the start position. - - // Now rebornix is confused as well. - if (isOnLastLine && - start.line !== 0 && - registerMode === RegisterMode.LineWise) { - start = start.getPreviousLineBegin().getLineEnd(); - } - - if (registerMode === RegisterMode.LineWise) { - // slice final newline in linewise mode - linewise put will add it back. - text = text.endsWith("\r\n") ? text.slice(0, -2) : (text.endsWith('\n') ? text.slice(0, -1) : text); - } - - if (yank) { - Register.put(text, vimState, this.multicursorIndex); - } - - let diff = new PositionDiff(0, 0); - let resultingPosition: Position; - - if (currentMode === ModeName.Visual) { - resultingPosition = Position.EarlierOf(start, end); - } - - if (start.character > TextEditor.getLineAt(start).text.length) { - resultingPosition = start.getLeft(); - diff = new PositionDiff(0, -1); - } else { - resultingPosition = start; - } - - if (registerMode === RegisterMode.LineWise) { - resultingPosition = resultingPosition.getLineBegin(); - diff = PositionDiff.NewBOLDiff(); - } - - vimState.recordedState.transformations.push({ - type : "deleteRange", - range : new Range(start, end), - diff : diff, - }); - - return resultingPosition; - } - - public async run(vimState: VimState, start: Position, end: Position, yank = true): Promise { - let newPos = await this.delete(start, end, vimState.currentMode, vimState.effectiveRegisterMode(), vimState, yank); - - vimState.currentMode = ModeName.Normal; - if (vimState.currentMode === ModeName.Visual) { - vimState.desiredColumn = newPos.character; - } - return vimState; - } - -} - -@RegisterAction -export class DeleteOperatorVisual extends BaseOperator { - public keys = ["D"]; - public modes = [ModeName.Visual, ModeName.VisualLine]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - // ensures linewise deletion when in visual mode - // see special case in DeleteOperator.delete() - vimState.currentRegisterMode = RegisterMode.LineWise; - - return await new DeleteOperator(this.multicursorIndex).run(vimState, start, end); - } -} - -@RegisterAction -export class YankOperator extends BaseOperator { - public keys = ["y"]; - public modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - canBeRepeatedWithDot = false; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - // Hack to make Surround with y (which takes a motion) work. - - if (vimState.surround) { - vimState.surround.range = new Range(start, end); - vimState.currentMode = ModeName.SurroundInputMode; - vimState.cursorPosition = start; - vimState.cursorStartPosition = start; - - return vimState; - } - - const originalMode = vimState.currentMode; - - if (start.isEarlierThan(end)) { - end = new Position(end.line, end.character + 1); - } else { - [start, end] = [end, start]; - - end = new Position(end.line, end.character + 1); - } - if (vimState.currentRegisterMode === RegisterMode.LineWise) { - start = start.getLineBegin(); - end = end.getLineEnd(); - } - - let text = TextEditor.getText(new vscode.Range(start, end)); - - // If we selected the newline character, add it as well. - if (vimState.currentMode === ModeName.Visual && - end.character === TextEditor.getLineAt(end).text.length + 1) { - text = text + "\n"; - } - - Register.put(text, vimState, this.multicursorIndex); - - vimState.currentMode = ModeName.Normal; - vimState.cursorStartPosition = start; - - // Only change cursor position if we ran a text object movement - let moveCursor = false; - if (vimState.recordedState.actionsRun.length > 1) { - if (vimState.recordedState.actionsRun[1] instanceof TextObjectMovement) { - moveCursor = true; - } - } - - if (originalMode === ModeName.Normal && !moveCursor) { - vimState.allCursors = vimState.cursorPositionJustBeforeAnythingHappened.map(x => new Range(x, x)); - } else { - vimState.cursorPosition = start; - } - - return vimState; - } -} - -@RegisterAction -export class ShiftYankOperatorVisual extends BaseOperator { - public keys = ["Y"]; - public modes = [ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - vimState.currentRegisterMode = RegisterMode.LineWise; - - return await new YankOperator().run(vimState, start, end); - - } -} - -@RegisterAction -export class DeleteOperatorXVisual extends BaseOperator { - public keys = [["x"], [""]]; - public modes = [ModeName.Visual, ModeName.VisualLine]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - return await new DeleteOperator(this.multicursorIndex).run(vimState, start, end); - } -} - -@RegisterAction -export class ChangeOperatorSVisual extends BaseOperator { - public keys = ["s"]; - public modes = [ModeName.Visual, ModeName.VisualLine]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - return await new ChangeOperator().run(vimState, start, end); - } -} - -@RegisterAction -export class FormatOperator extends BaseOperator { - public keys = ["="]; - public modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - vimState.editor.selection = new vscode.Selection(start, end); - await vscode.commands.executeCommand("editor.action.formatSelection"); - let line = vimState.cursorStartPosition.line; - - if (vimState.cursorStartPosition.isAfter(vimState.cursorPosition)) { - line = vimState.cursorPosition.line; - } - - let newCursorPosition = new Position(line, 0); - vimState.cursorPosition = newCursorPosition; - vimState.cursorStartPosition = newCursorPosition; - vimState.currentMode = ModeName.Normal; - return vimState; - } -} - -@RegisterAction -export class UpperCaseOperator extends BaseOperator { - public keys = ["U"]; - public modes = [ModeName.Visual, ModeName.VisualLine]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - const range = new vscode.Range(start, new Position(end.line, end.character + 1)); - let text = vimState.editor.document.getText(range); - - await TextEditor.replace(range, text.toUpperCase()); - - vimState.currentMode = ModeName.Normal; - vimState.cursorPosition = start; - - return vimState; - } -} - -@RegisterAction -export class UpperCaseWithMotion extends UpperCaseOperator { - public keys = ["g", "U"]; - public modes = [ModeName.Normal]; -} - -@RegisterAction -class UpperCaseVisualBlockOperator extends BaseOperator { - public keys = ["U"]; - public modes = [ModeName.VisualBlock]; - - public async run(vimState: VimState, startPos: Position, endPos: Position): Promise { - for (const { start, end } of Position.IterateLine(vimState)) { - const range = new vscode.Range(start, end); - let text = vimState.editor.document.getText(range); - await TextEditor.replace(range, text.toUpperCase()); - } - - const cursorPosition = startPos.isBefore(endPos) ? startPos : endPos; - vimState.cursorPosition = cursorPosition; - vimState.cursorStartPosition = cursorPosition; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -export class LowerCaseOperator extends BaseOperator { - public keys = ["u"]; - public modes = [ModeName.Visual, ModeName.VisualLine]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - const range = new vscode.Range(start, new Position(end.line, end.character + 1)); - let text = vimState.editor.document.getText(range); - - await TextEditor.replace(range, text.toLowerCase()); - - vimState.currentMode = ModeName.Normal; - vimState.cursorPosition = start; - - return vimState; - } -} - -@RegisterAction -export class LowerCaseWithMotion extends LowerCaseOperator { - public keys = ["g", "u"]; - public modes = [ModeName.Normal]; -} - - -@RegisterAction -class LowerCaseVisualBlockOperator extends BaseOperator { - public keys = ["u"]; - public modes = [ModeName.VisualBlock]; - - public async run(vimState: VimState, startPos: Position, endPos: Position): Promise { - for (const { start, end } of Position.IterateLine(vimState)) { - const range = new vscode.Range(start, end); - let text = vimState.editor.document.getText(range); - await TextEditor.replace(range, text.toLowerCase()); - } - - const cursorPosition = startPos.isBefore(endPos) ? startPos : endPos; - vimState.cursorPosition = cursorPosition; - vimState.cursorStartPosition = cursorPosition; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class IndentOperator extends BaseOperator { - modes = [ModeName.Normal]; - keys = [">"]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - vimState.editor.selection = new vscode.Selection(start.getLineBegin(), end.getLineEnd()); - - await vscode.commands.executeCommand("editor.action.indentLines"); - - vimState.currentMode = ModeName.Normal; - vimState.cursorPosition = start.getFirstLineNonBlankChar(); - - return vimState; - } -} - -/** - * `3>` to indent a line 3 times in visual mode is actually a bit of a special case. - * - * > is an operator, and generally speaking, you don't run operators multiple times, you run motions multiple times. - * e.g. `d3w` runs `w` 3 times, then runs d once. - * - * Same with literally every other operator motion combination... until `3>`in visual mode - * walked into my life. - */ -@RegisterAction -class IndentOperatorInVisualModesIsAWeirdSpecialCase extends BaseOperator { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = [">"]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - for (let i = 0; i < (vimState.recordedState.count || 1); i++) { - await vscode.commands.executeCommand("editor.action.indentLines"); - } - - vimState.currentMode = ModeName.Normal; - vimState.cursorPosition = start.getFirstLineNonBlankChar(); - - return vimState; - } -} - -@RegisterAction -class OutdentOperator extends BaseOperator { - modes = [ModeName.Normal]; - keys = ["<"]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - vimState.editor.selection = new vscode.Selection(start, end); - - await vscode.commands.executeCommand("editor.action.outdentLines"); - vimState.currentMode = ModeName.Normal; - vimState.cursorPosition = start.getFirstLineNonBlankChar(); - - return vimState; - } -} - -/** - * See comment for IndentOperatorInVisualModesIsAWeirdSpecialCase - */ -@RegisterAction -class OutdentOperatorInVisualModesIsAWeirdSpecialCase extends BaseOperator { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ["<"]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - for (let i = 0; i < (vimState.recordedState.count || 1); i++) { - await vscode.commands.executeCommand("editor.action.outdentLines"); - } - - vimState.currentMode = ModeName.Normal; - vimState.cursorPosition = start.getFirstLineNonBlankChar(); - - return vimState; - } -} - - -@RegisterAction -export class ChangeOperator extends BaseOperator { - public keys = ["c"]; - public modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - const isEndOfLine = end.character === end.getLineEnd().character; - vimState = await new YankOperator(this.multicursorIndex).run(vimState, start, end); - // which means the insert cursor would be one to the left of the end of - // the line. We do want to run delete if it is a multiline change though ex. c} - vimState.currentRegisterMode = RegisterMode.CharacterWise; - if (Position.getLineLength(TextEditor.getLineAt(start).lineNumber) !== 0 || (end.line !== start.line)) { - if (isEndOfLine) { - vimState = await new DeleteOperator(this.multicursorIndex).run(vimState, start, end.getLeftThroughLineBreaks(), false); - } else { - vimState = await new DeleteOperator(this.multicursorIndex).run(vimState, start, end, false); - } - } - vimState.currentRegisterMode = RegisterMode.FigureItOutFromCurrentMode; - - - vimState.currentMode = ModeName.Insert; - - if (isEndOfLine) { - vimState.cursorPosition = end.getRight(); - } - - return vimState; - } - - public async runRepeat(vimState: VimState, position: Position, count: number): Promise { - const lineIsAllWhitespace = TextEditor.getLineAt(position).text.trim() === ""; - vimState.currentRegisterMode = RegisterMode.LineWise; - if (lineIsAllWhitespace) { - return this.run(vimState, position.getLineBegin(), position.getDownByCount(Math.max(0, count - 1)).getLineEnd()); - } else { - return this.run(vimState, position.getLineBeginRespectingIndent(), position.getDownByCount(Math.max(0, count - 1)).getLineEnd()); - } - } -} - -@RegisterAction -export class YankVisualBlockMode extends BaseOperator { - public keys = ["y"]; - public modes = [ModeName.VisualBlock]; - canBeRepeatedWithDot = false; - runsOnceForEveryCursor() { return false; } - - public async run(vimState: VimState, start: Position, end: Position): Promise { - let toCopy: string = ""; - - for (const { line } of Position.IterateLine(vimState)) { - toCopy += line + '\n'; - } - - Register.put(toCopy, vimState, this.multicursorIndex); - - vimState.currentMode = ModeName.Normal; - vimState.cursorPosition = start; - return vimState; - } -} - - -@RegisterAction -export class ToggleCaseOperator extends BaseOperator { - public keys = ["~"]; - public modes = [ModeName.Visual, ModeName.VisualLine]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - const range = new vscode.Range(start, end.getRight()); - - await ToggleCaseOperator.toggleCase(range); - - const cursorPosition = start.isBefore(end) ? start : end; - vimState.cursorPosition = cursorPosition; - vimState.cursorStartPosition = cursorPosition; - vimState.currentMode = ModeName.Normal; - - return vimState; - } - - static async toggleCase(range: vscode.Range) { - const text = TextEditor.getText(range); - - let newText = ""; - for (var i = 0; i < text.length; i++) { - var char = text[i]; - // Try lower-case - let toggled = char.toLocaleLowerCase(); - if (toggled === char) { - // Try upper-case - toggled = char.toLocaleUpperCase(); - } - newText += toggled; - } - await TextEditor.replace(range, newText); - } -} - -@RegisterAction -class ToggleCaseVisualBlockOperator extends BaseOperator { - public keys = ["~"]; - public modes = [ModeName.VisualBlock]; - - public async run(vimState: VimState, startPos: Position, endPos: Position): Promise { - for (const { start, end } of Position.IterateLine(vimState)) { - const range = new vscode.Range(start, end); - await ToggleCaseOperator.toggleCase(range); - } - - const cursorPosition = startPos.isBefore(endPos) ? startPos : endPos; - vimState.cursorPosition = cursorPosition; - vimState.cursorStartPosition = cursorPosition; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -class ToggleCaseWithMotion extends ToggleCaseOperator { - public keys = ["g", "~"]; - public modes = [ModeName.Normal]; -} - - -@RegisterAction -export class CommentOperator extends BaseOperator { - public keys = ["g", "c"]; - public modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - vimState.editor.selection = new vscode.Selection(start.getLineBegin(), end.getLineEnd()); - await vscode.commands.executeCommand("editor.action.commentLine"); - - vimState.cursorPosition = new Position(start.line, 0); - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -@RegisterAction -export class CommentBlockOperator extends BaseOperator { - public keys = ["g", "C"]; - public modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - - public async run(vimState: VimState, start: Position, end: Position): Promise { - const endPosition = vimState.currentMode === ModeName.Normal ? end.getRight() : end; - vimState.editor.selection = new vscode.Selection(start, endPosition); - await vscode.commands.executeCommand("editor.action.blockComment"); - - vimState.cursorPosition = start; - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} - -interface CommentTypeSingle { - singleLine: true; - - start: string; -} - -interface CommentTypeMultiLine { - singleLine: false; - - start: string; - inner: string; - final: string; -} - -type CommentType = CommentTypeSingle | CommentTypeMultiLine; - -@RegisterAction -class ActionVisualReflowParagraph extends BaseOperator { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; - keys = ["g", "q"]; - - public static CommentTypes: CommentType[] = [ - { singleLine: false, start: "/**", inner: "*", final: "*/" }, - { singleLine: false, start: "/*", inner: "*", final: "*/" }, - { singleLine: false, start: "{-", inner: "-", final: "-}" }, - { singleLine: true, start: "///"}, - { singleLine: true, start: "//" }, - { singleLine: true, start: "--" }, - { singleLine: true, start: "#" }, - { singleLine: true, start: ";" }, - { singleLine: true, start: "*" }, - - // Needs to come last, since everything starts with the emtpy string! - { singleLine: true, start: "" }, - ]; - - public getIndentationLevel(s: string): number { - for (const line of s.split("\n")) { - const result = line.match(/^\s+/g); - const indentLevel = result ? result[0].length : 0; - - if (indentLevel !== line.length) { - return indentLevel; - } - } - - return 0; - } - - public reflowParagraph(s: string, indentLevel: number): string { - const maximumLineLength = Configuration.textwidth - indentLevel - 2; - const indent = Array(indentLevel + 1).join(" "); - - // Chunk the lines by commenting style. - - let chunksToReflow: { - commentType: CommentType; - content: string; - indentLevelAfterComment: number; - }[] = []; - - for (const line of s.split("\n")) { - let lastChunk: { commentType: CommentType; content: string} | undefined = chunksToReflow[chunksToReflow.length - 1]; - const trimmedLine = line.trim(); - - // See what comment type they are using. - - let commentType: CommentType | undefined; - - for (const type of ActionVisualReflowParagraph.CommentTypes) { - if (line.trim().startsWith(type.start)) { - commentType = type; - - break; - } - - // If they're currently in a multiline comment, see if they continued it. - if (lastChunk && type.start === lastChunk.commentType.start && !type.singleLine) { - if (line.trim().startsWith(type.inner)) { - commentType = type; - - break; - } - - if (line.trim().endsWith(type.final)) { - commentType = type; - - break; - } - } - } - - if (!commentType) { break; } // will never happen, just to satisfy typechecker. - - // Did they start a new comment type? - if (!lastChunk || commentType.start !== lastChunk.commentType.start) { - let chunk = { - commentType, - content: `${ trimmedLine.substr(commentType.start.length).trim() }`, - indentLevelAfterComment: 0 - }; - if (commentType.singleLine) { - chunk.indentLevelAfterComment = trimmedLine.substr(commentType.start.length).length - chunk.content.length; - } - chunksToReflow.push(chunk); - - continue; - } - - // Parse out commenting style, gather words. - - lastChunk = chunksToReflow[chunksToReflow.length - 1]; - - if (lastChunk.commentType.singleLine) { // is it a continuation of a comment like "//" - lastChunk.content += `\n${ trimmedLine.substr(lastChunk.commentType.start.length).trim() }`; - - } else { // are we in the middle of a multiline comment like "/*" - if (trimmedLine.endsWith(lastChunk.commentType.final)) { - if (trimmedLine.length > lastChunk.commentType.final.length) { - lastChunk.content += `\n${ trimmedLine.substr( - lastChunk.commentType.inner.length, - trimmedLine.length - lastChunk.commentType.final.length - ).trim() }`; - } - - } else if (trimmedLine.startsWith(lastChunk.commentType.inner)) { - lastChunk.content += `\n${ trimmedLine.substr(lastChunk.commentType.inner.length).trim() }`; - } else if (trimmedLine.startsWith(lastChunk.commentType.start)) { - lastChunk.content += `\n${ trimmedLine.substr(lastChunk.commentType.start.length).trim() }`; - } - } - } - - // Reflow each chunk. - let result: string[] = []; - - for (const { commentType, content, indentLevelAfterComment } of chunksToReflow) { - let lines: string[]; - const indentAfterComment = Array(indentLevelAfterComment + 1).join(" "); - - if (commentType.singleLine) { - lines = [``]; - } else { - lines = [``, ``]; - } - - // This tracks if we're pushing the first line of a chunk. If so, then we - // don't want to add an extra space. In addition, when there's a blank - // line, this needs to be reset. - let curIndex = 0; - for (const line of content.trim().split("\n")) { - // Preserve newlines. - - if (line.trim() === "") { - for (let i = 0; i < 2; i++) { - lines.push(``); - } - curIndex = 0; - - continue; - } - - // Add word by word, wrapping when necessary. - const words = line.split(/\s+/); - for (let i = 0; i < words.length; i++) { - const word = words[i]; - if (word === "") { continue; } - - if (lines[lines.length - 1].length + word.length + 1 < maximumLineLength) { - if (curIndex === 0 && i === 0) { - lines[lines.length - 1] += `${word}`; - } else { - lines[lines.length - 1] += ` ${ word }`; - } - } else { - lines.push(`${ word }`); - } - } - curIndex++; - } - - if (!commentType.singleLine) { - lines.push(``); - } - - if (commentType.singleLine) { - if (lines.length > 1 && lines[0].trim() === "") { lines = lines.slice(1); } - if (lines.length > 1 && lines[lines.length - 1].trim() === "") { lines = lines.slice(0, -1); } - } - - for (let i = 0; i < lines.length; i++) { - if (commentType.singleLine) { - lines[i] = `${ indent }${ commentType.start }${ indentAfterComment }${ lines[i] }`; - } else { - if (i === 0) { - lines[i] = `${ indent }${ commentType.start } ${ lines[i] }`; - } else if (i === lines.length - 1) { - lines[i] = `${ indent } ${ commentType.final }`; - } else { - lines[i] = `${ indent } ${ commentType.inner } ${ lines[i] }`; - } - } - } - - result = result.concat(lines); - } - - // Gather up multiple empty lines into single empty lines. - return result.join("\n"); - } - - public async run(vimState: VimState, start: Position, end: Position): Promise { - start = Position.EarlierOf(start, end); - end = Position.LaterOf(start, end); - - let textToReflow = TextEditor.getText(new vscode.Range(start, end)); - let indentLevel = this.getIndentationLevel(textToReflow); - - textToReflow = this.reflowParagraph(textToReflow, indentLevel); - - vimState.recordedState.transformations.push({ - type: "replaceText", - text: textToReflow, - start: start, - end: end, - // Move cursor to front of line to realign the view - diff: PositionDiff.NewBOLDiff(0, 0) - }); - - vimState.currentMode = ModeName.Normal; - - return vimState; - } -} \ No newline at end of file diff --git a/src/actions/plugins/easymotion/easymotion.cmd.ts b/src/actions/plugins/easymotion/easymotion.cmd.ts deleted file mode 100644 index 2e8d10aadba..00000000000 --- a/src/actions/plugins/easymotion/easymotion.cmd.ts +++ /dev/null @@ -1,320 +0,0 @@ -import { EasyMotion } from './easymotion'; -import { Position } from './../../../common/motion/position'; -import { ModeName } from './../../../mode/mode'; -import { Configuration } from './../../../configuration/configuration'; -import { BaseCommand } from './../../commands/actions'; -import { RegisterAction } from './../../base'; -import { VimState } from './../../../mode/modeHandler'; - -abstract class BaseEasyMotionCommand extends BaseCommand { - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - throw new Error("Not implemented!"); - } - - public getMatchPosition(match: EasyMotion.Match, position: Position, vimState: VimState): Position { - return match.position; - } - - public processMarkers(matches: EasyMotion.Match[], position: Position, vimState: VimState) { - // Clear existing markers, just in case - vimState.easyMotion.clearMarkers(); - - var index = 0; - for (var j = 0; j < matches.length; j++) { - var match = matches[j]; - var pos = this.getMatchPosition(match, position, vimState); - - if (match.position.isEqual(position)) { - continue; - } - - let marker = EasyMotion.generateMarker(index++, matches.length, position, pos); - if (marker) { - vimState.easyMotion.addMarker(marker); - } - } - } - - public async exec(position: Position, vimState: VimState): Promise { - // Only execute the action if the configuration is set - if (!Configuration.easymotion) { - return vimState; - } - - // Search all occurences of the character pressed - let matches = this.getMatches(position, vimState); - - // Stop if there are no matches - if (matches.length === 0) { - return vimState; - } - - // Enter the EasyMotion mode and await further keys - vimState.easyMotion = new EasyMotion(); - - // Store mode to return to after performing easy motion - vimState.easyMotion.previousMode = vimState.currentMode; - - vimState.currentMode = ModeName.EasyMotionMode; - - this.processMarkers(matches, position, vimState); - - return vimState; - } -} - -@RegisterAction -class ActionEasyMotionSearchCommand extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "s", ""]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - const searchChar = this.keysPressed[3]; - - // Search all occurences of the character pressed - if (searchChar === " ") { // Searching for space should only find the first space - return vimState.easyMotion.sortedSearch(position, new RegExp(" {1,}", "g")); - } else { - return vimState.easyMotion.sortedSearch(position, searchChar); - } - } -} - -@RegisterAction -class ActionEasyMotionFindForwardCommand extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "f", ""]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - const searchChar = this.keysPressed[3]; - - // Search all occurences of the character pressed after the cursor - if (searchChar === " ") { // Searching for space should only find the first space - return vimState.easyMotion.sortedSearch(position, new RegExp(" {1,}", "g"), { - min: position - }); - } else { - return vimState.easyMotion.sortedSearch(position, searchChar, { - min: position - }); - } - } -} - -@RegisterAction -class ActionEasyMotionFindBackwardCommand extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "F", ""]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - const searchChar = this.keysPressed[3]; - - // Search all occurences of the character pressed after the cursor - if (searchChar === " ") { // Searching for space should only find the first space - return vimState.easyMotion.sortedSearch(position, new RegExp(" {1,}", "g"), { - max: position - }); - } else { - return vimState.easyMotion.sortedSearch(position, searchChar, { - max: position - }); - } - } -} - -@RegisterAction -class ActionEasyMotionTilForwardCommand extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "t", ""]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - const searchChar = this.keysPressed[3]; - - // Search all occurences of the character pressed after the cursor - if (searchChar === " ") { // Searching for space should only find the first space - return vimState.easyMotion.sortedSearch(position, new RegExp(" {1,}", "g"), { - min: position - }); - } else { - return vimState.easyMotion.sortedSearch(position, searchChar, { - min: position - }); - } - } - - public getMatchPosition(match: EasyMotion.Match, position: Position, vimState: VimState): Position { - return new Position(match.position.line, Math.max(0, match.position.character - 1)); - } -} - -@RegisterAction -class ActionEasyMotionTilBackwardCommand extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "T", ""]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - const searchChar = this.keysPressed[3]; - - // Search all occurences of the character pressed after the cursor - if (searchChar === " ") { // Searching for space should only find the first space - return vimState.easyMotion.sortedSearch(position, new RegExp(" {1,}"), { - max: position - }); - } else { - return vimState.easyMotion.sortedSearch(position, searchChar, { - max: position - }); - } - } - - public getMatchPosition(match: EasyMotion.Match, position: Position, vimState: VimState): Position { - return new Position(match.position.line, Math.max(0, match.position.character + 1)); - } -} - -@RegisterAction -class ActionEasyMotionWordCommand extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "w"]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - // Search for the beginning of all words after the cursor - return vimState.easyMotion.sortedSearch(position, new RegExp("\\w{1,}", "g"), { - min: position - }); - } -} - -@RegisterAction -class ActionEasyMotionEndForwardCommand extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "e"]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - // Search for the end of all words after the cursor - return vimState.easyMotion.sortedSearch(position, new RegExp("\\w{1,}", "g"), { - min: position - }); - } - - public getMatchPosition(match: EasyMotion.Match, position: Position, vimState: VimState): Position { - return new Position(match.position.line, match.position.character + match.text.length - 1); - } -} - -@RegisterAction -class ActionEasyMotionEndBackwardCommand extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "g", "e"]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - // Search for the beginning of all words before the cursor - return vimState.easyMotion.sortedSearch(position, new RegExp("\\w{1,}", "g"), { - max: position, - }); - } - - public getMatchPosition(match: EasyMotion.Match, position: Position, vimState: VimState): Position { - return new Position(match.position.line, match.position.character + match.text.length - 1); - } -} - -@RegisterAction -class ActionEasyMotionBeginningWordCommand extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "b"]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - // Search for the beginning of all words before the cursor - return vimState.easyMotion.sortedSearch(position, new RegExp("\\w{1,}", "g"), { - max: position, - }); - } -} - -@RegisterAction -class ActionEasyMotionDownLines extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "j"]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - // Search for the beginning of all non whitespace chars on each line after the cursor - let matches = vimState.easyMotion.sortedSearch(position, new RegExp("^.", "gm"), { - min: position - }); - - for (let match of matches) { - match.position = match.position.getFirstLineNonBlankChar(); - } - return matches; - } -} - -@RegisterAction -class ActionEasyMotionUpLines extends BaseEasyMotionCommand { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; - keys = ["", "", "k"]; - - public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] { - // Search for the beginning of all non whitespace chars on each line before the cursor - let matches = vimState.easyMotion.sortedSearch(position, new RegExp("^.", "gm"), { - max: position - }); - - for (let match of matches) { - match.position = match.position.getFirstLineNonBlankChar(); - } - return matches; - } -} - -@RegisterAction -class MoveEasyMotion extends BaseCommand { - modes = [ModeName.EasyMotionMode]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - var key = this.keysPressed[0]; - if (!key) { - return vimState; - } - - // "nail" refers to the accumulated depth keys - var nail = vimState.easyMotion.accumulation + key; - vimState.easyMotion.accumulation = nail; - - // Find markers starting with "nail" - var markers = vimState.easyMotion.findMarkers(nail); - - // If previous mode was visual, restore visual selection - if (vimState.easyMotion.previousMode === ModeName.Visual || - vimState.easyMotion.previousMode === ModeName.VisualLine || - vimState.easyMotion.previousMode === ModeName.VisualBlock) { - vimState.cursorStartPosition = vimState.lastVisualSelectionStart; - vimState.cursorPosition = vimState.lastVisualSelectionEnd; - } - - - if (markers.length === 1) { // Only one found, navigate to it - var marker = markers[0]; - - vimState.easyMotion.clearDecorations(); - // Restore the mode from before easy motion - vimState.currentMode = vimState.easyMotion.previousMode; - - // Set cursor position based on marker entered - vimState.cursorPosition = marker.position; - - return vimState; - } else { - if (markers.length === 0) { // None found, exit mode - vimState.easyMotion.clearDecorations(); - vimState.currentMode = vimState.easyMotion.previousMode; - - return vimState; - } - } - - return vimState; - } -} diff --git a/src/actions/plugins/easymotion/easymotion.ts b/src/actions/plugins/easymotion/easymotion.ts deleted file mode 100644 index 943b1b98bab..00000000000 --- a/src/actions/plugins/easymotion/easymotion.ts +++ /dev/null @@ -1,437 +0,0 @@ -import * as vscode from "vscode"; -import { Position } from './../../../common/motion/position'; -import { Configuration } from './../../../configuration/configuration'; -import { TextEditor } from './../../../textEditor'; - -export class EasyMotion { - /** - * Refers to the accumulated keys for depth navigation - */ - public accumulation = ""; - - /** - * Array of all markers and decorations - */ - private markers: EasyMotion.Marker[]; - private visibleMarkers: EasyMotion.Marker[]; // Array of currently showing markers - private decorations: any[][]; - - /** - * TODO: For future motions - */ - private static specialCharactersRegex: RegExp = /[\-\[\]{}()*+?.,\\\^$|#\s]/g; - - /** - * Caches for decorations - */ - private static decorationTypeCache: vscode.TextEditorDecorationType[] = []; - private static svgCache: { [code: string]: vscode.Uri } = {}; - private static cachedBackgroundColor: string = ""; - private static cachedOneFontColor: string = ""; - private static cachedTwoFontColor: string = ""; - private static cachedWidthPerChar: number = -1; - private static cachedHeight: number = -1; - - /** - * The key sequence for marker name generation - */ - public static keyTable = [ - "a", "s", "d", "g", "h", "k", "l", "q", "w", "e", - "r", "t", "y", "u", "i", "o", "p", "z", "x", "c", - "v", "b", "n", "m", "f", "j" - ]; - - /** - * Mode to return to after attempting easymotion - */ - public previousMode: number; - - constructor() { - this.markers = []; - this.visibleMarkers = []; - this.decorations = []; - } - - /** - * Generate a marker following a sequence for the name and depth levels - */ - public static generateMarker(index: number, length: number, position: Position, markerPosition: Position): EasyMotion.Marker | null { - let keyTable = EasyMotion.keyTable; - var availableKeyTable = keyTable.slice(); - - // Depth table should always include a ; - var keyDepthTable = [";"]; - var totalSteps = 0; - - if (length >= keyTable.length) { - var totalRemainder = Math.max(length - keyTable.length, 0); - totalSteps = Math.floor(totalRemainder / keyTable.length); - - for (var i = 0; i < Math.min(totalSteps, 26); i++) { - keyDepthTable.push(availableKeyTable.pop()!); - } - } - - var prefix = ""; - if (index >= availableKeyTable.length) { - // Length of available keys before reset and ";" - var oldLength = availableKeyTable.length; - // The index that remains after taking away the first-level depth markers - var remainder = index - availableKeyTable.length; - - // ";" can be used as the last marker key, when inside a marker with depth. Reset to available keys and add ";" - availableKeyTable = keyTable.slice(); - availableKeyTable.push(";"); - - // Depth index counts down instead of up - var inverted = (length - oldLength - 1 - remainder); - var steps = Math.floor((inverted) / availableKeyTable.length); - - // Add the key to the prefix - if (steps > keyDepthTable.length - 1) { - return null; - } - - prefix += keyDepthTable[steps]; - - // Check if we're on the last depth level - if (steps >= totalSteps) { - // Return the proper key for this index - return new EasyMotion.Marker(prefix + availableKeyTable[remainder % availableKeyTable.length], markerPosition); - } - - // Return the proper index for depths earlier than the last one, including prefix - var num = (availableKeyTable.length - 1 - inverted % availableKeyTable.length) % availableKeyTable.length; - return new EasyMotion.Marker(prefix + availableKeyTable[num], markerPosition); - } - - // Return the last key in the marker, including prefix - return new EasyMotion.Marker(prefix + availableKeyTable[index % availableKeyTable.length], markerPosition); - } - - /** - * Create and cache decoration types for different marker lengths - */ - public static getDecorationType(length: number): vscode.TextEditorDecorationType { - var cache = this.decorationTypeCache[length]; - if (cache) { - return cache; - } - - var width = length * 8; - var type = vscode.window.createTextEditorDecorationType({ - after: { - margin: `0 0 0 -${width}px`, - height: `14px`, - width: `${width}px` - } - }); - - this.decorationTypeCache[length] = type; - - return type; - } - - /** - * Create and cache the SVG data URI for different marker codes and colors - */ - private static getSvgDataUri(code: string, backgroundColor: string, fontFamily: string, fontColor: string, - fontSize: string, fontWeight: string): vscode.Uri { - - // Clear cache if the backgroundColor or fontColor has changed - if (this.cachedBackgroundColor !== backgroundColor) { - this.svgCache = {}; - this.cachedBackgroundColor = backgroundColor; - } - - if (this.cachedOneFontColor !== Configuration.easymotionMarkerForegroundColorOneChar) { - this.svgCache = {}; - this.cachedOneFontColor = Configuration.easymotionMarkerForegroundColorOneChar; - } - - if (this.cachedTwoFontColor !== Configuration.easymotionMarkerForegroundColorTwoChar) { - this.svgCache = {}; - this.cachedTwoFontColor = Configuration.easymotionMarkerForegroundColorTwoChar; - } - - const widthPerChar = Configuration.easymotionMarkerWidthPerChar; - const width = code.length * widthPerChar + 1; - const height = Configuration.easymotionMarkerHeight; - - if (this.cachedWidthPerChar !== widthPerChar || this.cachedHeight !== height) { - this.svgCache = {}; - this.cachedWidthPerChar = width; - this.cachedHeight = height; - } - - if (fontFamily === undefined) { fontFamily = "Consolas"; } - if (fontColor === undefined) { fontColor = "white"; } - if (fontSize === undefined) { fontSize = "14"; } - if (fontWeight === undefined) { fontWeight = "normal"; } - if (backgroundColor === undefined) { backgroundColor = "black"; } - - var cache = this.svgCache[code]; - if (cache) { - return cache; - } - - var uri = vscode.Uri.parse( - `data:image/svg+xml;utf8,${code}`); - - this.svgCache[code] = uri; - - return uri; - } - - /** - * Clear all decorations - */ - public clearDecorations() { - var editor = vscode.window.activeTextEditor!; - for (var i = 1; i <= this.decorations.length; i++) { - editor.setDecorations(EasyMotion.getDecorationType(i), []); - } - } - - /** - * Clear all markers - */ - public clearMarkers() { - this.markers = []; - this.visibleMarkers = []; - } - - public addMarker(marker: EasyMotion.Marker) { - this.markers.push(marker); - } - - public getMarker(index: number): EasyMotion.Marker { - return this.markers[index]; - } - - /** - * Find markers beginning with a string - */ - public findMarkers(nail: string, visible = true): EasyMotion.Marker[] { - var arr = visible ? this.visibleMarkers : this.markers; - var markers: EasyMotion.Marker[] = []; - for (var i = 0; i < arr.length; i++) { - var marker = arr[i]; - - if (marker.name.startsWith(nail)) { - markers.push(marker); - } - } - - return markers; - } - - /** - * Search and sort using the index of a match compared to the index of position (usually the cursor) - */ - public sortedSearch(position: Position, search: string | RegExp = "", options: EasyMotion.SearchOptions = {}): EasyMotion.Match[] { - let regex: RegExp; - if (typeof search === "string") { - // Regex needs to be escaped - regex = new RegExp(search.replace(EasyMotion.specialCharactersRegex, "\\$&"), "g"); - } else { - regex = search; - } - - var matches: EasyMotion.Match[] = []; - - // Cursor index refers to the index of the marker that is on or to the right of the cursor - var cursorIndex = position.character; - var prevMatch: EasyMotion.Match | undefined; - - // Calculate the min/max bounds for the search - var lineCount = TextEditor.getLineCount(); - var lineMin = options.min ? Math.max(options.min.line, 0) : 0; - var lineMax = options.max ? Math.min(options.max.line + 1, lineCount) : lineCount; - - outer: - for (let lineIdx = lineMin; lineIdx < lineMax; lineIdx++) { - const line = TextEditor.getLineAt(new Position(lineIdx, 0)).text; - var result = regex.exec(line); - - while (result) { - if (matches.length >= 1000) { - break outer; - } - - let pos = new Position(lineIdx, result.index); - - // Check if match is within bounds - if ((options.min && pos.isBefore(options.min)) || - (options.max && pos.isAfter(options.max)) || - Math.abs(pos.line - position.line) > 100) { // Stop searching after 100 lines in both directions - - result = regex.exec(line); - continue; - } - - // Update cursor index to the marker on the right side of the cursor - if (!prevMatch || prevMatch.position.isBefore(position)) { - cursorIndex = matches.length; - } - - prevMatch = new EasyMotion.Match( - pos, - result[0], - matches.length - ); - - // Matches on the cursor position should be ignored - if (pos.isEqual(position)) { - result = regex.exec(line); - continue; - } - - matches.push(prevMatch); - - result = regex.exec(line); - } - } - - // Sort by the index distance from the cursor index - matches.sort((a: EasyMotion.Match, b: EasyMotion.Match): number => { - var diffA = cursorIndex - a.index; - var diffB = cursorIndex - b.index; - - var absDiffA = Math.abs(diffA); - var absDiffB = Math.abs(diffB); - - // Prioritize the matches on the right side of the cursor index - if (a.index < cursorIndex) { - absDiffA -= 0.5; - } - if (b.index < cursorIndex) { - absDiffB -= 0.5; - } - - return absDiffA - absDiffB; - }); - - return matches; - } - - - public updateDecorations() { - this.clearDecorations(); - - this.visibleMarkers = []; - this.decorations = []; - - const fontFamily = Configuration.easymotionMarkerFontFamily; - const fontSize = Configuration.easymotionMarkerFontSize; - const fontWeight = Configuration.easymotionMarkerFontWeight; - - for (var i = 0; i < this.markers.length; i++) { - var marker = this.getMarker(i); - - // Ignore markers that do not start with the accumulated depth level - if (!marker.name.startsWith(this.accumulation)) { - continue; - } - - var pos = marker.position; - // Get keys after the depth we're at - var keystroke = marker.name.substr(this.accumulation.length); - - if (!this.decorations[keystroke.length]) { - this.decorations[keystroke.length] = []; - } - - const fontColor = keystroke.length > 1 ? - Configuration.easymotionMarkerForegroundColorTwoChar - : Configuration.easymotionMarkerForegroundColorOneChar; - const backgroundColor = Configuration.easymotionMarkerBackgroundColor; - - // Position should be offsetted by the length of the keystroke to prevent hiding behind the gutter - var charPos = pos.character + 1 + (keystroke.length - 1); - this.decorations[keystroke.length].push({ - range: new vscode.Range(pos.line, charPos, pos.line, charPos), - renderOptions: { - dark: { - after: { - contentIconPath: EasyMotion.getSvgDataUri( - keystroke, backgroundColor, fontFamily, fontColor, fontSize, fontWeight) - } - }, - light: { - after: { - contentIconPath: EasyMotion.getSvgDataUri( - keystroke, backgroundColor, fontFamily, fontColor, fontSize, fontWeight) - } - } - } - }); - - this.visibleMarkers.push(marker); - } - - // Set the decorations for all the different marker lengths - var editor = vscode.window.activeTextEditor!; - for (var j = 1; j < this.decorations.length; j++) { - if (this.decorations[j]) { - editor.setDecorations(EasyMotion.getDecorationType(j), this.decorations[j]); - } - } - } -} - -export module EasyMotion { - export class Marker { - private _name: string; - private _position: Position; - - constructor(name: string, position: Position) { - this._name = name; - this._position = position; - } - - public get name(): string { - return this._name; - } - - public get position(): Position { - return this._position; - } - } - - export class Match { - private _position: Position; - private _text: string; - private _index: number; - - constructor(position: Position, text: string, index: number) { - this._position = position; - this._text = text; - this._index = index; - } - - public get position(): Position { - return this._position; - } - - public get text(): string { - return this._text; - } - - public get index(): number { - return this._index; - } - - public set position(position: Position) { - this._position = position; - } - } - - export interface SearchOptions { - min?: Position; // The minimum bound of the search - max?: Position; // The maximum bound of the search - } -} \ No newline at end of file diff --git a/src/actions/plugins/surround.ts b/src/actions/plugins/surround.ts deleted file mode 100644 index 654d37e737c..00000000000 --- a/src/actions/plugins/surround.ts +++ /dev/null @@ -1,488 +0,0 @@ -import { ModeName } from './../../mode/mode'; -import { Position } from './../../common/motion/position'; -import { Range } from './../../common/motion/range'; -import { TextEditor } from './../../textEditor'; -import { VimState } from './../../mode/modeHandler'; -import { PairMatcher } from './../../common/matching/matcher'; -import { Configuration } from './../../configuration/configuration'; -import { RegisterAction } from './../base'; -import { ChangeOperator, DeleteOperator, YankOperator } from './../operator'; -import { BaseCommand } from './../commands/actions'; -import { BaseMovement } from './../motion'; -import { - IMovement, - MoveQuoteMatch, MoveASingleQuotes, MoveADoubleQuotes, MoveABacktick, MoveInsideCharacter, MoveACurlyBrace, MoveInsideTag, - MoveAParentheses, MoveASquareBracket, MoveACaret, MoveAroundTag -} from './../motion'; -import { - TextObjectMovement, SelectInnerWord, SelectInnerBigWord, SelectInnerSentence, SelectInnerParagraph -} from './../textobject'; - - -@RegisterAction -class CommandSurroundAddTarget extends BaseCommand { - modes = [ModeName.SurroundInputMode]; - keys = [ - ["("], [")"], - ["{"], ["}"], - ["["], ["]"], - ["<"], [">"], - ["'"], ['"'], ["`"], - ["t"], - ["w"], ["W"], ["s"], - ["p"] - ]; - isCompleteAction = false; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - if (!vimState.surround) { - return vimState; - } - - vimState.surround.target = this.keysPressed[this.keysPressed.length - 1]; - - // It's possible we're already done, e.g. dst - await CommandSurroundAddToReplacement.TryToExecuteSurround(vimState, position); - - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && - !!(vimState.surround && vimState.surround.active && - !vimState.surround.target && - !vimState.surround.range); - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && - !!(vimState.surround && vimState.surround.active && - !vimState.surround.target && - !vimState.surround.range); - } -} - -// Aaaaagghhhh. I tried so hard to make surround an operator to make use of our -// sick new operator repeat structure, but there's just no clean way to do it. -// In the future, if somebody wants to refactor Surround, the big problem for -// why it's so weird is that typing `ys` loads up the Yank operator first, -// which prevents us from making a surround operator that's `ys` or something. -// You'd need to refactor our keybinding handling to "give up" keystrokes if it -// can't find a match. - -@RegisterAction -class CommandSurroundModeRepeat extends BaseMovement { - modes = [ModeName.Normal]; - keys = ["s"]; - isCompleteAction = false; - runsOnceForEveryCursor() { return false; } - - public async execAction(position: Position, vimState: VimState): Promise { - return { - start: position.getLineBeginRespectingIndent(), - stop: position.getLineEnd().getLastWordEnd().getRight() - }; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - return super.doesActionApply(vimState, keysPressed) && vimState.surround !== undefined; - } -} - -@RegisterAction -class CommandSurroundModeStart extends BaseCommand { - modes = [ModeName.Normal]; - keys = ["s"]; - isCompleteAction = false; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - // Only execute the action if the configuration is set - if (!Configuration.surround) { - return vimState; - } - - const operator = vimState.recordedState.operator; - let operatorString: "change" | "delete" | "yank" | undefined; - - if (operator instanceof ChangeOperator) { operatorString = "change"; } - if (operator instanceof DeleteOperator) { operatorString = "delete"; } - if (operator instanceof YankOperator) { operatorString = "yank"; } - - if (!operatorString) { return vimState; } - - // Start to record the keys to store for playback of surround using dot - vimState.recordedState.surroundKeys.push(vimState.keyHistory[vimState.keyHistory.length - 2]); - vimState.recordedState.surroundKeys.push("s"); - vimState.recordedState.surroundKeyIndexStart = vimState.keyHistory.length; - - vimState.surround = { - active: true, - target: undefined, - operator: operatorString, - replacement: undefined, - range: undefined, - isVisualLine: false - }; - - if (operatorString !== "yank") { - vimState.currentMode = ModeName.SurroundInputMode; - } - - return vimState; - } - - public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { - const hasSomeOperator = !!vimState.recordedState.operator; - - return super.doesActionApply(vimState, keysPressed) && - hasSomeOperator; - } - - public couldActionApply(vimState: VimState, keysPressed: string[]): boolean { - const hasSomeOperator = !!vimState.recordedState.operator; - - return super.doesActionApply(vimState, keysPressed) && - hasSomeOperator; - } -} - -@RegisterAction -class CommandSurroundModeStartVisual extends BaseCommand { - modes = [ModeName.Visual, ModeName.VisualLine]; - keys = ["S"]; - isCompleteAction = false; - runsOnceForEveryCursor() { return false; } - - public async exec(position: Position, vimState: VimState): Promise { - // Only execute the action if the configuration is set - if (!Configuration.surround) { - return vimState; - } - - // Start to record the keys to store for playback of surround using dot - vimState.recordedState.surroundKeys.push("S"); - vimState.recordedState.surroundKeyIndexStart = vimState.keyHistory.length; - - // Make sure cursor positions are ordered correctly for top->down or down->top selection - if (vimState.cursorStartPosition.line > vimState.cursorPosition.line) { - [vimState.cursorPosition, vimState.cursorStartPosition] = - [vimState.cursorStartPosition, vimState.cursorPosition]; - } - - // Make sure start/end cursor positions are in order - if (vimState.cursorPosition.line < vimState.cursorPosition.line || - (vimState.cursorPosition.line === vimState.cursorStartPosition.line - && vimState.cursorPosition.character < vimState.cursorStartPosition.character)) { - [vimState.cursorPosition, vimState.cursorStartPosition] = [vimState.cursorStartPosition, vimState.cursorPosition]; - } - - vimState.surround = { - active: true, - target: undefined, - operator: "yank", - replacement: undefined, - range: new Range(vimState.cursorStartPosition, vimState.cursorPosition), - isVisualLine: false - }; - - if (vimState.currentMode === ModeName.VisualLine) { - vimState.surround.isVisualLine = true; - } - - vimState.currentMode = ModeName.SurroundInputMode; - vimState.cursorPosition = vimState.cursorStartPosition; - - return vimState; - } -} - - -@RegisterAction -export class CommandSurroundAddToReplacement extends BaseCommand { - modes = [ModeName.SurroundInputMode]; - keys = [""]; - - public async exec(position: Position, vimState: VimState): Promise { - if (!vimState.surround) { - return vimState; - } - - // Backspace modifies the tag entry - if (vimState.surround.replacement !== undefined) { - if (this.keysPressed[this.keysPressed.length - 1] === "" && - vimState.surround.replacement[0] === "<") { - - // Only allow backspace up until the < character - if (vimState.surround.replacement.length > 1) { - vimState.surround.replacement = - vimState.surround.replacement.slice(0, vimState.surround.replacement.length - 1); - } - - return vimState; - } - } - - if (!vimState.surround.replacement) { - vimState.surround.replacement = ""; - } - - let stringToAdd = this.keysPressed[this.keysPressed.length - 1]; - - // t should start creation of a tag - if (this.keysPressed[0] === "t" && vimState.surround.replacement.length === 0) { - stringToAdd = "<"; - } - - // Convert a few shortcuts to the correct surround characters when NOT entering a tag - if (vimState.surround.replacement.length === 0) { - if (stringToAdd === "b") { stringToAdd = "("; } - if (stringToAdd === "B") { stringToAdd = "{"; } - if (stringToAdd === "r") { stringToAdd = "["; } - } - - vimState.surround.replacement += stringToAdd; - - await CommandSurroundAddToReplacement.TryToExecuteSurround(vimState, position); - - return vimState; - } - - public static Finish(vimState: VimState): boolean { - vimState.recordedState.hasRunOperator = false; - vimState.recordedState.actionsRun = []; - vimState.recordedState.hasRunSurround = true; - vimState.surround = undefined; - vimState.currentMode = ModeName.Normal; - - // Record keys that were pressed since surround started - for (let i = vimState.recordedState.surroundKeyIndexStart; i < vimState.keyHistory.length; i++) { - vimState.recordedState.surroundKeys.push(vimState.keyHistory[i]); - } - - return false; - } - - // we assume that we start directly on the characters we're operating over - // e.g. cs{' starts us with start on { end on }. - - public static RemoveWhitespace(vimState: VimState, start: Position, stop: Position): void { - const firstRangeStart = start.getRightThroughLineBreaks(); - let firstRangeEnd = start.getRightThroughLineBreaks(); - - let secondRangeStart = stop.getLeftThroughLineBreaks(); - const secondRangeEnd = stop.getLeftThroughLineBreaks().getRight(); - - if (firstRangeEnd.isEqual(secondRangeStart)) { return; } - - while (!firstRangeEnd.isEqual(stop) && - TextEditor.getCharAt(firstRangeEnd).match(/[ \t]/) && - !firstRangeEnd.isLineEnd()) { - firstRangeEnd = firstRangeEnd.getRight(); - } - - while (!secondRangeStart.isEqual(firstRangeEnd) && - TextEditor.getCharAt(secondRangeStart).match(/[ \t]/) && - !secondRangeStart.isLineBeginning()) { - secondRangeStart = secondRangeStart.getLeftThroughLineBreaks(false); - } - - // Adjust range start based on found position - secondRangeStart = secondRangeStart.getRight(); - - const firstRange = new Range(firstRangeStart, firstRangeEnd); - const secondRange = new Range(secondRangeStart, secondRangeEnd); - - vimState.recordedState.transformations.push({ type: "deleteRange", range: firstRange }); - vimState.recordedState.transformations.push({ type: "deleteRange", range: secondRange }); - } - - public static GetStartAndEndReplacements(replacement: string | undefined): { startReplace: string; endReplace: string; } { - if (!replacement) { return { startReplace: "", endReplace: "" }; } - - let startReplace = replacement; - let endReplace = replacement; - - if (startReplace[0] === "<") { - let tagName = /([-\w]+)/.exec(startReplace); - if (tagName) { - endReplace = ``; - } else { - endReplace = " { - const { target, replacement, operator } = vimState.surround!; - - if (operator === "change" || operator === "yank") { - if (!replacement) { - return false; - } - - // This is an incomplete tag. Wait for the user to finish it. - if (replacement[0] === "<" && replacement[replacement.length - 1] !== ">") { - return false; - } - } - - let { startReplace, endReplace } = this.GetStartAndEndReplacements(replacement); - - if (operator === "yank") { - if (!vimState.surround) { return false; } - if (!vimState.surround.range) { return false; } - - let start = vimState.surround.range.start; - let stop = vimState.surround.range.stop; - - stop = stop.getRight(); - - if (vimState.surround.isVisualLine) { - startReplace = startReplace + "\n"; - endReplace = "\n" + endReplace; - } - - vimState.recordedState.transformations.push({ type: "insertText", text: startReplace, position: start }); - vimState.recordedState.transformations.push({ type: "insertText", text: endReplace, position: stop }); - - return CommandSurroundAddToReplacement.Finish(vimState); - } - - let startReplaceRange: Range | undefined; - let endReplaceRange: Range | undefined; - let startDeleteRange: Range | undefined; - let endDeleteRange: Range | undefined; - - const quoteMatches: { char: string; movement: () => MoveQuoteMatch }[] = [ - { char: "'", movement: () => new MoveASingleQuotes() }, - { char: '"', movement: () => new MoveADoubleQuotes() }, - { char: "`", movement: () => new MoveABacktick() }, - ]; - - for (const { char, movement } of quoteMatches) { - if (char !== target) { continue; } - - const { start, stop, failed } = await movement().execAction(position, vimState); - - if (failed) { return CommandSurroundAddToReplacement.Finish(vimState); } - - startReplaceRange = new Range(start, start.getRight()); - endReplaceRange = new Range(stop, stop.getRight()); - } - - const pairedMatchings: { open: string, close: string, movement: () => MoveInsideCharacter }[] = [ - { open: "{", close: "}", movement: () => new MoveACurlyBrace() }, - { open: "[", close: "]", movement: () => new MoveASquareBracket() }, - { open: "(", close: ")", movement: () => new MoveAParentheses() }, - { open: "<", close: ">", movement: () => new MoveACaret() }, - ]; - - for (const { open, close, movement } of pairedMatchings) { - if (target !== open && target !== close) { continue; } - - let { start, stop, failed } = await movement().execAction(position, vimState); - - if (failed) { return CommandSurroundAddToReplacement.Finish(vimState); } - - stop = stop.getLeft(); - - startReplaceRange = new Range(start, start.getRight()); - endReplaceRange = new Range(stop, stop.getRight()); - - if (target === open) { - CommandSurroundAddToReplacement.RemoveWhitespace(vimState, start, stop); - } - } - - if (target === "t") { - let { start, stop, failed } = await new MoveAroundTag().execAction(position, vimState); - let tagEnd = await new MoveInsideTag().execAction(position, vimState); - - if (failed || tagEnd.failed) { return CommandSurroundAddToReplacement.Finish(vimState); } - - stop = stop.getRight(); - tagEnd.stop = tagEnd.stop.getRight(); - - if (failed) { return CommandSurroundAddToReplacement.Finish(vimState); } - - startReplaceRange = new Range(start, start.getRight()); - endReplaceRange = new Range(tagEnd.stop, tagEnd.stop.getRight()); - - startDeleteRange = new Range(start.getRight(), tagEnd.start); - endDeleteRange = new Range(tagEnd.stop.getRight(), stop); - } - - if (operator === "change") { - if (!replacement) { return false; } - const wordMatchings: { char: string, movement: () => TextObjectMovement, addNewline: "no" | "end-only" | "both" }[] = [ - { char: "w", movement: () => new SelectInnerWord(), addNewline: "no" }, - { char: "p", movement: () => new SelectInnerParagraph(), addNewline: "both" }, - { char: "s", movement: () => new SelectInnerSentence(), addNewline: "end-only" }, - { char: "W", movement: () => new SelectInnerBigWord(), addNewline: "no" }, - ]; - - for (const { char, movement, addNewline } of wordMatchings) { - if (target !== char) { continue; } - - let { stop, start, failed } = await movement().execAction(position, vimState) as IMovement; - - stop = stop.getRight(); - - if (failed) { return CommandSurroundAddToReplacement.Finish(vimState); } - - if (addNewline === "end-only" || addNewline === "both") { endReplace = "\n" + endReplace; } - if (addNewline === "both") { startReplace += "\n"; } - - vimState.recordedState.transformations.push({ type: "insertText", text: startReplace, position: start }); - vimState.recordedState.transformations.push({ type: "insertText", text: endReplace, position: stop }); - - return CommandSurroundAddToReplacement.Finish(vimState); - } - } - - // We've got our ranges. Run the surround command with the appropriate operator. - - if (!startReplaceRange && !endReplaceRange && !startDeleteRange && !endDeleteRange) { - return false; - } - - if (operator === "change") { - if (!replacement) { return false; } - - if (startReplaceRange) { TextEditor.replaceText(vimState, startReplace, startReplaceRange.start, startReplaceRange.stop); } - if (endReplaceRange) { TextEditor.replaceText(vimState, endReplace, endReplaceRange.start, endReplaceRange.stop); } - if (startDeleteRange) { vimState.recordedState.transformations.push({ type: "deleteRange", range: startDeleteRange }); } - if (endDeleteRange) { vimState.recordedState.transformations.push({ type: "deleteRange", range: endDeleteRange }); } - - return CommandSurroundAddToReplacement.Finish(vimState); - } - - if (operator === "delete") { - if (startReplaceRange) { vimState.recordedState.transformations.push({ type: "deleteRange", range: startReplaceRange }); } - if (endReplaceRange) { vimState.recordedState.transformations.push({ type: "deleteRange", range: endReplaceRange }); } - - if (startDeleteRange) { vimState.recordedState.transformations.push({ type: "deleteRange", range: startDeleteRange }); } - if (endDeleteRange) { vimState.recordedState.transformations.push({ type: "deleteRange", range: endDeleteRange }); } - - return CommandSurroundAddToReplacement.Finish(vimState); - } - - return false; - } -} \ No newline at end of file diff --git a/src/actions/textobject.ts b/src/actions/textobject.ts deleted file mode 100644 index ed8ca7cd27d..00000000000 --- a/src/actions/textobject.ts +++ /dev/null @@ -1,531 +0,0 @@ -import { ModeName } from './../mode/mode'; -import { Position } from './../common/motion/position'; -import { Range } from './../common/motion/range'; -import { TextEditor } from './../textEditor'; -import { VimState } from './../mode/modeHandler'; -import { RegisterAction } from './base'; -import { ChangeOperator } from './operator'; -import { - BaseMovement, IMovement, - MoveASingleQuotes, MoveADoubleQuotes, MoveAClosingCurlyBrace, MoveAParentheses, MoveASquareBracket -} from './motion'; - -export abstract class TextObjectMovement extends BaseMovement { - modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualBlock]; - canBePrefixedWithCount = true; - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - const res = await this.execAction(position, vimState) as IMovement; - // Since we need to handle leading spaces, we cannot use MoveWordBegin.execActionForOperator - // In normal mode, the character on the stop position will be the first character after the operator executed - // and we do left-shifting in operator-pre-execution phase, here we need to right-shift the stop position accordingly. - res.stop = new Position(res.stop.line, res.stop.character + 1); - - return res; - } -} - -@RegisterAction -export class SelectWord extends TextObjectMovement { - keys = ["a", "w"]; - - public async execAction(position: Position, vimState: VimState): Promise { - let start: Position; - let stop: Position; - const currentChar = TextEditor.getLineAt(position).text[position.character]; - - if (/\s/.test(currentChar)) { - start = position.getLastWordEnd().getRight(); - stop = position.getCurrentWordEnd(); - } else { - stop = position.getWordRight(); - // If the next word is not at the beginning of the next line, we want to pretend it is. - // This is because 'aw' has two fundamentally different behaviors distinguished by whether - // the next word is directly after the current word, as described in the following comment. - // The only case that's not true is in cases like #1350. - if (stop.isEqual(stop.getFirstLineNonBlankChar())) { - stop = stop.getLineBegin(); - } - stop = stop.getLeftThroughLineBreaks().getLeftIfEOL(); - // If we aren't separated from the next word by whitespace(like in "horse ca|t,dog" or at the end of the line) - // then we delete the spaces to the left of the current word. Otherwise, we delete to the right. - // Also, if the current word is the leftmost word, we only delete from the start of the word to the end. - if (stop.isEqual(position.getCurrentWordEnd(true)) && - !position.getWordLeft(true).isEqual(position.getFirstLineNonBlankChar()) - && vimState.recordedState.count === 0) { - start = position.getLastWordEnd().getRight(); - } else { - start = position.getWordLeft(true); - } - } - - if (vimState.currentMode === ModeName.Visual && !vimState.cursorPosition.isEqual(vimState.cursorStartPosition)) { - start = vimState.cursorStartPosition; - - if (vimState.cursorPosition.isBefore(vimState.cursorStartPosition)) { - // If current cursor postion is before cursor start position, we are selecting words in reverser order. - if (/\s/.test(currentChar)) { - stop = position.getWordLeft(true); - } else { - stop = position.getLastWordEnd().getRight(); - } - } - } - - return { - start: start, - stop: stop - }; - } -} - -@RegisterAction -export class SelectABigWord extends TextObjectMovement { - keys = ["a", "W"]; - - public async execAction(position: Position, vimState: VimState): Promise { - let start: Position; - let stop: Position; - - const currentChar = TextEditor.getLineAt(position).text[position.character]; - - if (/\s/.test(currentChar)) { - start = position.getLastBigWordEnd().getRight(); - stop = position.getCurrentBigWordEnd(); - } else { - // Check 'aw' code for much of the reasoning behind this logic. - let nextWord = position.getBigWordRight(); - if ((nextWord.isEqual(nextWord.getFirstLineNonBlankChar()) || nextWord.isLineEnd()) && - vimState.recordedState.count === 0) { - start = position.getLastWordEnd().getRight(); - stop = position.getLineEnd(); - } else { - start = position.getBigWordLeft(true); - stop = position.getBigWordRight().getLeft(); - } - } - if (vimState.currentMode === ModeName.Visual && !vimState.cursorPosition.isEqual(vimState.cursorStartPosition)) { - start = vimState.cursorStartPosition; - - if (vimState.cursorPosition.isBefore(vimState.cursorStartPosition)) { - // If current cursor postion is before cursor start position, we are selecting words in reverser order. - if (/\s/.test(currentChar)) { - stop = position.getBigWordLeft(); - } else { - stop = position.getLastBigWordEnd().getRight(); - } - } - } - - return { - start: start, - stop: stop - }; - } -} - -/** - * This is a custom action that I (johnfn) added. It selects procedurally - * larger blocks. e.g. if you had "blah (foo [bar 'ba|z'])" then it would - * select 'baz' first. If you pressed az again, it'd then select [bar 'baz'], - * and if you did it a third time it would select "(foo [bar 'baz'])". - */ -@RegisterAction -export class SelectAnExpandingBlock extends TextObjectMovement { - keys = ["a", "f"]; - modes = [ModeName.Visual, ModeName.VisualLine]; - - public async execAction(position: Position, vimState: VimState): Promise { - const ranges = [ - await new MoveASingleQuotes().execAction(position, vimState), - await new MoveADoubleQuotes().execAction(position, vimState), - await new MoveAClosingCurlyBrace().execAction(position, vimState), - await new MoveAParentheses().execAction(position, vimState), - await new MoveASquareBracket().execAction(position, vimState), - ]; - - let smallestRange: Range | undefined = undefined; - - for (const iMotion of ranges) { - if (iMotion.failed) { continue; } - - const range = Range.FromIMovement(iMotion); - let contender: Range | undefined = undefined; - - if (!smallestRange) { - contender = range; - } else { - if (range.start.isAfter(smallestRange.start) && - range.stop.isBefore(smallestRange.stop)) { - contender = range; - } - } - - if (contender) { - const areTheyEqual = - contender.equals(new Range(vimState.cursorStartPosition, vimState.cursorPosition)) || - (vimState.currentMode === ModeName.VisualLine && - contender.start.line === vimState.cursorStartPosition.line && - contender.stop.line === vimState.cursorPosition.line); - - if (!areTheyEqual) { - smallestRange = contender; - } - } - } - - if (!smallestRange) { - return { - start: vimState.cursorStartPosition, - stop: vimState.cursorPosition, - }; - } else { - return { - start: smallestRange.start, - stop: smallestRange.stop, - }; - } - } -} - -@RegisterAction -export class SelectInnerWord extends TextObjectMovement { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ["i", "w"]; - - public async execAction(position: Position, vimState: VimState): Promise { - let start: Position; - let stop: Position; - const currentChar = TextEditor.getLineAt(position).text[position.character]; - - if (/\s/.test(currentChar)) { - start = position.getLastWordEnd().getRight(); - stop = position.getWordRight().getLeftThroughLineBreaks(); - } else { - start = position.getWordLeft(true); - stop = position.getCurrentWordEnd(true); - } - - if (vimState.currentMode === ModeName.Visual && !vimState.cursorPosition.isEqual(vimState.cursorStartPosition)) { - start = vimState.cursorStartPosition; - - if (vimState.cursorPosition.isBefore(vimState.cursorStartPosition)) { - // If current cursor postion is before cursor start position, we are selecting words in reverser order. - if (/\s/.test(currentChar)) { - stop = position.getLastWordEnd().getRight(); - } else { - stop = position.getWordLeft(true); - } - } - } - - return { - start: start, - stop: stop - }; - } -} - -@RegisterAction -export class SelectInnerBigWord extends TextObjectMovement { - modes = [ModeName.Normal, ModeName.Visual]; - keys = ["i", "W"]; - - public async execAction(position: Position, vimState: VimState): Promise { - let start: Position; - let stop: Position; - const currentChar = TextEditor.getLineAt(position).text[position.character]; - - if (/\s/.test(currentChar)) { - start = position.getLastBigWordEnd().getRight(); - stop = position.getBigWordRight().getLeft(); - } else { - start = position.getBigWordLeft(); - stop = position.getCurrentBigWordEnd(true); - } - - if (vimState.currentMode === ModeName.Visual && !vimState.cursorPosition.isEqual(vimState.cursorStartPosition)) { - start = vimState.cursorStartPosition; - - if (vimState.cursorPosition.isBefore(vimState.cursorStartPosition)) { - // If current cursor postion is before cursor start position, we are selecting words in reverser order. - if (/\s/.test(currentChar)) { - stop = position.getLastBigWordEnd().getRight(); - } else { - stop = position.getBigWordLeft(); - } - } - } - - return { - start: start, - stop: stop - }; - } -} - -@RegisterAction -export class SelectSentence extends TextObjectMovement { - keys = ["a", "s"]; - - public async execAction(position: Position, vimState: VimState): Promise { - let start: Position; - let stop: Position; - - const currentSentenceBegin = position.getSentenceBegin({ forward: false }); - const currentSentenceNonWhitespaceEnd = currentSentenceBegin.getCurrentSentenceEnd(); - - if (currentSentenceNonWhitespaceEnd.isBefore(position)) { - // The cursor is on a trailing white space. - start = currentSentenceNonWhitespaceEnd.getRight(); - stop = currentSentenceBegin.getSentenceBegin({ forward: true }).getCurrentSentenceEnd(); - } else { - const nextSentenceBegin = currentSentenceBegin.getSentenceBegin({ forward: true }); - - // If the sentence has no trailing white spaces, `as` should include its leading white spaces. - if (nextSentenceBegin.isEqual(currentSentenceBegin.getCurrentSentenceEnd())) { - start = currentSentenceBegin.getSentenceBegin({ forward: false }).getCurrentSentenceEnd().getRight(); - stop = nextSentenceBegin; - } else { - start = currentSentenceBegin; - stop = nextSentenceBegin.getLeft(); - } - } - - if (vimState.currentMode === ModeName.Visual && !vimState.cursorPosition.isEqual(vimState.cursorStartPosition)) { - start = vimState.cursorStartPosition; - - if (vimState.cursorPosition.isBefore(vimState.cursorStartPosition)) { - // If current cursor postion is before cursor start position, we are selecting sentences in reverser order. - if (currentSentenceNonWhitespaceEnd.isAfter(vimState.cursorPosition)) { - stop = currentSentenceBegin.getSentenceBegin({ forward: false }).getCurrentSentenceEnd().getRight(); - } else { - stop = currentSentenceBegin; - } - } - } - - return { - start: start, - stop: stop - }; - } -} - -@RegisterAction -export class SelectInnerSentence extends TextObjectMovement { - keys = ["i", "s"]; - - public async execAction(position: Position, vimState: VimState): Promise { - let start: Position; - let stop: Position; - - const currentSentenceBegin = position.getSentenceBegin({ forward: false }); - const currentSentenceNonWhitespaceEnd = currentSentenceBegin.getCurrentSentenceEnd(); - - if (currentSentenceNonWhitespaceEnd.isBefore(position)) { - // The cursor is on a trailing white space. - start = currentSentenceNonWhitespaceEnd.getRight(); - stop = currentSentenceBegin.getSentenceBegin({ forward: true }).getLeft(); - } else { - start = currentSentenceBegin; - stop = currentSentenceNonWhitespaceEnd; - } - - if (vimState.currentMode === ModeName.Visual && !vimState.cursorPosition.isEqual(vimState.cursorStartPosition)) { - start = vimState.cursorStartPosition; - - if (vimState.cursorPosition.isBefore(vimState.cursorStartPosition)) { - // If current cursor postion is before cursor start position, we are selecting sentences in reverser order. - if (currentSentenceNonWhitespaceEnd.isAfter(vimState.cursorPosition)) { - stop = currentSentenceBegin; - } else { - stop = currentSentenceNonWhitespaceEnd.getRight(); - } - } - } - - return { - start: start, - stop: stop - }; - } -} - -@RegisterAction -export class SelectParagraph extends TextObjectMovement { - keys = ["a", "p"]; - - public async execAction(position: Position, vimState: VimState): Promise { - let start: Position; - const currentParagraphBegin = position.getCurrentParagraphBeginning(); - - if (position.isLineBeginning() && position.isLineEnd()) { - // The cursor is at an empty line, it can be both the start of next paragraph and the end of previous paragraph - start = position.getCurrentParagraphBeginning().getCurrentParagraphEnd(); - } else { - if (currentParagraphBegin.isLineBeginning() && currentParagraphBegin.isLineEnd()) { - start = currentParagraphBegin.getRightThroughLineBreaks(); - } else { - start = currentParagraphBegin; - } - } - - return { - start: start, - stop: position.getCurrentParagraphEnd() - }; - } -} - -@RegisterAction -export class SelectInnerParagraph extends TextObjectMovement { - keys = ["i", "p"]; - - public async execAction(position: Position, vimState: VimState): Promise { - let start: Position; - let stop: Position = position.getCurrentParagraphEnd(); - - if (stop.isLineBeginning() && stop.isLineEnd()) { - stop = stop.getLeftThroughLineBreaks(); - } - - const currentParagraphBegin = position.getCurrentParagraphBeginning(); - - if (position.isLineBeginning() && position.isLineEnd()) { - // The cursor is at an empty line, it can be both the start of next paragraph and the end of previous paragraph - start = position.getCurrentParagraphBeginning().getCurrentParagraphEnd(); - stop = position.getCurrentParagraphEnd().getCurrentParagraphBeginning(); - } else { - if (currentParagraphBegin.isLineBeginning() && currentParagraphBegin.isLineEnd()) { - start = currentParagraphBegin.getRightThroughLineBreaks(); - } else { - start = currentParagraphBegin; - } - } - - return { - start: start, - stop: stop - }; - } -} - -abstract class IndentObjectMatch extends TextObjectMovement { - setsDesiredColumnToEOL = true; - - protected includeLineAbove = false; - protected includeLineBelow = false; - - public async execAction(position: Position, vimState: VimState): Promise { - const isChangeOperator = vimState.recordedState.operator instanceof ChangeOperator; - const firstValidLineNumber = IndentObjectMatch.findFirstValidLine(position); - const firstValidLine = TextEditor.getLineAt(new Position(firstValidLineNumber, 0)); - const cursorIndent = firstValidLine.firstNonWhitespaceCharacterIndex; - - // let startLineNumber = findRangeStart(firstValidLineNumber, cursorIndent); - let startLineNumber = IndentObjectMatch.findRangeStartOrEnd(firstValidLineNumber, cursorIndent, -1); - let endLineNumber = IndentObjectMatch.findRangeStartOrEnd(firstValidLineNumber, cursorIndent, 1); - - // Adjust the start line as needed. - if (this.includeLineAbove) { - startLineNumber -= 1; - } - // Check for OOB. - if (startLineNumber < 0) { - startLineNumber = 0; - } - - // Adjust the end line as needed. - if (this.includeLineBelow) { - endLineNumber += 1; - } - // Check for OOB. - if (endLineNumber > TextEditor.getLineCount() - 1) { - endLineNumber = TextEditor.getLineCount() - 1; - } - - // If initiated by a change operation, adjust the cursor to the indent level - // of the block. - let startCharacter = 0; - if (isChangeOperator) { - startCharacter = TextEditor.getLineAt(new Position(startLineNumber, 0)).firstNonWhitespaceCharacterIndex; - } - // TextEditor.getLineMaxColumn throws when given line 0, which we don't - // care about here since it just means this text object wouldn't work on a - // single-line document. - let endCharacter; - if (endLineNumber === TextEditor.getLineCount() - 1 || vimState.currentMode === ModeName.Visual) { - endCharacter = TextEditor.getLineMaxColumn(endLineNumber); - } else { - endCharacter = 0; - endLineNumber++; - } - return { - start: new Position(startLineNumber, startCharacter), - stop: new Position(endLineNumber, endCharacter), - }; - } - - public async execActionForOperator(position: Position, vimState: VimState): Promise { - return await this.execAction(position, vimState); - } - - /** - * Searches up from the cursor for the first non-empty line. - */ - public static findFirstValidLine(cursorPosition: Position): number { - for (let i = cursorPosition.line; i >= 0; i--) { - const line = TextEditor.getLineAt(new Position(i, 0)); - - if (!line.isEmptyOrWhitespace) { - return i; - } - } - - return cursorPosition.line; - } - - /** - * Searches up or down from a line finding the first with a lower indent level. - */ - public static findRangeStartOrEnd(startIndex: number, cursorIndent: number, step: -1 | 1): number { - let i = startIndex; - let ret = startIndex; - const end = step === 1 - ? TextEditor.getLineCount() - : -1; - - for (; i !== end; i += step) { - const line = TextEditor.getLineAt(new Position(i, 0)); - const isLineEmpty = line.isEmptyOrWhitespace; - const lineIndent = line.firstNonWhitespaceCharacterIndex; - - if (lineIndent < cursorIndent && !isLineEmpty) { - break; - } - - ret = i; - } - - return ret; - } -} - -@RegisterAction -class InsideIndentObject extends IndentObjectMatch { - keys = ["i", "i"]; -} - -@RegisterAction -class InsideIndentObjectAbove extends IndentObjectMatch { - keys = ["a", "i"]; - includeLineAbove = true; -} - -@RegisterAction -class InsideIndentObjectBoth extends IndentObjectMatch { - keys = ["a", "I"]; - includeLineAbove = true; - includeLineBelow = true; -} \ No newline at end of file diff --git a/src/actions/vim.all.ts b/src/actions/vim.all.ts deleted file mode 100644 index 10f6bb77ee3..00000000000 --- a/src/actions/vim.all.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Make sure they are all loaded. - */ -import './base'; -import './operator'; -import './motion'; -import './textobject'; - -// commands -import './commands/insert'; - -/** - * Plugins - */ - -// easymotion -import './plugins/easymotion/easymotion.cmd'; -// surround -import './plugins/surround'; \ No newline at end of file diff --git a/src/cmd_line/commands/close.ts b/src/cmd_line/commands/close.ts deleted file mode 100644 index 98094ff058e..00000000000 --- a/src/cmd_line/commands/close.ts +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as node from "../node"; -import * as error from '../../error'; - -export interface ICloseCommandArguments extends node.ICommandArgs { - bang?: boolean; - range?: node.LineRange; - quitAll?: boolean; -} - -// -// Implements :close -// http://vimdoc.sourceforge.net/htmldoc/windows.html#:close -// -export class CloseCommand extends node.CommandBase { - protected _arguments : ICloseCommandArguments; - - constructor(args : ICloseCommandArguments) { - super(); - this._name = 'close'; - this._arguments = args; - } - - get arguments() : ICloseCommandArguments { - return this._arguments; - } - - async execute() : Promise { - if (this.activeTextEditor!.document.isDirty && !this.arguments.bang) { - throw error.VimError.fromCode(error.ErrorCode.E37); - } - - if (vscode.window.visibleTextEditors.length === 1) { - throw error.VimError.fromCode(error.ErrorCode.E444); - } - - let oldViewColumn = this.activeTextEditor!.viewColumn; - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - - if (vscode.window.activeTextEditor !== undefined && vscode.window.activeTextEditor.viewColumn === oldViewColumn) { - await vscode.commands.executeCommand('workbench.action.previousEditor'); - } - }} diff --git a/src/cmd_line/commands/deleteRange.ts b/src/cmd_line/commands/deleteRange.ts deleted file mode 100644 index ef069d8ea89..00000000000 --- a/src/cmd_line/commands/deleteRange.ts +++ /dev/null @@ -1,86 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as node from "../node"; -import * as token from '../token'; -import { ModeHandler } from "../../mode/modeHandler"; -import { TextEditor } from "../../textEditor"; -import { Register, RegisterMode } from '../../register/register'; -import { Position } from '../../common/motion/position'; - -export interface IDeleteRangeCommandArguments extends node.ICommandArgs { - register?: string; -} - - -export class DeleteRangeCommand extends node.CommandBase { - neovimCapable = true; - protected _arguments : IDeleteRangeCommandArguments; - - constructor(args : IDeleteRangeCommandArguments) { - super(); - this._name = 'delete'; - this._arguments = args; - } - - get arguments() : IDeleteRangeCommandArguments{ - return this._arguments; - } - - async deleteRange(start: Position, end: Position, modeHandler: ModeHandler): Promise { - start = start.getLineBegin(); - end = end.getLineEnd(); - end = Position.FromVSCodePosition(end.with(end.line, end.character + 1)); - - const isOnLastLine = end.line === TextEditor.getLineCount() - 1; - - if (end.character === TextEditor.getLineAt(end).text.length + 1) { - end = end.getDown(0); - } - - if (isOnLastLine && start.line !== 0) { - start = start.getPreviousLineBegin().getLineEnd(); - } - - let text = modeHandler.vimState.editor.document.getText(new vscode.Range(start, end)); - text = text.endsWith("\r\n") ? text.slice(0, -2) : text.slice(0, -1); - await TextEditor.delete(new vscode.Range(start, end)); - - let resultPosition = Position.EarlierOf(start, end); - if (start.character > TextEditor.getLineAt(start).text.length) { - resultPosition = start.getLeft(); - } else { - resultPosition = start; - } - - resultPosition = resultPosition.getLineBegin(); - modeHandler.vimState.editor.selection = new vscode.Selection(resultPosition, resultPosition); - return text; - } - - async execute(modeHandler: ModeHandler): Promise { - if (!modeHandler.vimState.editor) { - return; - } - - let cursorPosition = Position.FromVSCodePosition(modeHandler.vimState.editor.selection.active); - let text = await this.deleteRange(cursorPosition, cursorPosition, modeHandler); - Register.putByKey(text, this._arguments.register, RegisterMode.LineWise); - } - - async executeWithRange(modeHandler : ModeHandler, range: node.LineRange) { - let start: vscode.Position; - let end: vscode.Position; - - if (range.left[0].type === token.TokenType.Percent) { - start = new vscode.Position(0, 0); - end = new vscode.Position(TextEditor.getLineCount() - 1, 0); - } else { - start = range.lineRefToPosition(modeHandler.vimState.editor, range.left, modeHandler); - end = range.lineRefToPosition(modeHandler.vimState.editor, range.right, modeHandler); - } - - let text = await this.deleteRange(Position.FromVSCodePosition(start), Position.FromVSCodePosition(end), modeHandler); - Register.putByKey(text, this._arguments.register, RegisterMode.LineWise); - } -} diff --git a/src/cmd_line/commands/file.ts b/src/cmd_line/commands/file.ts deleted file mode 100644 index d8a473f91fb..00000000000 --- a/src/cmd_line/commands/file.ts +++ /dev/null @@ -1,118 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as path from "path"; -import * as fs from "fs"; -import * as node from "../node"; -const untildify = require('untildify'); - -export enum FilePosition { - CurrentWindow, - NewWindow -} - -export interface IFileCommandArguments extends node.ICommandArgs { - name: string | undefined; - bang?: boolean; - position?: FilePosition; - lineNumber?: number; -} - -export class FileCommand extends node.CommandBase { - protected _arguments: IFileCommandArguments; - - constructor(args : IFileCommandArguments) { - super(); - this._name = 'file'; - this._arguments = args; - } - - get arguments() : IFileCommandArguments { - return this._arguments; - } - - getActiveViewColumn(): vscode.ViewColumn { - const active = vscode.window.activeTextEditor; - - if (!active) { - return vscode.ViewColumn.One; - } - - return active.viewColumn!; - } - - getViewColumnToRight() : vscode.ViewColumn { - const active = vscode.window.activeTextEditor; - - if (!active) { - return vscode.ViewColumn.One; - } - - switch (active.viewColumn) { - case vscode.ViewColumn.One: - return vscode.ViewColumn.Two; - case vscode.ViewColumn.Two: - return vscode.ViewColumn.Three; - } - - return active.viewColumn!; - } - - async execute(): Promise { - if (this._arguments.bang) { - await vscode.commands.executeCommand("workbench.action.files.revert"); - } - if (this._arguments.name === undefined) { - // Open an empty file - if (this._arguments.position === FilePosition.CurrentWindow) { - await vscode.commands.executeCommand("workbench.action.files.newUntitledFile"); - } else { - await vscode.commands.executeCommand("workbench.action.splitEditor"); - await vscode.commands.executeCommand("workbench.action.files.newUntitledFile"); - await vscode.commands.executeCommand("workbench.action.closeOtherEditors"); - } - - return; - } else if (this._arguments.name === "") { - if (this._arguments.position === FilePosition.NewWindow) { - await vscode.commands.executeCommand("workbench.action.splitEditor"); - } - return; - } - - let currentFilePath = vscode.window.activeTextEditor!.document.uri.path; - this._arguments.name = untildify(this._arguments.name); - let newFilePath = path.isAbsolute(this._arguments.name) ? - this._arguments.name : - path.join(path.dirname(currentFilePath), this._arguments.name); - - if (newFilePath !== currentFilePath) { - const newFileDoesntExist = !await this.fileExists(newFilePath); - const newFileHasNoExtname = path.extname(newFilePath) === ''; - if (newFileDoesntExist && newFileHasNoExtname) { - const pathWithExtname = newFilePath + path.extname(currentFilePath); - if (await this.fileExists(pathWithExtname)) { - newFilePath = pathWithExtname; - } - } - - let folder = vscode.Uri.file(newFilePath); - await vscode.commands.executeCommand("vscode.open", folder, - this._arguments.position === FilePosition.NewWindow ? this.getViewColumnToRight() : this.getActiveViewColumn()); - - if (this.arguments.lineNumber) { - vscode.window.activeTextEditor!.revealRange( - new vscode.Range(new vscode.Position(this.arguments.lineNumber, 0), - new vscode.Position(this.arguments.lineNumber, 0))); - } - } - } - - protected fileExists(filePath: string) { - return new Promise((resolve, reject) => { - fs.stat(filePath, async (error, stat) => { - resolve(!error); - }); - }); - } -} diff --git a/src/cmd_line/commands/nohl.ts b/src/cmd_line/commands/nohl.ts deleted file mode 100644 index 3646d6eda0b..00000000000 --- a/src/cmd_line/commands/nohl.ts +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; - -import * as node from "../node"; -import { ModeHandler } from "../../mode/modeHandler"; - -export class NohlCommand extends node.CommandBase { - protected _arguments: {}; - - constructor(args: {}) { - super(); - - this._name = 'nohl'; - this._arguments = args; - } - - get arguments(): {} { - return this._arguments; - } - - async execute(modeHandler : ModeHandler): Promise { - modeHandler.vimState.globalState.hl = false; - } -} \ No newline at end of file diff --git a/src/cmd_line/commands/quit.ts b/src/cmd_line/commands/quit.ts deleted file mode 100644 index 321bd6b39c2..00000000000 --- a/src/cmd_line/commands/quit.ts +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as node from "../node"; -import * as error from '../../error'; - -export interface IQuitCommandArguments extends node.ICommandArgs { - bang?: boolean; - range?: node.LineRange; - quitAll?: boolean; -} - -// -// Implements :quit -// http://vimdoc.sourceforge.net/htmldoc/editing.html#:quit -// -export class QuitCommand extends node.CommandBase { - protected _arguments : IQuitCommandArguments; - - constructor(args : IQuitCommandArguments) { - super(); - this._name = 'quit'; - this._arguments = args; - } - - get arguments() : IQuitCommandArguments { - return this._arguments; - } - - async execute() : Promise { - if (this.activeTextEditor!.document.isDirty && !this.arguments.bang) { - throw error.VimError.fromCode(error.ErrorCode.E37); - } - - if (this._arguments.quitAll) { - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - } else { - let oldViewColumn = this.activeTextEditor!.viewColumn; - if (!this.arguments.bang) { - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - } else { - await vscode.commands.executeCommand('workbench.action.revertAndCloseActiveEditor'); - } - - if (vscode.window.activeTextEditor !== undefined && vscode.window.activeTextEditor.viewColumn === oldViewColumn) { - await vscode.commands.executeCommand('workbench.action.previousEditor'); - } - } - }} diff --git a/src/cmd_line/commands/read.ts b/src/cmd_line/commands/read.ts deleted file mode 100644 index 857fa00af3e..00000000000 --- a/src/cmd_line/commands/read.ts +++ /dev/null @@ -1,81 +0,0 @@ -"use strict"; - -import * as node from "../node"; -import {readFile} from 'fs'; -import {exec} from 'child_process'; -import {TextEditor} from '../../textEditor'; - -export interface IReadCommandArguments extends node.ICommandArgs { - file?: string; - cmd?: string; -} - -// -// Implements :read and :read! -// http://vimdoc.sourceforge.net/htmldoc/insert.html#:read -// http://vimdoc.sourceforge.net/htmldoc/insert.html#:read! -// -export class ReadCommand extends node.CommandBase { - neovimCapable = true; - protected _arguments : IReadCommandArguments; - - constructor(args : IReadCommandArguments) { - super(); - this._name = 'read'; - this._arguments = args; - } - - get arguments() : IReadCommandArguments { - return this._arguments; - } - - async execute() : Promise { - const textToInsert = await this.getTextToInsert(); - if (textToInsert) { - await TextEditor.insert(textToInsert); - } - } - - async getTextToInsert() : Promise { - if (this.arguments.file && this.arguments.file.length > 0) { - return await this.getTextToInsertFromFile(); - } else if (this.arguments.cmd && this.arguments.cmd.length > 0) { - return await this.getTextToInsertFromCmd(); - } else { - throw Error('Invalid arguments'); - } - } - - async getTextToInsertFromFile() : Promise { - // TODO: Read encoding from ++opt argument. - return new Promise((resolve, reject) => { - try { - readFile(this.arguments.file as string, 'utf8', (err, data) => { - if (err) { - reject(err); - } else { - resolve(data); - } - }); - } catch (e) { - reject(e); - } - }); - } - - async getTextToInsertFromCmd() : Promise { - return new Promise((resolve, reject) => { - try { - exec(this.arguments.cmd as string, (err, stdout, stderr) => { - if (err) { - reject(err); - } else { - resolve(stdout); - } - }); - } catch (e) { - reject(e); - } - }); - } -} diff --git a/src/cmd_line/commands/register.ts b/src/cmd_line/commands/register.ts deleted file mode 100644 index f86d7ca7e97..00000000000 --- a/src/cmd_line/commands/register.ts +++ /dev/null @@ -1,65 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as node from "../node"; -import {ModeHandler, RecordedState} from "../../mode/modeHandler"; -import { Register} from '../../register/register'; - -export interface IRegisterCommandArguments extends node.ICommandArgs { - arg?: string; -} -export class RegisterCommand extends node.CommandBase { - protected _arguments : IRegisterCommandArguments; - - constructor(args : IRegisterCommandArguments) { - super(); - this._name = 'register'; - this._arguments = args; - } - - get arguments() : IRegisterCommandArguments{ - return this._arguments; - } - - private async getRegisterDisplayValue(register: string) { - let result = (await Register.getByKey(register)).text; - if (result instanceof Array) { - result = result.join("\n").substr(0, 100); - } else if (result instanceof RecordedState) { - result = result.actionsRun.map(x => x.keysPressed.join("")).join(""); - } - - return result; - } - - async displayRegisterValue(register: string): Promise { - let result = await this.getRegisterDisplayValue(register); - result = result.replace(/\n/g, "\\n"); - vscode.window.showInformationMessage(`${register} ${result}`); - } - - async execute(modeHandler: ModeHandler): Promise { - if (this.arguments.arg !== undefined && this.arguments.arg.length > 0) { - await this.displayRegisterValue(this.arguments.arg); - } else { - const currentRegisterKeys = Register.getKeys(); - const registerKeyAndContent = new Array(); - - for (let registerKey of currentRegisterKeys) { - registerKeyAndContent.push( - { - label: registerKey, - description: await this.getRegisterDisplayValue(registerKey) - } - ); - } - - vscode.window.showQuickPick(registerKeyAndContent).then(async (val) => { - if (val) { - let result = val.description; - vscode.window.showInformationMessage(`${val.label} ${result}`); - } - }); - } - } -} \ No newline at end of file diff --git a/src/cmd_line/commands/setoptions.ts b/src/cmd_line/commands/setoptions.ts deleted file mode 100644 index de1ef6c8829..00000000000 --- a/src/cmd_line/commands/setoptions.ts +++ /dev/null @@ -1,106 +0,0 @@ -"use strict"; - -import * as node from "../node"; -import * as util from '../../util'; -import { Configuration } from '../../configuration/configuration'; - -export enum SetOptionOperator { - /* - * Set string or number option to {value}. - * White space between {option} and '=' is allowed and will be ignored. White space between '=' and {value} is not allowed. - */ - Equal, - /* - * Toggle option: set, switch it on. - * Number option: show value. - * String option: show value. - */ - Set, - /* - * Toggle option: Reset, switch it off. - */ - Reset, - /** - * Toggle option: Insert value. - */ - Invert, - /* - * Add the {value} to a number option, or append the {value} to a string option. - * When the option is a comma separated list, a comma is added, unless the value was empty. - */ - Append, - /* - * Subtract the {value} from a number option, or remove the {value} from a string option, if it is there. - */ - Subtract, - /** - * Multiply the {value} to a number option, or prepend the {value} to a string option. - */ - Multiply, - /** - * Show value of {option}. - */ - Info -} - -export interface IOptionArgs extends node.ICommandArgs { - name?: string; - operator?: SetOptionOperator; - value?: string | number | boolean; -} - -export class SetOptionsCommand extends node.CommandBase { - protected _arguments: IOptionArgs; - - constructor(args : IOptionArgs) { - super(); - this._name = 'setoptions'; - this._arguments = args; - } - - get arguments() : IOptionArgs { - return this._arguments; - } - - async execute(): Promise { - if (!this._arguments.name) { - throw new Error("Unknown option"); - } - - switch (this._arguments.operator) { - case SetOptionOperator.Set: - Configuration[this._arguments.name] = true; - break; - case SetOptionOperator.Reset: - Configuration[this._arguments.name] = false; - break; - case SetOptionOperator.Equal: - Configuration[this._arguments.name] = this._arguments.value!; - break; - case SetOptionOperator.Invert: - Configuration[this._arguments.name] = !Configuration[this._arguments.name]; - break; - case SetOptionOperator.Append: - Configuration[this._arguments.name] += this._arguments.value!; - break; - case SetOptionOperator.Subtract: - if (typeof this._arguments.value! === "number") { - Configuration[this._arguments.name] -= this._arguments.value! as number; - } else { - let initialValue = Configuration[this._arguments.name]; - Configuration[this._arguments.name] = initialValue.split(this._arguments.value! as string).join(''); - } - break; - case SetOptionOperator.Info: - let value = Configuration[this._arguments.name]; - if (value === undefined) { - await util.showError(`E518 Unknown option: ${this._arguments.name}`); - } else { - await util.showInfo(`${this._arguments.name}=${value}`); - } - break; - default: - break; - } - } -} \ No newline at end of file diff --git a/src/cmd_line/commands/sort.ts b/src/cmd_line/commands/sort.ts deleted file mode 100644 index b50d13bc7e1..00000000000 --- a/src/cmd_line/commands/sort.ts +++ /dev/null @@ -1,76 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as node from "../node"; -import * as token from "../token"; -import { ModeHandler } from "../../mode/modeHandler"; -import { TextEditor } from "../../textEditor"; -import { ModeName } from '../../mode/mode'; - -export interface ISortCommandArguments extends node.ICommandArgs { - reverse: boolean; -} - - -export class SortCommand extends node.CommandBase { - neovimCapable = true; - protected _arguments : ISortCommandArguments; - - constructor(args: ISortCommandArguments) { - super(); - this._arguments = args; - } - - get arguments() : ISortCommandArguments { - return this._arguments; - } - - async execute(modeHandler : ModeHandler): Promise { - let mode = modeHandler.vimState.currentMode; - if ([ModeName.Visual, ModeName.VisualBlock, ModeName.VisualLine].indexOf(mode) >= 0) { - const selection = modeHandler.vimState.editor.selection; - let start = selection.start; - let end = selection.end; - if (start.isAfter(end)) { - [start, end] = [end, start]; - } - await this.sortLines(start, end); - } else { - await this.sortLines(new vscode.Position(0, 0), new vscode.Position(TextEditor.getLineCount() - 1, 0)); - } - } - - async sortLines(startLine: vscode.Position, endLine: vscode.Position) { - let originalLines: String[] = []; - - for (let currentLine = startLine.line; currentLine <= endLine.line && currentLine < TextEditor.getLineCount(); currentLine++) { - originalLines.push(TextEditor.readLineAt(currentLine)); - } - - let lastLineLength = originalLines[originalLines.length - 1].length; - let sortedLines = originalLines.sort(); - - if (this._arguments.reverse) { - sortedLines.reverse(); - } - - let sortedContent = sortedLines.join("\n"); - - await TextEditor.replace(new vscode.Range(startLine.line, 0, endLine.line, lastLineLength), sortedContent); - } - - async executeWithRange(modeHandler : ModeHandler, range: node.LineRange) { - let startLine: vscode.Position; - let endLine: vscode.Position; - - if (range.left[0].type === token.TokenType.Percent) { - startLine = new vscode.Position(0, 0); - endLine = new vscode.Position(TextEditor.getLineCount() - 1, 0); - } else { - startLine = range.lineRefToPosition(modeHandler.vimState.editor, range.left, modeHandler); - endLine = range.lineRefToPosition(modeHandler.vimState.editor, range.right, modeHandler); - } - - await this.sortLines(startLine, endLine); - } -} diff --git a/src/cmd_line/commands/substitute.ts b/src/cmd_line/commands/substitute.ts deleted file mode 100644 index ff6f3d9a4b8..00000000000 --- a/src/cmd_line/commands/substitute.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* tslint:disable:no-bitwise */ -"use strict"; - -import * as vscode from "vscode"; -import * as node from "../node"; -import * as token from '../token'; -import { ModeHandler } from "../../mode/modeHandler"; -import { TextEditor } from "../../textEditor"; - -export interface ISubstituteCommandArguments extends node.ICommandArgs { - pattern: string; - replace: string; - flags: number; - count?: number; -} - -/** - * The flags that you can use for the substitute commands: - * [&] Must be the first one: Keep the flags from the previous substitute command. - * [c] Confirm each substitution. - * [e] When the search pattern fails, do not issue an error message and, in - * particular, continue in maps as if no error occurred. - * [g] Replace all occurrences in the line. Without this argument, replacement - * occurs only for the first occurrence in each line. - * [i] Ignore case for the pattern. - * [I] Don't ignore case for the pattern. - * [n] Report the number of matches, do not actually substitute. - * [p] Print the line containing the last substitute. - * [#] Like [p] and prepend the line number. - * [l] Like [p] but print the text like |:list|. - * [r] When the search pattern is empty, use the previously used search pattern - * instead of the search pattern from the last substitute or ":global". - */ -export enum SubstituteFlags { - None = 0, - KeepPreviousFlags = 0x1, - ConfirmEach = 0x2, - SuppressError = 0x4, - ReplaceAll = 0x8, - IgnoreCase = 0x10, - NoIgnoreCase = 0x20, - PrintCount = 0x40, - PrintLastMatchedLine = 0x80, - PrintLastMatchedLineWithNumber = 0x100, - PrintLastMatchedLineWithList = 0x200, - UsePreviousPattern = 0x400 -} - -export class SubstituteCommand extends node.CommandBase { - neovimCapable = true; - protected _arguments : ISubstituteCommandArguments; - - constructor(args : ISubstituteCommandArguments) { - super(); - this._name = 'search'; - this._arguments = args; - } - - get arguments() : ISubstituteCommandArguments { - return this._arguments; - } - - getRegex(args: ISubstituteCommandArguments, modeHandler: ModeHandler) { - let jsRegexFlags = ""; - - if (args.flags & SubstituteFlags.ReplaceAll) { - jsRegexFlags += "g"; - } - - if (args.flags & SubstituteFlags.IgnoreCase) { - jsRegexFlags += "i"; - } - - // If no pattern is entered, use previous search state - if (args.pattern === "") { - const prevSearchState = modeHandler.vimState.globalState.searchStatePrevious; - if (prevSearchState) { - const prevSearchString = prevSearchState[prevSearchState.length - 1].searchString; - args.pattern = prevSearchString; - } - } - - return new RegExp(args.pattern, jsRegexFlags); - } - - async replaceTextAtLine(line: number, regex: RegExp) { - const originalContent = TextEditor.readLineAt(line); - const newContent = originalContent.replace(regex, this._arguments.replace); - - if (originalContent !== newContent) { - await TextEditor.replace(new vscode.Range(line, 0, line, originalContent.length), newContent); - } - } - - async execute(modeHandler : ModeHandler): Promise { - const regex = this.getRegex(this._arguments, modeHandler); - const selection = modeHandler.vimState.editor.selection; - const line = selection.start.isBefore(selection.end) ? selection.start.line : selection.end.line; - - await this.replaceTextAtLine(line, regex); - } - - async executeWithRange(modeHandler : ModeHandler, range: node.LineRange) { - let startLine: vscode.Position; - let endLine: vscode.Position; - - if (range.left[0].type === token.TokenType.Percent) { - startLine = new vscode.Position(0, 0); - endLine = new vscode.Position(TextEditor.getLineCount() - 1, 0); - } else { - startLine = range.lineRefToPosition(modeHandler.vimState.editor, range.left, modeHandler); - endLine = range.lineRefToPosition(modeHandler.vimState.editor, range.right, modeHandler); - } - - if (this._arguments.count && this._arguments.count >= 0) { - startLine = endLine; - endLine = new vscode.Position(endLine.line + this._arguments.count - 1, 0); - } - - // TODO: Global Setting. - // TODO: There are differencies between Vim Regex and JS Regex. - - let regex = this.getRegex(this._arguments, modeHandler); - for (let currentLine = startLine.line; currentLine <= endLine.line && currentLine < TextEditor.getLineCount(); currentLine++) { - await this.replaceTextAtLine(currentLine, regex); - } - } -} diff --git a/src/cmd_line/commands/tab.ts b/src/cmd_line/commands/tab.ts deleted file mode 100644 index 4a6f0604e95..00000000000 --- a/src/cmd_line/commands/tab.ts +++ /dev/null @@ -1,118 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as node from "../node"; -import * as path from "path"; - -export enum Tab { - Next, - Previous, - First, - Last, - New, - Close, - Only, - Move -} - -export interface ITabCommandArguments extends node.ICommandArgs { - tab: Tab; - count?: number; - file?: string; -} - -// -// Implements tab -// http://vimdoc.sourceforge.net/htmldoc/tabpage.html -// -export class TabCommand extends node.CommandBase { - protected _arguments : ITabCommandArguments; - - constructor(args : ITabCommandArguments) { - super(); - this._name = 'tab'; - this._arguments = args; - } - - get arguments() : ITabCommandArguments { - return this._arguments; - } - - private executeCommandWithCount(count: number, command: string) { - for (let i = 0; i < count; i++) { - vscode.commands.executeCommand(command); - } - } - - execute() : void { - switch (this._arguments.tab) { - case Tab.Next: - if (this._arguments.count /** not undefined or 0 */) { - vscode.commands.executeCommand("workbench.action.openEditorAtIndex1"); - this.executeCommandWithCount(this._arguments.count! - 1, "workbench.action.nextEditorInGroup"); - } else { - this.executeCommandWithCount(1, "workbench.action.nextEditorInGroup"); - } - break; - case Tab.Previous: - if (this._arguments.count !== undefined && this._arguments.count <= 0) { - break; - } - - this.executeCommandWithCount(this._arguments.count || 1, "workbench.action.previousEditorInGroup"); - break; - case Tab.First: - this.executeCommandWithCount(1, "workbench.action.openEditorAtIndex1"); - break; - case Tab.Last: - this.executeCommandWithCount(1, "workbench.action.openLastEditorInGroup"); - break; - case Tab.New: - if (this.arguments.file) { - let currentFilePath = vscode.window.activeTextEditor!.document.uri.path; - let newFilePath = path.isAbsolute(this._arguments.file!) ? - this._arguments.file! : - path.join(path.dirname(currentFilePath), this._arguments.file!); - - if (newFilePath !== currentFilePath) { - let folder = vscode.Uri.file(newFilePath); - vscode.commands.executeCommand("vscode.open", folder); - } - } else { - this.executeCommandWithCount(1, "workbench.action.files.newUntitledFile"); - } - break; - case Tab.Close: - // Navigate the correct position - if (this._arguments.count === undefined) { - vscode.commands.executeCommand("workbench.action.closeActiveEditor"); - break; - } - - if (this._arguments.count === 0) { - // Wrong paramter - break; - } - - // TODO: Close Page {count}. Page count is one-based. - break; - case Tab.Only: - this.executeCommandWithCount(1, "workbench.action.closeOtherEditors"); - break; - case Tab.Move: - if (this._arguments.count !== undefined) { - if (this._arguments.count === 0) { - vscode.commands.executeCommand("activeEditorMove", { to: "first" }); - } else { - vscode.commands.executeCommand("activeEditorMove", { to: "position", amount: this._arguments.count }); - } - } else { - vscode.commands.executeCommand("activeEditorMove", { to: "last" }); - } - break; - - default: - break; - } - } -} diff --git a/src/cmd_line/commands/wall.ts b/src/cmd_line/commands/wall.ts deleted file mode 100644 index ea2c23999d9..00000000000 --- a/src/cmd_line/commands/wall.ts +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as node from "../node"; - -export interface IWallCommandArguments extends node.ICommandArgs { - bang?: boolean; - range?: node.LineRange; -} - -// -// Implements :wall (write all) -// http://vimdoc.sourceforge.net/htmldoc/editing.html#:wall -// -export class WallCommand extends node.CommandBase { - protected _arguments : IWallCommandArguments; - - constructor(args : IWallCommandArguments) { - super(); - - this._name = 'wall'; - this._arguments = args; - } - - get arguments() : IWallCommandArguments { - return this._arguments; - } - - async execute() : Promise { - // TODO : overwrite readonly files when bang? == true - await vscode.workspace.saveAll(false); - } -} \ No newline at end of file diff --git a/src/cmd_line/commands/write.ts b/src/cmd_line/commands/write.ts deleted file mode 100644 index b6ee845de74..00000000000 --- a/src/cmd_line/commands/write.ts +++ /dev/null @@ -1,87 +0,0 @@ -"use strict"; - -// XXX: use graceful-fs ?? -import * as fs from 'fs'; -import * as path from 'path'; -import * as vscode from 'vscode'; - -import * as node from '../node'; -import * as util from '../../util'; -import {ModeHandler} from "../../mode/modeHandler"; - -export interface IWriteCommandArguments extends node.ICommandArgs { - opt? : string; - optValue? : string; - bang? : boolean; - range? : node.LineRange; - file? : string; - append? : boolean; - cmd? : string; -} - -// -// Implements :write -// http://vimdoc.sourceforge.net/htmldoc/editing.html#:write -// -export class WriteCommand extends node.CommandBase { - protected _arguments : IWriteCommandArguments; - - constructor(args : IWriteCommandArguments) { - super(); - this._name = 'write'; - this._arguments = args; - } - - get arguments() : IWriteCommandArguments { - return this._arguments; - } - - async execute(modeHandler : ModeHandler) : Promise { - if (this.arguments.opt) { - util.showError("Not implemented."); - return; - } else if (this.arguments.file) { - util.showError("Not implemented."); - return; - } else if (this.arguments.append) { - util.showError("Not implemented."); - return; - } else if (this.arguments.cmd) { - util.showError("Not implemented."); - return; - } - - if (modeHandler.vimState.editor.document.isUntitled) { - await vscode.commands.executeCommand("workbench.action.files.save"); - return; - } - - try { - fs.accessSync(modeHandler.vimState.editor.document.fileName, fs.constants.W_OK); - return this.save(modeHandler); - } catch (accessErr) { - if (this.arguments.bang) { - fs.chmod(modeHandler.vimState.editor.document.fileName, 666, e => { - if (e) { - return modeHandler.setStatusBarText(e.message); - } else { - return this.save(modeHandler); - } - }); - } else { - modeHandler.setStatusBarText(accessErr.message); - } - } - } - - private async save(modeHandler : ModeHandler) : Promise { - await modeHandler.vimState.editor.document.save().then( - (ok) => { - modeHandler.setStatusBarText('"' + path.basename(modeHandler.vimState.editor.document.fileName) + - '" ' + modeHandler.vimState.editor.document.lineCount + 'L ' + - modeHandler.vimState.editor.document.getText().length + 'C written'); - }, - (e) => modeHandler.setStatusBarText(e) - ); - } -} diff --git a/src/cmd_line/commands/writequit.ts b/src/cmd_line/commands/writequit.ts deleted file mode 100644 index 43126bce375..00000000000 --- a/src/cmd_line/commands/writequit.ts +++ /dev/null @@ -1,60 +0,0 @@ -"use strict"; - -import * as node from "../node"; -import {ModeHandler} from "../../mode/modeHandler"; -import * as write from "./write"; -import * as quit from "./quit"; - -// -// Implements :writequit -// http://vimdoc.sourceforge.net/htmldoc/editing.html#write-quit -// -export interface IWriteQuitCommandArguments extends node.ICommandArgs { - // arguments - // [++opt] - opt? : string; - optValue? : string; - // wq! [++opt] - bang? : boolean; - // wq [++opt] {file} - file? : string; - // wq! [++opt] {file} - // [range]wq[!] [++opt] [file] - range?: node.LineRange; -} - -export class WriteQuitCommand extends node.CommandBase { - protected _arguments : IWriteQuitCommandArguments; - - constructor(args : IWriteQuitCommandArguments) { - super(); - this._name = "writequit"; - this._arguments = args; - } - - get arguments() : IWriteQuitCommandArguments { - return this._arguments; - } - - // Writing command. Taken as a basis from the "write.ts" file. - async execute(modeHandler : ModeHandler) : Promise { - let writeArgs : write.IWriteCommandArguments = { - opt: this.arguments.opt, - optValue: this.arguments.optValue, - bang: this.arguments.bang, - file: this.arguments.file, - range: this.arguments.range - }; - - let writeCmd = new write.WriteCommand(writeArgs); - await writeCmd.execute(modeHandler); - let quitArgs : quit.IQuitCommandArguments = { - // wq! fails when no file name is provided - bang: false, - range: this.arguments.range - }; - - let quitCmd = new quit.QuitCommand(quitArgs); - await quitCmd.execute(); - } -} \ No newline at end of file diff --git a/src/cmd_line/commands/writequitall.ts b/src/cmd_line/commands/writequitall.ts deleted file mode 100644 index 436fd64338b..00000000000 --- a/src/cmd_line/commands/writequitall.ts +++ /dev/null @@ -1,52 +0,0 @@ -"use strict"; - -import * as node from "../node"; -import {ModeHandler} from "../../mode/modeHandler"; -import * as wall from "../commands/wall"; -import * as quit from "./quit"; - -// -// Implements :writequitall -// http://vimdoc.sourceforge.net/htmldoc/editing.html#:wqall -// -export interface IWriteQuitAllCommandArguments extends node.ICommandArgs { - // arguments - // [++opt] - opt? : string; - optValue? : string; - // wqa! [++opt] - bang? : boolean; -} - -export class WriteQuitAllCommand extends node.CommandBase { - protected _arguments : IWriteQuitAllCommandArguments; - - constructor(args : IWriteQuitAllCommandArguments) { - super(); - this._name = "writequitall"; - this._arguments = args; - } - - get arguments() : IWriteQuitAllCommandArguments { - return this._arguments; - } - - // Writing command. Taken as a basis from the "write.ts" file. - async execute(modeHandler : ModeHandler) : Promise { - let writeArgs: wall.IWallCommandArguments = { - bang: this.arguments.bang - }; - - let quitArgs: quit.IQuitCommandArguments = { - // wq! fails when no file name is provided - bang: false, - }; - - const wallCmd = new wall.WallCommand(writeArgs); - await wallCmd.execute(); - - quitArgs.quitAll = true; - const quitCmd = new quit.QuitCommand(quitArgs); - await quitCmd.execute(); - } -} \ No newline at end of file diff --git a/src/cmd_line/lexer.ts b/src/cmd_line/lexer.ts deleted file mode 100644 index 3e94a183645..00000000000 --- a/src/cmd_line/lexer.ts +++ /dev/null @@ -1,223 +0,0 @@ -"use strict"; - -import {Scanner} from "./scanner"; -import {Token, TokenType} from "./token"; - -// Describes a function that can lex part of a Vim command line. -interface ILexFunction { - (state: Scanner, tokens: Token[]) : ILexFunction | null; -} - -export function lex(input : string) : Token[] { - // We use a character scanner as state for the lexer. - var state = new Scanner(input); - var tokens: Token[] = []; - var f: ILexFunction | null = LexerFunctions.lexRange; - while (f) { - // Each lexing function returns the next lexing function or null. - f = f(state, tokens); - } - return tokens; -} - -function emitToken(type: TokenType, state: Scanner): Token | null { - var content = state.emit(); - - return (content.length > 0) ? new Token(type, content) : null; -} - -module LexerFunctions { - // Starts lexing a Vim command line and delegates on other lexer functions as needed. - export function lexRange(state: Scanner, tokens: Token[]): ILexFunction | null { - while (true) { - if (state.isAtEof) { - break; - } - var c = state.next(); - switch (c) { - case ",": - case ";": - tokens.push(emitToken(TokenType.Comma, state)!); - continue; - case "%": - tokens.push(emitToken(TokenType.Percent, state)!); - continue; - case "$": - tokens.push(emitToken(TokenType.Dollar, state)!); - continue; - case ".": - tokens.push(emitToken(TokenType.Dot, state)!); - continue; - case "/": - return lexForwardSearch; - case "?": - return lexReverseSearch; - case "0": - case "1": - case "2": - case "3": - case "4": - case "5": - case "6": - case "7": - case "8": - case "9": - return lexLineRef; - case "+": - tokens.push(emitToken(TokenType.Plus, state)!); - continue; - case "-": - tokens.push(emitToken(TokenType.Minus, state)!); - continue; - case "*": - state.emit(); - tokens.push(new Token(TokenType.SelectionFirstLine, '<')!); - tokens.push(new Token(TokenType.Comma, ',')!); - tokens.push(new Token(TokenType.SelectionLastLine, '>')!); - continue; - case "'": - return lexMark; - default: - return lexCommand; - } - } - - return null; - } - - function lexMark(state: Scanner, tokens: Token[]): ILexFunction | null { - // The first token has already been lexed. - if (state.isAtEof) { - return null; - } - - var c = state.next(); - switch (c) { - case '<': - tokens.push(emitToken(TokenType.SelectionFirstLine, state) !); - break; - case '>': - tokens.push(emitToken(TokenType.SelectionLastLine, state) !); - break; - default: - if (/[a-zA-Z]/.test(c)) { - state.emit(); - tokens.push(new Token(TokenType.Mark, c) !); - } else { - state.backup(); - } - break; - } - - return lexRange; - } - - function lexLineRef(state : Scanner, tokens: Token[]): ILexFunction | null { - // The first digit has already been lexed. - while (true) { - if (state.isAtEof) { - tokens.push(emitToken(TokenType.LineNumber, state)!); - return null; - } - - var c = state.next(); - switch (c) { - case "0": - case "1": - case "2": - case "3": - case "4": - case "5": - case "6": - case "7": - case "8": - case "9": - continue; - default: - state.backup(); - tokens.push(emitToken(TokenType.LineNumber, state)!); - return lexRange; - } - } - } - - function lexCommand(state : Scanner, tokens : Token[]): ILexFunction | null { - // The first character of the command's name has already been lexed. - while (true) { - if (state.isAtEof) { - tokens.push(emitToken(TokenType.CommandName, state)!); - break; - } - var c = state.next(); - var lc = c.toLowerCase(); - if (lc >= "a" && lc <= "z") { - continue; - } else { - state.backup(); - tokens.push(emitToken(TokenType.CommandName, state)!); - while (!state.isAtEof) { - state.next(); - } - // TODO(guillermooo): We need to parse multiple commands. - var args = emitToken(TokenType.CommandArgs, state); - if (args) { - tokens.push(args); - } - break; - } - } - return null; - } - - function lexForwardSearch(state : Scanner, tokens : Token[]): ILexFunction { - // The first slash has already been lexed. - state.skip("/"); // XXX: really? - var escaping = false; - var searchTerm = ""; - while (!state.isAtEof) { - var c = state.next(); - if (c === "/" && !escaping) { - break; - } - if (c === "\\") { - escaping = true; - continue; - } else { - escaping = false; - } - searchTerm += c !== "\\" ? c : "\\\\"; - } - tokens.push(new Token(TokenType.ForwardSearch, searchTerm)); - state.ignore(); - if (!state.isAtEof) { - state.skip("/"); - } - return lexRange; - } - - function lexReverseSearch(state : Scanner, tokens : Token[]): ILexFunction { - // The first question mark has already been lexed. - state.skip("?"); // XXX: really? - var escaping = false; - var searchTerm = ""; - while (!state.isAtEof) { - var c = state.next(); - if (c === "?" && !escaping) { - break; - } - if (c === "\\") { - escaping = true; - continue; - } else { - escaping = false; - } - searchTerm += c !== "\\" ? c : "\\\\"; - } - tokens.push(new Token(TokenType.ReverseSearch, searchTerm)); - state.ignore(); - if (!state.isAtEof) { - state.skip("?"); - } - return lexRange; - } -} diff --git a/src/cmd_line/main.ts b/src/cmd_line/main.ts deleted file mode 100644 index 8b03075d471..00000000000 --- a/src/cmd_line/main.ts +++ /dev/null @@ -1,57 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as parser from "./parser"; -import {ModeHandler} from "../mode/modeHandler"; -import { Neovim } from "../neovim/nvimUtil"; -import { Configuration } from "../configuration/configuration"; - -// Shows the vim command line. -export async function showCmdLine(initialText: string, modeHandler : ModeHandler): Promise { - if (!vscode.window.activeTextEditor) { - console.log("No active document."); - return; - } - - - const options : vscode.InputBoxOptions = { - prompt: "Vim command line", - value: initialText, - ignoreFocusOut: true, - valueSelection: [initialText.length, initialText.length] - }; - - try { - const cmdString = await vscode.window.showInputBox(options); - await runCmdLine(cmdString!, modeHandler); - return; - } catch (e) { - modeHandler.setStatusBarText(e.toString()); - return; - } -} - -export async function runCmdLine(command : string, modeHandler : ModeHandler) : Promise { - if (!command || command.length === 0) { - return; - } - - try { - var cmd = parser.parse(command); - if (Configuration.enableNeovim && (!cmd.command || (cmd.command && cmd.command.neovimCapable))) { - await Neovim.command(modeHandler.vimState, command).then(() => { - console.log("Substituted for neovim command"); - }).catch((err) => console.log(err)); - } else { - await cmd.execute(modeHandler.vimState.editor, modeHandler); - } - return; - } catch (e) { - if (Configuration.enableNeovim) { - await Neovim.command(modeHandler.vimState, command).then(() => { - console.log("SUCCESS"); - }).catch((err) => console.log(err)); - } - return; - } -} diff --git a/src/cmd_line/node.ts b/src/cmd_line/node.ts deleted file mode 100644 index 3abe1f002d5..00000000000 --- a/src/cmd_line/node.ts +++ /dev/null @@ -1,145 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import * as token from "./token"; -import {ModeHandler} from "../mode/modeHandler"; - -export class LineRange { - left : token.Token[]; - separator : token.Token; - right : token.Token[]; - - constructor() { - this.left = []; - this.right = []; - } - - addToken(tok : token.Token) : void { - if (tok.type === token.TokenType.Comma) { - this.separator = tok; - return; - } - - if (!this.separator) { - if (this.left.length > 0 && tok.type !== token.TokenType.Offset) { - // XXX: is this always this error? - throw Error("not a Vim command"); - } - this.left.push(tok); - } else { - if (this.right.length > 0 && tok.type !== token.TokenType.Offset) { - // XXX: is this always this error? - throw Error("not a Vim command"); - } - this.right.push(tok); - } - } - - get isEmpty() : boolean { - return this.left.length === 0 && this.right.length === 0 && !this.separator; - } - - toString() : string { - return this.left.toString() + this.separator.content + this.right.toString(); - } - - execute(document : vscode.TextEditor, modeHandler: ModeHandler) : void { - if (this.isEmpty) { - return; - } - var lineRef = this.right.length === 0 ? this.left : this.right; - var pos = this.lineRefToPosition(document, lineRef, modeHandler); - let vimState = modeHandler.vimState; - vimState.cursorPosition = vimState.cursorPosition.setLocation(pos.line, pos.character); - vimState.cursorStartPosition = vimState.cursorPosition; - modeHandler.updateView(modeHandler.vimState); - } - - lineRefToPosition(doc : vscode.TextEditor, toks : token.Token[], modeHandler: ModeHandler) : vscode.Position { - var first = toks[0]; - switch (first.type) { - case token.TokenType.Dollar: - case token.TokenType.Percent: - return new vscode.Position(doc.document.lineCount, 0); - case token.TokenType.Dot: - return new vscode.Position(doc.selection.active.line, 0); - case token.TokenType.LineNumber: - var line = Number.parseInt(first.content); - line = Math.max(0, line - 1); - line = Math.min(doc.document.lineCount, line); - return new vscode.Position(line, 0); - case token.TokenType.SelectionFirstLine: - let startLine = Math.min.apply(null, doc.selections.map(selection => - selection.start.isBeforeOrEqual(selection.end) ? selection.start.line : selection.end.line)); - return new vscode.Position(startLine, 0); - case token.TokenType.SelectionLastLine: - let endLine = Math.max.apply(null, doc.selections.map(selection => - selection.start.isAfter(selection.end) ? selection.start.line : selection.end.line)); - return new vscode.Position(endLine, 0); - case token.TokenType.Mark: - return modeHandler.vimState.historyTracker.getMark(first.content).position; - default: - throw new Error("not implemented"); - } - } -} - -export class CommandLine { - range : LineRange; - command : CommandBase; - - constructor() { - this.range = new LineRange(); - } - - get isEmpty() : boolean { - return this.range.isEmpty && !this.command; - } - - toString() : string { - return ":" + this.range.toString() + " " + this.command.toString(); - } - - async execute(document : vscode.TextEditor, modeHandler : ModeHandler) : Promise { - if (!this.command) { - this.range.execute(document, modeHandler); - return; - } - - if (this.range.isEmpty) { - await this.command.execute(modeHandler); - } else { - await this.command.executeWithRange(modeHandler, this.range); - } - - } -} - -export interface ICommandArgs { - bang? : boolean; - range? : LineRange; -} - -export abstract class CommandBase { - - public neovimCapable = false; - protected get activeTextEditor() { - return vscode.window.activeTextEditor; - } - - get name() : string { - return this._name; - } - protected _name : string; - - get arguments() : ICommandArgs { - return this._arguments; - } - protected _arguments : ICommandArgs; - - abstract execute(modeHandler : ModeHandler) : void; - - executeWithRange(modeHandler : ModeHandler, range: LineRange) : void { - throw new Error("Not implemented!"); - } -} diff --git a/src/cmd_line/parser.ts b/src/cmd_line/parser.ts deleted file mode 100644 index 3b8a44af562..00000000000 --- a/src/cmd_line/parser.ts +++ /dev/null @@ -1,108 +0,0 @@ -"use strict"; - -import * as token from './token'; -import * as node from './node'; -import * as lexer from './lexer'; -import {commandParsers} from './subparser'; - -interface IParseFunction { - (state : ParserState, command : node.CommandLine): IParseFunction | null; -} - -export function parse(input : string) : node.CommandLine { - var cmd = new node.CommandLine(); - var f: IParseFunction | null = parseLineRange; - let state : ParserState = new ParserState(input); - while (f) { - f = f(state, cmd); - } - return cmd; -} - -function parseLineRange(state: ParserState, commandLine: node.CommandLine): IParseFunction | null { - while (true) { - let tok = state.next(); - switch (tok.type) { - case token.TokenType.Eof: - return null; - case token.TokenType.Dot: - case token.TokenType.Dollar: - case token.TokenType.Percent: - case token.TokenType.Comma: - case token.TokenType.LineNumber: - case token.TokenType.SelectionFirstLine: - case token.TokenType.SelectionLastLine: - case token.TokenType.Mark: - commandLine.range.addToken(tok); - continue; - case token.TokenType.CommandName: - state.backup(); - return parseCommand; - // commandLine.command = new node.CommandLineCommand(tok.content, null); - // continue; - default: - console.warn("skipping token " + "Token(" + tok.type + ",{" + tok.content + "})"); - return null; - } - } -} - -function parseCommand(state: ParserState, commandLine: node.CommandLine): IParseFunction | null { - while (!state.isAtEof) { - var tok = state.next(); - switch (tok.type) { - case token.TokenType.CommandName: - var commandParser = (commandParsers as any)[tok.content]; - if (!commandParser) { - throw new Error("Not implemented or not a valid command"); - } - // TODO: Pass the args, but keep in mind there could be multiple - // commands, not just one. - var argsTok = state.next(); - var args = argsTok.type === token.TokenType.CommandArgs ? argsTok.content : null; - commandLine.command = commandParser(args); - return null; - default: - throw new Error("Not implemented"); - } - } - - if (!state.isAtEof) { - state.backup(); - return parseCommand; - } else { - return null; - } -} - -// Keeps track of parsing state. -class ParserState { - tokens : token.Token[] = []; - pos : number = 0; - - constructor(input : string) { - this.lex(input); - } - - private lex(input : string) { - this.tokens = lexer.lex(input); - } - - next() : token.Token { - if (this.pos >= this.tokens.length) { - this.pos = this.tokens.length; - return new token.Token(token.TokenType.Eof, '__EOF__'); - } - let tok = this.tokens[this.pos]; - this.pos++; - return tok; - } - - backup() : void { - this.pos--; - } - - get isAtEof() { - return this.pos >= this.tokens.length; - } -} diff --git a/src/cmd_line/scanner.ts b/src/cmd_line/scanner.ts deleted file mode 100644 index 89bac7ca4f0..00000000000 --- a/src/cmd_line/scanner.ts +++ /dev/null @@ -1,153 +0,0 @@ -"use strict"; - -// Provides state and behavior to scan an input string character by character. -export class Scanner { - static EOF : string = "__EOF__"; - start : number = 0; - pos : number = 0; - input : string; - - constructor(input : string) { - this.input = input; - } - - // Returns the next character in the input, or EOF. - next() : string { - if (this.isAtEof) { - this.pos = this.input.length; - return Scanner.EOF; - } - let c = this.input[this.pos]; - this.pos++; - return c; - } - - // Returns the next word in the input, or EOF. - nextWord(wordSeparators: string[] = [" ", "\t"]): string { - this.skipRun(wordSeparators); - - if (this.isAtEof) { - this.pos = this.input.length; - return Scanner.EOF; - } - - let result = ""; - let c: string | undefined = undefined; - - while (!this.isAtEof) { - c = this.next(); - - if (c === Scanner.EOF || wordSeparators.indexOf(c) !== -1) { - break; - } - - result += c; - } - - if (c && wordSeparators.indexOf(c) !== -1) { - this.backup(); - } - - this.ignore(); - return result; - } - - // Returns whether we've reached EOF. - get isAtEof() : boolean { - return this.pos >= this.input.length; - } - - // Ignores the span of text between the current start and the current position. - ignore() : void { - this.start = this.pos; - } - - // Returns the span of text between the current start and the current position. - emit() : string { - let s = this.input.substring(this.start, this.pos); - this.ignore(); - return s; - } - - // Returns the text from the current position to the end. - remaining() : string { - while (!this.isAtEof) { - this.next(); - } - return this.emit(); - } - - backup(): void { - this.pos--; - } - - // skips over c and ignores the text span - skip(c : string) : void { - if (this.isAtEof) { - return; - } - var s = this.next(); - while (!this.isAtEof) { - if (s !== c) { - break; - } - s = this.next(); - } - this.backup(); - this.ignore(); - } - - // skips text while any of chars matches and ignores the text span - skipRun(chars : string[]) : void { - if (this.isAtEof) { - return; - } - while (!this.isAtEof) { - var c = this.next(); - if (chars.indexOf(c) === -1) { - break; - } - } - this.backup(); - this.ignore(); - } - - // skips over whitespace (tab, space) and ignores the text span - skipWhiteSpace(): void { - if (this.isAtEof) { - return; - } - let c: string | null = null; - - while (!this.isAtEof) { - c = this.next(); - if (c === " " || c === "\t") { - continue; - } - break; - } - - if (c !== Scanner.EOF && c !== ' ' && c !== '\t') { - this.backup(); - } - this.ignore(); - } - - expect(value : string) : void { - if (!this.input.substring(this.pos).startsWith(value)) { - throw new Error("Unexpected character."); - } - this.pos += value.length; - } - - expectOneOf(values : string[]) : void { - let match = values.filter(s => this.input.substr(this.pos).startsWith(s)); - if (match.length !== 1) { - if (match.length > 1) { - throw new Error("too many maches"); - } - throw new Error("Unexpected character."); - } - this.pos += match[0].length; - } -} diff --git a/src/cmd_line/subparser.ts b/src/cmd_line/subparser.ts deleted file mode 100644 index 78e9c050f19..00000000000 --- a/src/cmd_line/subparser.ts +++ /dev/null @@ -1,101 +0,0 @@ -"use strict"; - -import { parseQuitCommandArgs, parseQuitAllCommandArgs } from './subparsers/quit'; -import { parseWriteCommandArgs } from './subparsers/write'; -import { parseWallCommandArgs } from './subparsers/wall'; -import { parseNohlCommandArgs } from './subparsers/nohl'; -import { parseWriteQuitCommandArgs } from './subparsers/writequit'; -import { parseWriteQuitAllCommandArgs } from './subparsers/writequitall'; -import * as tabCmd from './subparsers/tab'; -import * as fileCmd from './subparsers/file'; -import { parseOptionsCommandArgs } from './subparsers/setoptions'; -import { parseSubstituteCommandArgs } from './subparsers/substitute'; -import { parseReadCommandArgs } from './subparsers/read'; -import { parseRegisterCommandArgs } from './subparsers/register'; -import { parseDeleteRangeLinesCommandArgs } from './subparsers/deleteRange'; -import { parseSortCommandArgs } from './subparsers/sort'; -import { parseCloseCommandArgs } from './subparsers/close'; - -// maps command names to parsers for said commands. -export const commandParsers = { - w: parseWriteCommandArgs, - write: parseWriteCommandArgs, - - wa: parseWallCommandArgs, - wall: parseWallCommandArgs, - - nohlsearch: parseNohlCommandArgs, - noh: parseNohlCommandArgs, - nohl: parseNohlCommandArgs, - - close: parseCloseCommandArgs, - clo: parseCloseCommandArgs, - - quit: parseQuitCommandArgs, - q: parseQuitCommandArgs, - - qa: parseQuitAllCommandArgs, - qall: parseQuitAllCommandArgs, - - wq: parseWriteQuitCommandArgs, - writequit: parseWriteQuitCommandArgs, - x: parseWriteQuitCommandArgs, - - wqa: parseWriteQuitAllCommandArgs, - wqall: parseWriteQuitAllCommandArgs, - xa: parseWriteQuitAllCommandArgs, - xall: parseWriteQuitAllCommandArgs, - - tabn: tabCmd.parseTabNCommandArgs, - tabnext: tabCmd.parseTabNCommandArgs, - - tabp: tabCmd.parseTabPCommandArgs, - tabprevious: tabCmd.parseTabPCommandArgs, - tabN: tabCmd.parseTabPCommandArgs, - tabNext: tabCmd.parseTabPCommandArgs, - - tabfirst: tabCmd.parseTabFirstCommandArgs, - tabfir: tabCmd.parseTabFirstCommandArgs, - - tablast: tabCmd.parseTabLastCommandArgs, - tabl: tabCmd.parseTabLastCommandArgs, - - tabe: tabCmd.parseTabNewCommandArgs, - tabedit: tabCmd.parseTabNewCommandArgs, - tabnew: tabCmd.parseTabNewCommandArgs, - - tabclose: tabCmd.parseTabCloseCommandArgs, - tabc: tabCmd.parseTabCloseCommandArgs, - - tabo: tabCmd.parseTabOnlyCommandArgs, - tabonly: tabCmd.parseTabOnlyCommandArgs, - - tabm: tabCmd.parseTabMovementCommandArgs, - - e: fileCmd.parseEditFileCommandArgs, - - s: parseSubstituteCommandArgs, - - vs: fileCmd.parseEditFileInNewWindowCommandArgs, - vsp: fileCmd.parseEditFileInNewWindowCommandArgs, - sp: fileCmd.parseEditFileInNewWindowCommandArgs, - split: fileCmd.parseEditFileInNewWindowCommandArgs, - vsplit: fileCmd.parseEditFileInNewWindowCommandArgs, - ne: fileCmd.parseEditNewFileInNewWindowCommandArgs, - vne: fileCmd.parseEditNewFileInNewWindowCommandArgs, - new: fileCmd.parseEditNewFileInNewWindowCommandArgs, - vnew: fileCmd.parseEditNewFileInNewWindowCommandArgs, - - set: parseOptionsCommandArgs, - se: parseOptionsCommandArgs, - - read: parseReadCommandArgs, - r: parseReadCommandArgs, - - reg: parseRegisterCommandArgs, - - d: parseDeleteRangeLinesCommandArgs, - - sort: parseSortCommandArgs, - -}; diff --git a/src/cmd_line/subparsers/close.ts b/src/cmd_line/subparsers/close.ts deleted file mode 100644 index 5f7d653e71d..00000000000 --- a/src/cmd_line/subparsers/close.ts +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; - -import * as node from "../commands/close"; -import {Scanner} from '../scanner'; -import {VimError, ErrorCode} from '../../error'; - -export function parseCloseCommandArgs(args : string) : node.CloseCommand { - if (!args) { - return new node.CloseCommand({}); - } - var scannedArgs : node.ICloseCommandArguments = {}; - var scanner = new Scanner(args); - const c = scanner.next(); - if (c === '!') { - scannedArgs.bang = true; - scanner.ignore(); - } else if (c !== ' ') { - throw VimError.fromCode(ErrorCode.E488); - } - scanner.skipWhiteSpace(); - if (!scanner.isAtEof) { - throw VimError.fromCode(ErrorCode.E488); - } - return new node.CloseCommand(scannedArgs); -} \ No newline at end of file diff --git a/src/cmd_line/subparsers/deleteRange.ts b/src/cmd_line/subparsers/deleteRange.ts deleted file mode 100644 index 1c7c2dd8c09..00000000000 --- a/src/cmd_line/subparsers/deleteRange.ts +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; - -import * as node from "../commands/deleteRange"; -import {Scanner} from '../scanner'; - -export function parseDeleteRangeLinesCommandArgs(args: string): node.DeleteRangeCommand { - if (!args) { - return new node.DeleteRangeCommand({}); - } - - let scanner = new Scanner(args); - let register = scanner.nextWord(); - - return new node.DeleteRangeCommand({ - register: register - }); -} \ No newline at end of file diff --git a/src/cmd_line/subparsers/file.ts b/src/cmd_line/subparsers/file.ts deleted file mode 100644 index 7f1113e5793..00000000000 --- a/src/cmd_line/subparsers/file.ts +++ /dev/null @@ -1,48 +0,0 @@ -"use strict"; - -import * as node from "../commands/file"; -import {Scanner} from '../scanner'; - -export function parseEditFileCommandArgs(args: string): node.FileCommand { - if (!args) { - return new node.FileCommand({name: ""}); - } - - let scanner = new Scanner(args); - let bang; - const c = scanner.next(); - bang = (c === '!'); - if (scanner.isAtEof) { - return new node.FileCommand({name: "", bang: bang}); - } - - let name = scanner.nextWord(); - - return new node.FileCommand({ - name: name.trim(), - position: node.FilePosition.CurrentWindow, - bang: bang - }); -} - -// Note that this isn't really implemented. -export function parseEditNewFileInNewWindowCommandArgs(args: string): node.FileCommand { - return new node.FileCommand({ - name: undefined, - position: node.FilePosition.NewWindow - }); -} - -export function parseEditFileInNewWindowCommandArgs(args: string): node.FileCommand { - let name = ""; - - if (args) { - let scanner = new Scanner(args); - name = scanner.nextWord(); - } - - return new node.FileCommand({ - name: name, - position: node.FilePosition.NewWindow - }); -} diff --git a/src/cmd_line/subparsers/nohl.ts b/src/cmd_line/subparsers/nohl.ts deleted file mode 100644 index 1521cb5e746..00000000000 --- a/src/cmd_line/subparsers/nohl.ts +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -import * as node from "../commands/nohl"; - -export function parseNohlCommandArgs(args: string): node.NohlCommand { - return new node.NohlCommand({}); -} diff --git a/src/cmd_line/subparsers/quit.ts b/src/cmd_line/subparsers/quit.ts deleted file mode 100644 index 00ec7247ec5..00000000000 --- a/src/cmd_line/subparsers/quit.ts +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; - -import * as node from "../commands/quit"; -import {Scanner} from '../scanner'; -import {VimError, ErrorCode} from '../../error'; - -export function parseQuitCommandArgs(args : string) : node.QuitCommand { - if (!args) { - return new node.QuitCommand({}); - } - var scannedArgs : node.IQuitCommandArguments = {}; - var scanner = new Scanner(args); - const c = scanner.next(); - if (c === '!') { - scannedArgs.bang = true; - scanner.ignore(); - } else if (c !== ' ') { - throw VimError.fromCode(ErrorCode.E488); - } - scanner.skipWhiteSpace(); - if (!scanner.isAtEof) { - throw VimError.fromCode(ErrorCode.E488); - } - return new node.QuitCommand(scannedArgs); -} - -export function parseQuitAllCommandArgs(args: string): node.QuitCommand { - let command = parseQuitCommandArgs(args); - command.arguments.quitAll = true; - return command; -} \ No newline at end of file diff --git a/src/cmd_line/subparsers/read.ts b/src/cmd_line/subparsers/read.ts deleted file mode 100644 index 645036877e3..00000000000 --- a/src/cmd_line/subparsers/read.ts +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; - -import {ReadCommand, IReadCommandArguments} from '../commands/read'; -import {Scanner} from '../scanner'; - -export function parseReadCommandArgs(args : string) : ReadCommand { - if (!args) { - throw Error('Expected arguments.'); - } - - var scannedArgs : IReadCommandArguments = {}; - var scanner = new Scanner(args); - - scanner.skipWhiteSpace(); - let c = scanner.next(); - // read command has 2 forms - 'read ' and 'read! ' - if (c === '!') { - scanner.ignore(); - scanner.skipWhiteSpace(); - scannedArgs.cmd = scanner.remaining(); - if (!scannedArgs.cmd || scannedArgs.cmd.length === 0) { - throw Error('Expected shell command.'); - } - } else { - scannedArgs.file = scanner.remaining(); - if (!scannedArgs.file || scannedArgs.file.length === 0) { - throw Error('Expected file path.'); - } - } - - return new ReadCommand(scannedArgs); -} diff --git a/src/cmd_line/subparsers/register.ts b/src/cmd_line/subparsers/register.ts deleted file mode 100644 index e9554d32a3a..00000000000 --- a/src/cmd_line/subparsers/register.ts +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; - -import * as node from "../commands/register"; -import {Scanner} from '../scanner'; - -export function parseRegisterCommandArgs(args: string): node.RegisterCommand { - if (!args) { - return new node.RegisterCommand({}); - } - - let scanner = new Scanner(args); - let name = scanner.nextWord(); - - return new node.RegisterCommand({ - arg: name - }); -} \ No newline at end of file diff --git a/src/cmd_line/subparsers/setoptions.ts b/src/cmd_line/subparsers/setoptions.ts deleted file mode 100644 index 761a70435f3..00000000000 --- a/src/cmd_line/subparsers/setoptions.ts +++ /dev/null @@ -1,78 +0,0 @@ -"use strict"; - -import * as node from "../commands/setoptions"; -import {Scanner} from '../scanner'; - -export function parseOption(args: string) : node.IOptionArgs { - let scanner = new Scanner(args); - scanner.skipWhiteSpace(); - - if (scanner.isAtEof) { - return {}; - } - - let optionName = scanner.nextWord("?!&=:^+-".split("")); - - if (optionName.startsWith("no")) { - return { - name: optionName.substring(2, optionName.length), - operator: node.SetOptionOperator.Reset - }; - } - - if (optionName.startsWith("inv")) { - return { - name: optionName.substring(3, optionName.length), - operator: node.SetOptionOperator.Invert - }; - } - - scanner.skipWhiteSpace(); - - if (scanner.isAtEof) { - return { - name: optionName, - operator: node.SetOptionOperator.Set - }; - } - - let operator = scanner.next(); - let optionArgs: node.IOptionArgs = { - name: optionName, - value: scanner.nextWord([]) - }; - - switch (operator) { - case "=": - case ":": - optionArgs.operator = node.SetOptionOperator.Equal; - break; - case "!": - optionArgs.operator = node.SetOptionOperator.Invert; - break; - case "^": - optionArgs.operator = node.SetOptionOperator.Multiply; - break; - case "+": - optionArgs.operator = node.SetOptionOperator.Append; - break; - case "-": - optionArgs.operator = node.SetOptionOperator.Subtract; - break; - case "?": - optionArgs.operator = node.SetOptionOperator.Info; - break; - case "&": - optionArgs.operator = node.SetOptionOperator.Reset; - break; - default: - throw new Error("Unknown option"); - } - - return optionArgs; -} - -export function parseOptionsCommandArgs(args : string) : node.SetOptionsCommand { - let option = parseOption(args); - return new node.SetOptionsCommand(option); -} \ No newline at end of file diff --git a/src/cmd_line/subparsers/sort.ts b/src/cmd_line/subparsers/sort.ts deleted file mode 100644 index a16efada18f..00000000000 --- a/src/cmd_line/subparsers/sort.ts +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; - -import * as node from "../commands/sort"; - -export function parseSortCommandArgs(args : string) : node.SortCommand { - - const reverse = args !== null && args.indexOf("!") >= 0; - - return new node.SortCommand({ reverse }); - -} diff --git a/src/cmd_line/subparsers/substitute.ts b/src/cmd_line/subparsers/substitute.ts deleted file mode 100644 index 735434720fc..00000000000 --- a/src/cmd_line/subparsers/substitute.ts +++ /dev/null @@ -1,164 +0,0 @@ -/* tslint:disable:no-bitwise */ -"use strict"; - -import * as node from "../commands/substitute"; -import {Scanner} from '../scanner'; - -function isValidDelimiter(char: string): boolean { - return !! (/^[^\w\s\\|"]{1}$/g.exec(char)); -} - -function parsePattern(pattern: string, scanner: Scanner, delimiter: string): [string, boolean] { - if (scanner.isAtEof) { - return [pattern, false]; - } else { - let currentChar = scanner.next(); - - if (currentChar === delimiter) { - // TODO skip delimiter - return [pattern, true]; - } else if (currentChar === "\\") { - if (!scanner.isAtEof) { - currentChar = scanner.next(); - - if (currentChar !== delimiter) { - switch (currentChar) { - case 'r': - pattern += '\r'; - break; - case 'n': - pattern += '\n'; - break; - default: - pattern += "\\"; - pattern += currentChar; - break; - } - } - } - - return parsePattern(pattern, scanner, delimiter); - } else { - pattern += currentChar; - return parsePattern(pattern, scanner, delimiter); - } - } -} - -function parseSubstituteFlags(scanner: Scanner): number { - let flags: number = 0; - let index = 0; - while (true) { - if (scanner.isAtEof) { - break; - } - - let c = scanner.next(); - switch (c) { - case "&": - if (index === 0) { - flags = flags | node.SubstituteFlags.KeepPreviousFlags; - } else { - // Raise Error - return node.SubstituteFlags.None; - } - break; - case "c": - flags = flags | node.SubstituteFlags.ConfirmEach; - break; - case "e": - flags = flags | node.SubstituteFlags.SuppressError; - break; - case "g": - flags = flags | node.SubstituteFlags.ReplaceAll; - break; - case "i": - flags = flags | node.SubstituteFlags.IgnoreCase; - break; - case "I": - flags = flags | node.SubstituteFlags.NoIgnoreCase; - break; - case "n": - flags = flags | node.SubstituteFlags.PrintCount; - break; - case "p": - flags = flags | node.SubstituteFlags.PrintLastMatchedLine; - break; - case "#": - flags = flags | node.SubstituteFlags.PrintLastMatchedLineWithNumber; - break; - case "l": - flags = flags | node.SubstituteFlags.PrintLastMatchedLineWithList; - break; - case "r": - flags = flags | node.SubstituteFlags.UsePreviousPattern; - break; - default: - return node.SubstituteFlags.None; - } - - index++; - } - - return flags; -} - -function parseCount(scanner: Scanner): number { - let countStr = ""; - - while (true) { - if (scanner.isAtEof) { - break; - } - countStr += scanner.next(); - } - - let count = Number.parseInt(countStr); - - // TODO: If count is not valid number, raise error - return Number.isInteger(count) ? count : -1; -} -/** - * Substitute - * :[range]s[ubstitute]/{pattern}/{string}/[flags] [count] - * For each line in [range] replace a match of {pattern} with {string}. - * {string} can be a literal string, or something special; see |sub-replace-special|. - */ -export function parseSubstituteCommandArgs(args : string) : node.SubstituteCommand { - let delimiter = args[0]; - - if (isValidDelimiter(delimiter)) { - let scanner = new Scanner(args.substr(1, args.length - 1)); - let [searchPattern, searchDelimiter] = parsePattern("", scanner, delimiter); - - if (searchDelimiter) { - let replaceString = parsePattern("", scanner, delimiter)[0]; - - scanner.skipWhiteSpace(); - let flags = parseSubstituteFlags(scanner); - scanner.skipWhiteSpace(); - let count = parseCount(scanner); - return new node.SubstituteCommand({ - pattern: searchPattern, - replace: replaceString, - flags: flags, - count: count - }); - - } else { - return new node.SubstituteCommand({ - pattern: searchPattern, - replace: "", - flags: node.SubstituteFlags.None, - count: 0 - }); - } - } - - // TODO(rebornix): Can this ever happen? - return new node.SubstituteCommand({ - pattern: "", - replace: "", - flags: node.SubstituteFlags.None - }); -} diff --git a/src/cmd_line/subparsers/tab.ts b/src/cmd_line/subparsers/tab.ts deleted file mode 100644 index df642250420..00000000000 --- a/src/cmd_line/subparsers/tab.ts +++ /dev/null @@ -1,112 +0,0 @@ -"use strict"; - -import * as node from "../commands/tab"; -import {Scanner} from '../scanner'; - -function parseCount(args: string): number | undefined { - if (!args) { - return undefined; - } - - let scanner = new Scanner(args); - scanner.skipWhiteSpace(); - - if (scanner.isAtEof) { - return undefined; - } - - let c = scanner.next(); - let count = Number.parseInt(c); - - if (Number.isInteger(count) && count >= 0 ) { - if (count > 999) { - count = 999; - } - - return count; - } else { - throw new Error(`Invalid tab number: ${c}!`); - } -} - -/** - * :tabn[ext] Go to the next tab page. - * :tabn[ext] {count} Go to tab page {count}. - */ -export function parseTabNCommandArgs(args : string) : node.TabCommand { - return new node.TabCommand({ - tab: node.Tab.Next, - count: parseCount(args) - }); -} - -/** - * :tabp[revious] Go to the previous tab page. Wraps around from the first one to the last one. - * :tabp[revious] {count} Go {count} tab pages back. - */ -export function parseTabPCommandArgs(args : string) : node.TabCommand { - return new node.TabCommand({ - tab: node.Tab.Previous, - count: parseCount(args) - }); -} - -/** - * :tabfir[st] Go to the first tab page. - */ -export function parseTabFirstCommandArgs(args : string) : node.TabCommand { - return new node.TabCommand({ - tab: node.Tab.First - }); -} - -/** - * :tabl[ast] Go to the last tab page. - */ -export function parseTabLastCommandArgs(args : string) : node.TabCommand { - return new node.TabCommand({ - tab: node.Tab.Last - }); -} - -/** - * :tabe[dit] - * :tabnew Open a new tab page with an empty window, after the current tab page. - */ -export function parseTabNewCommandArgs(args: string) : node.TabCommand { - let name = ""; - - if (args) { - let scanner = new Scanner(args); - name = scanner.nextWord(); - } - - return new node.TabCommand({ - tab: node.Tab.New, - file: name - }); -} - -/** - * :tabc[lose][!] Close current tab page. - * :tabc[lose][!] {count}. Close tab page {count}. - */ -export function parseTabCloseCommandArgs(args: string) : node.TabCommand { - return new node.TabCommand({ - tab: node.Tab.Close, - count: parseCount(args) - }); -} - -export function parseTabOnlyCommandArgs(args: string) : node.TabCommand { - return new node.TabCommand({ - tab: node.Tab.Only - }); -} - -export function parseTabMovementCommandArgs(args: string) : node.TabCommand { - return new node.TabCommand({ - tab: node.Tab.Move, - count: parseCount(args) - }); -} \ No newline at end of file diff --git a/src/cmd_line/subparsers/wall.ts b/src/cmd_line/subparsers/wall.ts deleted file mode 100644 index 748498db363..00000000000 --- a/src/cmd_line/subparsers/wall.ts +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; - -import * as node from "../commands/wall"; -import { Scanner} from '../scanner'; -import { VimError, ErrorCode } from '../../error'; - -export function parseWallCommandArgs(args : string) : node.WallCommand { - if (!args) { - return new node.WallCommand({}); - } - var scannedArgs : node.IWallCommandArguments = {}; - var scanner = new Scanner(args); - const c = scanner.next(); - if (c === '!') { - scannedArgs.bang = true; - scanner.ignore(); - } else if (c !== ' ') { - throw VimError.fromCode(ErrorCode.E488); - } - scanner.skipWhiteSpace(); - if (!scanner.isAtEof) { - throw VimError.fromCode(ErrorCode.E488); - } - return new node.WallCommand(scannedArgs); -} diff --git a/src/cmd_line/subparsers/write.ts b/src/cmd_line/subparsers/write.ts deleted file mode 100644 index f6588d3b285..00000000000 --- a/src/cmd_line/subparsers/write.ts +++ /dev/null @@ -1,63 +0,0 @@ -"use strict"; - -import {WriteCommand, IWriteCommandArguments} from '../commands/write'; -import {Scanner} from '../scanner'; - -export function parseWriteCommandArgs(args : string) : WriteCommand { - if (!args) { - return new WriteCommand({}); - } - var scannedArgs : IWriteCommandArguments = {}; - var scanner = new Scanner(args); - while (true) { - scanner.skipWhiteSpace(); - if (scanner.isAtEof) { - break; - } - let c = scanner.next(); - switch (c) { - case '!': - if (scanner.start > 0) { - // :write !cmd - scanner.ignore(); - while (!scanner.isAtEof) { - scanner.next(); - } - // vim ignores silently if no command after :w ! - scannedArgs.cmd = scanner.emit().trim() || undefined; - continue; - } - // :write! - scannedArgs.bang = true; - scanner.ignore(); - continue; - case '+': - // :write ++opt=value - scanner.expect('+'); - scanner.ignore(); - scanner.expectOneOf(['bin', 'nobin', 'ff', 'enc']); - scannedArgs.opt = scanner.emit(); - scanner.expect('='); - scanner.ignore(); - while (!scanner.isAtEof) { - c = scanner.next(); - if (c !== ' ' && c !== '\t') { - continue; - } - scanner.backup(); - continue; - } - let value = scanner.emit(); - if (!value) { - throw new Error("Expected value for option."); - } - scannedArgs.optValue = value; - continue; - default: - throw new Error("Not implemented"); - } - } - // TODO: actually parse arguments. - // ++bin ++nobin ++ff ++enc =VALUE - return new WriteCommand(scannedArgs); -} diff --git a/src/cmd_line/subparsers/writequit.ts b/src/cmd_line/subparsers/writequit.ts deleted file mode 100644 index 424fca8c69d..00000000000 --- a/src/cmd_line/subparsers/writequit.ts +++ /dev/null @@ -1,53 +0,0 @@ -"use strict"; - -import {WriteQuitCommand, IWriteQuitCommandArguments} from '../commands/writequit'; -import {Scanner} from '../scanner'; - -export function parseWriteQuitCommandArgs(args : string) : WriteQuitCommand { - if (!args) { - return new WriteQuitCommand({}); - } - var scannedArgs : IWriteQuitCommandArguments = {}; - var scanner = new Scanner(args); - while (true) { - scanner.skipWhiteSpace(); - if (scanner.isAtEof) { - break; - } - let c = scanner.next(); - switch (c) { - case '!': - // :writequit! - scannedArgs.bang = true; - scanner.ignore(); - continue; - case '+': - // :writequit ++opt=value - scanner.expect('+'); - scanner.ignore(); - scanner.expectOneOf(['bin', 'nobin', 'ff', 'enc']); - scannedArgs.opt = scanner.emit(); - scanner.expect('='); - scanner.ignore(); - while (!scanner.isAtEof) { - c = scanner.next(); - if (c !== ' ' && c !== '\t') { - continue; - } - scanner.backup(); - continue; - } - let value = scanner.emit(); - if (!value) { - throw new Error("Expected value for option."); - } - scannedArgs.optValue = value; - continue; - default: - throw new Error("Not implemented"); - } - } - // TODO: parse the stuff (it's really not). - // ++bin ++nobin ++ff ++enc =VALUE - return new WriteQuitCommand(scannedArgs); -} diff --git a/src/cmd_line/subparsers/writequitall.ts b/src/cmd_line/subparsers/writequitall.ts deleted file mode 100644 index 1a1dfffa713..00000000000 --- a/src/cmd_line/subparsers/writequitall.ts +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; - -import * as node from "../commands/writequitall"; -import { Scanner} from '../scanner'; -import { VimError, ErrorCode } from '../../error'; - -export function parseWriteQuitAllCommandArgs(args : string) : node.WriteQuitAllCommand { - if (!args) { - return new node.WriteQuitAllCommand({}); - } - var scannedArgs : node.IWriteQuitAllCommandArguments = {}; - var scanner = new Scanner(args); - const c = scanner.next(); - if (c === '!') { - scannedArgs.bang = true; - scanner.ignore(); - } else if (c !== ' ') { - throw VimError.fromCode(ErrorCode.E488); - } - scanner.skipWhiteSpace(); - if (!scanner.isAtEof) { - throw VimError.fromCode(ErrorCode.E488); - } - return new node.WriteQuitAllCommand(scannedArgs); -} diff --git a/src/cmd_line/token.ts b/src/cmd_line/token.ts deleted file mode 100644 index 19f7b171ea4..00000000000 --- a/src/cmd_line/token.ts +++ /dev/null @@ -1,37 +0,0 @@ -"use strict"; - -// Tokens for the Vim command line. - -export enum TokenType { - Unknown, - Eof, - LineNumber, - Dot, - Dollar, - Percent, - Comma, - Plus, - Minus, - CommandName, - CommandArgs, - ForwardSearch, - ReverseSearch, - Offset, - /** - * Marks - * - */ - SelectionFirstLine, - SelectionLastLine, - Mark -} - -export class Token { - type : TokenType; - content : string; - - constructor(type : TokenType, content : string) { - this.type = type; - this.content = content; - } -} diff --git a/src/common/matching/matcher.ts b/src/common/matching/matcher.ts deleted file mode 100644 index 88f84231f6a..00000000000 --- a/src/common/matching/matcher.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { Position, PositionDiff } from './../motion/position'; -import { TextEditor } from "./../../textEditor"; -import * as vscode from 'vscode'; - -function escapeRegExpCharacters(value: string): string { - return value.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); -} - -let toReversedString = (function () { - - function reverse(str: string): string { - let reversedStr = ''; - for (let i = str.length - 1; i >= 0; i--) { - reversedStr += str.charAt(i); - } - return reversedStr; - } - - let lastInput: string = ''; - let lastOutput: string = ''; - return function toReversedString(str: string): string { - if (lastInput !== str) { - lastInput = str; - lastOutput = reverse(lastInput); - } - return lastOutput; - }; -})(); - -/** - * PairMatcher finds the position matching the given character, respecting nested - * instances of the pair. - */ -export class PairMatcher { - static pairings: { - [key: string]: - { match: string, nextMatchIsForward: boolean, directionLess?: boolean, matchesWithPercentageMotion?: boolean } - } = { - "(" : { match: ")", nextMatchIsForward: true, matchesWithPercentageMotion: true }, - "{" : { match: "}", nextMatchIsForward: true, matchesWithPercentageMotion: true }, - "[" : { match: "]", nextMatchIsForward: true, matchesWithPercentageMotion: true }, - ")" : { match: "(", nextMatchIsForward: false, matchesWithPercentageMotion: true }, - "}" : { match: "{", nextMatchIsForward: false, matchesWithPercentageMotion: true }, - "]" : { match: "[", nextMatchIsForward: false, matchesWithPercentageMotion: true }, - // These characters can't be used for "%"-based matching, but are still - // useful for text objects. - "<" : { match: ">", nextMatchIsForward: true }, - ">" : { match: "<", nextMatchIsForward: false }, - // These are useful for deleting closing and opening quotes, but don't seem to negatively - // affect how text objects such as `ci"` work, which was my worry. - '"': { match: '"', nextMatchIsForward: false, directionLess: true }, - "'": { match: "'", nextMatchIsForward: false, directionLess: true }, - "`": { match: "`", nextMatchIsForward: false, directionLess: true }, - }; - - static nextPairedChar(position: Position, charToMatch: string, closed: boolean = true): Position | undefined { - /** - * We do a fairly basic implementation that only tracks the state of the type of - * character you're over and its pair (e.g. "[" and "]"). This is similar to - * what Vim does. - * - * It can't handle strings very well - something like "|( ')' )" where | is the - * cursor will cause it to go to the ) in the quotes, even though it should skip over it. - * - * PRs welcomed! (TODO) - * Though ideally VSC implements https://github.com/Microsoft/vscode/issues/7177 - */ - const toFind = this.pairings[charToMatch]; - - if (toFind === undefined || toFind.directionLess) { - return undefined; - } - - let regex = new RegExp('(' + escapeRegExpCharacters(charToMatch) + '|' + escapeRegExpCharacters(toFind.match) + ')', 'i'); - - let stackHeight = closed ? 0 : 1; - let matchedPosition: Position | undefined = undefined; - - // find matched bracket up - if (!toFind.nextMatchIsForward) { - for (let lineNumber = position.line; lineNumber >= 0; lineNumber--) { - let lineText = TextEditor.getLineAt(new Position(lineNumber, 0)).text; - let startOffset = lineNumber === position.line ? lineText.length - position.character - 1 : 0; - - while (true) { - let queryText = toReversedString(lineText).substr(startOffset); - if (queryText === '') { - break; - } - - let m = queryText.match(regex); - - if (!m) { - break; - } - - let matchedChar = m[0]; - if (matchedChar === charToMatch) { - stackHeight++; - } - - if (matchedChar === toFind.match) { - stackHeight--; - } - - if (stackHeight === 0) { - matchedPosition = new Position(lineNumber, lineText.length - startOffset - m.index! - 1); - return matchedPosition; - } - - startOffset = startOffset + m.index! + 1; - } - } - } else { - for (let lineNumber = position.line, lineCount = TextEditor.getLineCount(); lineNumber < lineCount; lineNumber++) { - let lineText = TextEditor.getLineAt(new Position(lineNumber, 0)).text; - let startOffset = lineNumber === position.line ? position.character : 0; - - while (true) { - let queryText = lineText.substr(startOffset); - if (queryText === '') { - break; - } - - let m = queryText.match(regex); - - if (!m) { - break; - } - - let matchedChar = m[0]; - if (matchedChar === charToMatch) { - stackHeight++; - } - - if (matchedChar === toFind.match) { - stackHeight--; - } - - if (stackHeight === 0) { - matchedPosition = new Position(lineNumber, startOffset + m.index!); - return matchedPosition; - } - - startOffset = startOffset + m.index! + 1; - } - } - } - - if (matchedPosition) { - return matchedPosition; - } - - // TODO(bell) - return undefined; - } - - /** - * Given a current position, find an immediate following bracket and return the range. If - * no matching bracket is found immediately following the opening bracket, return undefined. - */ - static immediateMatchingBracket(currentPosition: Position): vscode.Range | undefined { - // Don't delete bracket unless autoClosingBrackets is set - if (!vscode.workspace.getConfiguration().get("editor.autoClosingBrackets")) { return undefined; } - - const deleteRange = new vscode.Range(currentPosition, currentPosition.getLeftThroughLineBreaks()); - const deleteText = vscode.window.activeTextEditor!.document.getText(deleteRange); - let matchRange: vscode.Range | undefined; - let isNextMatch = false; - - if ("{[(\"'`".indexOf(deleteText) > -1) { - const matchPosition = currentPosition.add(new PositionDiff(0, 1)); - matchRange = new vscode.Range(matchPosition, matchPosition.getLeftThroughLineBreaks()); - isNextMatch = vscode.window.activeTextEditor!.document.getText(matchRange) === PairMatcher.pairings[deleteText].match; - } - - if (isNextMatch && matchRange) { - return matchRange; - } - - return undefined; - } -} \ No newline at end of file diff --git a/src/common/matching/quoteMatcher.ts b/src/common/matching/quoteMatcher.ts deleted file mode 100644 index c89ce6960f3..00000000000 --- a/src/common/matching/quoteMatcher.ts +++ /dev/null @@ -1,53 +0,0 @@ -enum QuoteMatch { - None, - Opening, - Closing -} - -/** - * QuoteMatcher matches quoted strings, respecting escaped quotes (\") and friends - */ -export class QuoteMatcher { - static escapeChar = "\\"; - - private quoteMap: QuoteMatch[] = []; - - constructor(char: string, corpus: string) { - let openingQuote = false; - // Loop over corpus, marking quotes and respecting escape characters. - for (let i = 0; i < corpus.length; i++) { - if (corpus[i] === QuoteMatcher.escapeChar) { - i += 1; - continue; - } - if (corpus[i] === char) { - openingQuote = !openingQuote; - this.quoteMap[i] = openingQuote ? QuoteMatch.Opening : QuoteMatch.Closing; - } - } - } - - findOpening(start: number): number { - // First, search backwards to see if we could be inside a quote - for (let i = start; i >= 0; i--) { - if (this.quoteMap[i] === QuoteMatch.Opening) { - return i; - } - } - - // Didn't find one behind us, the string may start ahead of us. This happens - // to be the same logic we use to search forwards. - return this.findClosing(start); - } - - findClosing(start: number): number { - // Search forwards from start, looking for a non-escaped char - for (let i = start; i <= this.quoteMap.length; i++) { - if (this.quoteMap[i]) { - return i; - } - } - - return -1; - } -} \ No newline at end of file diff --git a/src/common/matching/tagMatcher.ts b/src/common/matching/tagMatcher.ts deleted file mode 100644 index 76f0ab43288..00000000000 --- a/src/common/matching/tagMatcher.ts +++ /dev/null @@ -1,100 +0,0 @@ -type Tag = { name: string; type: "close" | "open"; startPos: number; endPos: number }; -type MatchedTag = {tag: string, openingTagStart: number, openingTagEnd: number, closingTagStart: number, closingTagEnd: number}; - -export class TagMatcher { - static TAG_REGEX = /\<(\/)?([^\>\<\s]+)[^\>\<]*?(\/?)\>/g; - static OPEN_FORWARD_SLASH = 1; - static TAG_NAME = 2; - static CLOSE_FORWARD_SLASH = 3; - - openStart: number|undefined; - openEnd: number|undefined; - closeStart: number|undefined; - closeEnd: number|undefined; - - constructor(corpus: string, position: number) { - let match = TagMatcher.TAG_REGEX.exec(corpus); - const tags : Tag[] = []; - - // Gather all the existing tags. - while (match) { - // Node is a self closing tag, skip. - if (match[TagMatcher.CLOSE_FORWARD_SLASH]) { - match = TagMatcher.TAG_REGEX.exec(corpus); - continue; - } - - tags.push({ - name: match[TagMatcher.TAG_NAME], - type: match[TagMatcher.OPEN_FORWARD_SLASH] ? 'close' : 'open', - startPos: match.index, - endPos: TagMatcher.TAG_REGEX.lastIndex, - }); - - match = TagMatcher.TAG_REGEX.exec(corpus); - } - - const stack : Tag[] = []; - const matchedTags : MatchedTag[] = []; - - for (let tag of tags) { - // We have to push on the stack - // if it is an open tag. - if (tag.type === 'open') { - stack.push(tag); - } else { - // We have an unmatched closing tag, - // so try and match it with any existing tag. - for (let i = stack.length - 1; i >= 0; i--) { - const openNode = stack[i]; - - if (openNode.type === 'open' - && openNode.name === tag.name) { - // A matching tag was found, ignore - // any tags that were in between. - matchedTags.push({ - tag: openNode.name, - openingTagStart: openNode.startPos, - openingTagEnd: openNode.endPos, - closingTagStart: tag.startPos, - closingTagEnd: tag.endPos, - }); - - stack.splice(i); - break; - } - } - } - } - - const tagsSurrounding = matchedTags.filter(n => { - return position >= n.openingTagStart && position <= n.closingTagEnd; - }); - - if (!tagsSurrounding.length) { - return; - } - - // The first one should be the most relevant tag. - const nodeSurrounding = tagsSurrounding[0]; - - this.openStart = nodeSurrounding.openingTagStart; - this.openEnd = nodeSurrounding.openingTagEnd; - this.closeStart = nodeSurrounding.closingTagStart; - this.closeEnd = nodeSurrounding.closingTagEnd; - } - - findOpening(inclusive: boolean): number|undefined { - if (inclusive) { - return this.openStart; - } - return this.openEnd; - } - - findClosing(inclusive: boolean): number|undefined { - if (inclusive) { - return this.closeEnd; - } - return this.closeStart; - } -} \ No newline at end of file diff --git a/src/common/motion/position.ts b/src/common/motion/position.ts index 690e42a6e30..36e82ad08cf 100644 --- a/src/common/motion/position.ts +++ b/src/common/motion/position.ts @@ -1,12 +1,9 @@ -"use strict"; +'use strict'; -import * as _ from "lodash"; -import * as vscode from "vscode"; -import { TextEditor } from "./../../textEditor"; -import { VimState } from './../../mode/modeHandler'; -import { VisualBlockMode } from './../../mode/modeVisualBlock'; -import { Configuration } from "./../../configuration/configuration"; -import { betterEscapeRegex } from "./../../util"; +import * as _ from 'lodash'; +import * as vscode from 'vscode'; +import { TextEditor } from './../../textEditor'; +import { betterEscapeRegex } from './../../util'; /** * Represents a difference between two positions. Add it to a position @@ -44,28 +41,26 @@ export class PositionDiff { throw new Error("johnfn hasn't done this case yet and doesnt want to"); } - return new PositionDiff( - this._line + other._line, - this._character + other._character - ); + return new PositionDiff(this._line + other._line, this._character + other._character); } /** * Adds a Position to this PositionDiff, returning a new PositionDiff. */ - public addPosition(other: Position, { boundsCheck = true } = { } ): Position { + public addPosition(other: Position, { boundsCheck = true } = {}): Position { let resultChar = this.isBOLDiff() ? 0 : this.character + other.character; let resultLine = this.line + other.line; if (boundsCheck) { - if (resultChar < 0) { resultChar = 0; } - if (resultLine < 0) { resultLine = 0; } + if (resultChar < 0) { + resultChar = 0; + } + if (resultLine < 0) { + resultLine = 0; + } } - return new Position( - resultLine, - resultChar - ); + return new Position(resultLine, resultChar); } /** @@ -94,17 +89,17 @@ export class PositionDiff { return `[ Diff: BOL ]`; } - return `[ Diff: ${ this._line } ${ this._character } ]`; + return `[ Diff: ${this._line} ${this._character} ]`; } } export class Position extends vscode.Position { - private static NonWordCharacters = Configuration.iskeyword!; - private static NonBigWordCharacters = ""; - private static NonFileCharacters = "\"'`;<>{}[]()"; + private static NonWordCharacters = '/\\()"\':,.;<>~!@#$%^&*|+=[]{}`?-'; + private static NonBigWordCharacters = ''; + private static NonFileCharacters = '"\'`;<>{}[]()'; - private _nonWordCharRegex : RegExp; - private _nonBigWordCharRegex : RegExp; + private _nonWordCharRegex: RegExp; + private _nonBigWordCharRegex: RegExp; private _sentenceEndRegex: RegExp; private _nonFileNameRegex: RegExp; @@ -117,8 +112,21 @@ export class Position extends vscode.Position { this._nonFileNameRegex = this.makeWordRegex(Position.NonFileCharacters); } + public getRightThroughLineBreaks() { + if (this.isAtDocumentEnd()) { + // TODO(bell) + return this; + } + + if (this.isLineEnd()) { + return this.getDown(0); + } + + return this.getRight(); + } + public toString(): string { - return `[${ this.line }, ${ this.character }]`; + return `[${this.line}, ${this.character}]`; } public static FromVSCodePosition(pos: vscode.Position): Position { @@ -129,15 +137,23 @@ export class Position extends vscode.Position { * Returns which of the 2 provided Positions comes earlier in the document. */ public static EarlierOf(p1: Position, p2: Position): Position { - if (p1.line < p2.line) { return p1; } - if (p1.line === p2.line && p1.character < p2.character) { return p1; } + if (p1.line < p2.line) { + return p1; + } + if (p1.line === p2.line && p1.character < p2.character) { + return p1; + } return p2; } public isEarlierThan(other: Position): boolean { - if (this.line < other.line) { return true; } - if (this.line === other.line && this.character < other.character) { return true; } + if (this.line < other.line) { + return true; + } + if (this.line === other.line && this.character < other.character) { + return true; + } return false; } @@ -146,7 +162,10 @@ export class Position extends vscode.Position { * Iterates over every position in the document starting at start, returning * at every position the current line text, character text, and a position object. */ - public static *IterateDocument(start: Position, forward = true): Iterable<{ line: string, char: string, pos: Position }> { + public static *IterateDocument( + start: Position, + forward = true + ): Iterable<{ line: string; char: string; pos: Position }> { let lineIndex: number, charIndex: number; if (forward) { @@ -158,7 +177,7 @@ export class Position extends vscode.Position { yield { line: line, char: line[charIndex], - pos: new Position(lineIndex, charIndex) + pos: new Position(lineIndex, charIndex), }; } } @@ -171,7 +190,7 @@ export class Position extends vscode.Position { yield { line: line, char: line[charIndex], - pos: new Position(lineIndex, charIndex) + pos: new Position(lineIndex, charIndex), }; } } @@ -181,7 +200,10 @@ export class Position extends vscode.Position { /** * Iterate over every position in the block defined by the two positions passed in. */ - public static *IterateBlock(topLeft: Position, bottomRight: Position): Iterable<{ line: string, char: string, pos: Position }> { + public static *IterateBlock( + topLeft: Position, + bottomRight: Position + ): Iterable<{ line: string; char: string; pos: Position }> { for (let lineIndex = topLeft.line; lineIndex <= bottomRight.line; lineIndex++) { const line = TextEditor.getLineAt(new Position(lineIndex, 0)).text; @@ -189,7 +211,7 @@ export class Position extends vscode.Position { yield { line: line, char: line[charIndex], - pos : new Position(lineIndex, charIndex) + pos: new Position(lineIndex, charIndex), }; } } @@ -198,7 +220,10 @@ export class Position extends vscode.Position { /** * Iterate over every position in the selection defined by the two positions passed in. */ - public static *IterateSelection(topLeft: Position, bottomRight: Position): Iterable<{ line: string, char: string, pos: Position }> { + public static *IterateSelection( + topLeft: Position, + bottomRight: Position + ): Iterable<{ line: string; char: string; pos: Position }> { for (let lineIndex = topLeft.line; lineIndex <= bottomRight.line; lineIndex++) { const line = TextEditor.getLineAt(new Position(lineIndex, 0)).text; @@ -207,7 +232,7 @@ export class Position extends vscode.Position { yield { line: line, char: line[charIndex], - pos: new Position(lineIndex, charIndex) + pos: new Position(lineIndex, charIndex), }; } } else if (lineIndex === bottomRight.line) { @@ -215,7 +240,7 @@ export class Position extends vscode.Position { yield { line: line, char: line[charIndex], - pos: new Position(lineIndex, charIndex) + pos: new Position(lineIndex, charIndex), }; } } else { @@ -223,49 +248,17 @@ export class Position extends vscode.Position { yield { line: line, char: line[charIndex], - pos: new Position(lineIndex, charIndex) + pos: new Position(lineIndex, charIndex), }; } } } } - /** - * Iterate over every line in the block defined by the two positions passed in. - * - * This is intended for visual block mode. - */ - public static *IterateLine(vimState: VimState, options: { reverse?: boolean } = { reverse: false }) - : Iterable<{ line: string, start: Position, end: Position }> { - - const { reverse } = options; - const start = vimState.cursorStartPosition; - const stop = vimState.cursorPosition; - - const topLeft = VisualBlockMode.getTopLeftPosition(start, stop); - const bottomRight = VisualBlockMode.getBottomRightPosition(start, stop); - - // Special case for $, which potentially makes the block ragged - // on the right side. - const runToLineEnd = vimState.desiredColumn === Number.POSITIVE_INFINITY; - - const itrStart = reverse ? bottomRight.line : topLeft.line; - const itrEnd = reverse ? topLeft.line : bottomRight.line; - - for (let lineIndex = itrStart; reverse ? lineIndex >= itrEnd : lineIndex <= itrEnd; reverse ? lineIndex-- : lineIndex++) { - const line = TextEditor.getLineAt(new Position(lineIndex, 0)).text; - const endCharacter = runToLineEnd ? line.length + 1 : Math.min(line.length, bottomRight.character + 1); - - yield { - line : line.substring(topLeft.character, endCharacter), - start : new Position(lineIndex, topLeft.character), - end : new Position(lineIndex, bottomRight.character + 1) - }; - } - } - // Iterates through words on the same line, starting from the current position. - public static *IterateWords(start: Position): Iterable<{ start: Position, end: Position, word: string }> { + public static *IterateWords( + start: Position + ): Iterable<{ start: Position; end: Position; word: string }> { const text = TextEditor.getLineAt(start).text; let wordEnd = start.getCurrentWordEnd(true); do { @@ -288,7 +281,9 @@ export class Position extends vscode.Position { * Returns which of the 2 provided Positions comes later in the document. */ public static LaterOf(p1: Position, p2: Position): Position { - if (Position.EarlierOf(p1, p2) === p1) { return p2; } + if (Position.EarlierOf(p1, p2) === p1) { + return p2; + } return p1; } @@ -298,17 +293,14 @@ export class Position extends vscode.Position { * difference between the two. */ public subtract(other: Position): PositionDiff { - return new PositionDiff( - this.line - other.line, - this.character - other.character, - ); + return new PositionDiff(this.line - other.line, this.character - other.character); } /** * Adds a PositionDiff to this position, returning a new * position. */ - public add(diff: PositionDiff, { boundsCheck = true } = { } ): Position { + public add(diff: PositionDiff, { boundsCheck = true } = {}): Position { let resultChar = this.character + diff.character; let resultLine = this.line + diff.line; @@ -317,37 +309,25 @@ export class Position extends vscode.Position { } if (boundsCheck) { - if (resultChar < 0) { resultChar = 0; } - if (resultLine < 0) { resultLine = 0; } - if (resultLine >= TextEditor.getLineCount() - 1) { resultLine = TextEditor.getLineCount() - 1; } + if (resultChar < 0) { + resultChar = 0; + } + if (resultLine < 0) { + resultLine = 0; + } + if (resultLine >= TextEditor.getLineCount() - 1) { + resultLine = TextEditor.getLineCount() - 1; + } } - return new Position( - resultLine, - resultChar - ); + return new Position(resultLine, resultChar); } - public setLocation(line: number, character: number) : Position { + public setLocation(line: number, character: number): Position { let position = new Position(line, character); return position; } - public getLeftTabStop(): Position { - if (!this.isLineBeginning()) { - let indentationWidth = TextEditor.getIndentationLevel(TextEditor.getLineAt(this).text); - let tabSize = vscode.window.activeTextEditor!.options.tabSize as number; - - if (indentationWidth % tabSize > 0) { - return new Position(this.line, Math.max(0, this.character - indentationWidth % tabSize)); - } else { - return new Position(this.line, Math.max(0, this.character - tabSize)); - } - } - - return this; - } - /** * Gets the position one to the left of this position. Does not go up line * breaks. @@ -379,23 +359,12 @@ export class Position extends vscode.Position { if (includeEol) { return this.getUp(0).getLineEnd(); } else { - return this.getUp(0).getLineEnd().getLeft(); + return this.getUp(0) + .getLineEnd() + .getLeft(); } } - public getRightThroughLineBreaks(): Position { - if (this.isAtDocumentEnd()) { - // TODO(bell) - return this; - } - - if (this.getRight().isLineEnd()) { - return this.getDown(0); - } - - return this.getRight(); - } - public getRight(count: number = 1): Position { if (!this.isLineEnd()) { return new Position(this.line, this.character + count); @@ -407,7 +376,7 @@ export class Position extends vscode.Position { /** * Get the position of the line directly below the current line. */ - public getDown(desiredColumn: number) : Position { + public getDown(desiredColumn: number): Position { if (this.getDocumentEnd().line !== this.line) { let nextLine = this.line + 1; let nextLineLength = Position.getLineLength(nextLine); @@ -421,10 +390,10 @@ export class Position extends vscode.Position { /** * Get the position of the line directly above the current line. */ - public getUp(desiredColumn: number) : Position { + public getUp(desiredColumn: number): Position { if (this.getDocumentBegin().line !== this.line) { let prevLine = this.line - 1; - let prevLineLength = Position.getLineLength(prevLine); + let prevLineLength = Position.getLineLength(prevLine); return new Position(prevLine, Math.min(prevLineLength, desiredColumn)); } @@ -437,14 +406,11 @@ export class Position extends vscode.Position { * than the end of the document. */ public getDownByCount(count = 0, { boundsCheck = true } = {}): Position { - const line = boundsCheck ? - Math.min(TextEditor.getLineCount() - 1, this.line + count) : - this.line + count; + const line = boundsCheck + ? Math.min(TextEditor.getLineCount() - 1, this.line + count) + : this.line + count; - return new Position( - line, - this.character - ); + return new Position(line, this.character); } /** @@ -452,10 +418,7 @@ export class Position extends vscode.Position { * than the end of the document. */ public getUpByCount(count = 0): Position { - return new Position( - Math.max(0, this.line - count), - this.character - ); + return new Position(Math.max(0, this.line - count), this.character); } /** @@ -471,7 +434,10 @@ export class Position extends vscode.Position { * than the end of the line */ public getRightByCount(count = 0): Position { - return new Position(this.line, Math.min(TextEditor.getLineAt(this).text.length - 1, this.character + count)); + return new Position( + this.line, + Math.min(TextEditor.getLineAt(this).text.length - 1, this.character + count) + ); } /** @@ -492,11 +458,11 @@ export class Position extends vscode.Position { /** * Inclusive is true if we consider the current position a valid result, false otherwise. */ - public getWordRight(inclusive: boolean = false) : Position { + public getWordRight(inclusive: boolean = false): Position { return this.getWordRightWithRegex(this._nonWordCharRegex, inclusive); } - public getBigWordRight(inclusive: boolean = false) : Position { + public getBigWordRight(inclusive: boolean = false): Position { return this.getWordRightWithRegex(this._nonBigWordCharRegex); } @@ -529,11 +495,13 @@ export class Position extends vscode.Position { /** * Get the boundary position of the section. */ - public getSectionBoundary(args: { forward: boolean, boundary: string }): Position { + public getSectionBoundary(args: { forward: boolean; boundary: string }): Position { let pos: Position = this; - if ((args.forward && pos.line === TextEditor.getLineCount() - 1) || - (!args.forward && pos.line === 0)) { + if ( + (args.forward && pos.line === TextEditor.getLineCount() - 1) || + (!args.forward && pos.line === 0) + ) { return pos.getFirstLineNonBlankChar(); } @@ -565,13 +533,13 @@ export class Position extends vscode.Position { let pos: Position = this; // If we're not in a paragraph yet, go down until we are. - while (TextEditor.getLineAt(pos).text === "" && !TextEditor.isLastLine(pos)) { - pos = pos.getDown(0); + while (TextEditor.getLineAt(pos).text === '' && !TextEditor.isLastLine(pos)) { + pos = pos.getDown(0); } // Go until we're outside of the paragraph, or at the end of the document. - while (TextEditor.getLineAt(pos).text !== "" && pos.line < TextEditor.getLineCount() - 1) { - pos = pos.getDown(0); + while (TextEditor.getLineAt(pos).text !== '' && pos.line < TextEditor.getLineCount() - 1) { + pos = pos.getDown(0); } return pos.getLineEnd(); @@ -584,19 +552,19 @@ export class Position extends vscode.Position { let pos: Position = this; // If we're not in a paragraph yet, go up until we are. - while (TextEditor.getLineAt(pos).text === "" && !TextEditor.isFirstLine(pos)) { + while (TextEditor.getLineAt(pos).text === '' && !TextEditor.isFirstLine(pos)) { pos = pos.getUp(0); } // Go until we're outside of the paragraph, or at the beginning of the document. - while (pos.line > 0 && TextEditor.getLineAt(pos).text !== "") { + while (pos.line > 0 && TextEditor.getLineAt(pos).text !== '') { pos = pos.getUp(0); } return pos.getLineBegin(); } - public getSentenceBegin(args: {forward: boolean}): Position { + public getSentenceBegin(args: { forward: boolean }): Position { if (args.forward) { return this.getNextSentenceBeginWithRegex(this._sentenceEndRegex, false); } else { @@ -615,18 +583,6 @@ export class Position extends vscode.Position { return new Position(this.line, 0); } - /** - * Get the beginning of the line, excluding preceeding whitespace. - * This respects the `autoindent` setting, and returns `getLineBegin()` if auto-indent - * is disabled. - */ - public getLineBeginRespectingIndent(): Position { - if (!Configuration.autoindent) { - return this.getLineBegin(); - } - return this.getFirstLineNonBlankChar(); - } - /** * Get the beginning of the next line. */ @@ -689,13 +645,13 @@ export class Position extends vscode.Position { return new Position( this.line + numberOfLinesSpanned, - numberOfLinesSpanned === 0 ? - this.character + text.length : - text.length - (text.lastIndexOf('\n') + 1) + numberOfLinesSpanned === 0 + ? this.character + text.length + : text.length - (text.lastIndexOf('\n') + 1) ); } - public getDocumentEnd() : Position { + public getDocumentEnd(): Position { let lineCount = TextEditor.getLineCount(); let line = lineCount > 0 ? lineCount - 1 : 0; let char = Position.getLineLength(line); @@ -768,18 +724,18 @@ export class Position extends vscode.Position { return new Position(this.line, Position.getFirstNonBlankCharAtLine(this.line)); } - public static getLineLength(line: number) : number { + public static getLineLength(line: number): number { return TextEditor.readLineAt(line).length; } - private makeWordRegex(characterSet: string) : RegExp { + private makeWordRegex(characterSet: string): RegExp { let escaped = characterSet && betterEscapeRegex(characterSet); let segments: string[] = []; segments.push(`([^\\s${escaped}]+)`); segments.push(`[${escaped}]+`); segments.push(`$^`); - let result = new RegExp(segments.join("|"), "g"); + let result = new RegExp(segments.join('|'), 'g'); return result; } @@ -791,9 +747,11 @@ export class Position extends vscode.Position { while (result) { positions.push(result.index); - // Handles the case where an empty string match causes lastIndex not to advance, - // which gets us in an infinite loop. - if (result.index === regex.lastIndex) { regex.lastIndex++; } + // Handles the case where an empty string match causes lastIndex not to advance, + // which gets us in an infinite loop. + if (result.index === regex.lastIndex) { + regex.lastIndex++; + } result = regex.exec(line); } @@ -809,9 +767,11 @@ export class Position extends vscode.Position { positions.push(result.index + result[0].length - 1); } - // Handles the case where an empty string match causes lastIndex not to advance, - // which gets us in an infinite loop. - if (result.index === regex.lastIndex) { regex.lastIndex++; } + // Handles the case where an empty string match causes lastIndex not to advance, + // which gets us in an infinite loop. + if (result.index === regex.lastIndex) { + regex.lastIndex++; + } result = regex.exec(line); } @@ -823,10 +783,17 @@ export class Position extends vscode.Position { */ private getWordLeftWithRegex(regex: RegExp, inclusive: boolean = false): Position { for (let currentLine = this.line; currentLine >= 0; currentLine--) { - let positions = this.getAllPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); - let newCharacter = _.find(positions.reverse(), - index => ((index < this.character && !inclusive) || - (index <= this.character && inclusive)) || currentLine !== this.line); + let positions = this.getAllPositions( + TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, + regex + ); + let newCharacter = _.find( + positions.reverse(), + index => + (index < this.character && !inclusive) || + (index <= this.character && inclusive) || + currentLine !== this.line + ); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter); @@ -841,10 +808,17 @@ export class Position extends vscode.Position { */ private getWordRightWithRegex(regex: RegExp, inclusive: boolean = false): Position { for (let currentLine = this.line; currentLine < TextEditor.getLineCount(); currentLine++) { - let positions = this.getAllPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); - let newCharacter = _.find(positions, - index => ((index > this.character && !inclusive) || - (index >= this.character && inclusive)) || currentLine !== this.line); + let positions = this.getAllPositions( + TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, + regex + ); + let newCharacter = _.find( + positions, + index => + (index > this.character && !inclusive) || + (index >= this.character && inclusive) || + currentLine !== this.line + ); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter); @@ -854,9 +828,12 @@ export class Position extends vscode.Position { return new Position(TextEditor.getLineCount() - 1, 0).getLineEnd(); } - private getLastWordEndWithRegex(regex: RegExp) : Position { + private getLastWordEndWithRegex(regex: RegExp): Position { for (let currentLine = this.line; currentLine < TextEditor.getLineCount(); currentLine++) { - let positions = this.getAllEndPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); + let positions = this.getAllEndPositions( + TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, + regex + ); let index = _.findIndex(positions, i => i >= this.character || currentLine !== this.line); let newCharacter = 0; if (index === -1) { @@ -878,10 +855,17 @@ export class Position extends vscode.Position { */ private getCurrentWordEndWithRegex(regex: RegExp, inclusive: boolean): Position { for (let currentLine = this.line; currentLine < TextEditor.getLineCount(); currentLine++) { - let positions = this.getAllEndPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); - let newCharacter = _.find(positions, - index => ((index > this.character && !inclusive) || - (index >= this.character && inclusive)) || currentLine !== this.line); + let positions = this.getAllEndPositions( + TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, + regex + ); + let newCharacter = _.find( + positions, + index => + (index > this.character && !inclusive) || + (index >= this.character && inclusive) || + currentLine !== this.line + ); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter); @@ -894,19 +878,26 @@ export class Position extends vscode.Position { private getPreviousSentenceBeginWithRegex(regex: RegExp, inclusive: boolean): Position { let paragraphBegin = this.getCurrentParagraphBeginning(); for (let currentLine = this.line; currentLine >= paragraphBegin.line; currentLine--) { - let endPositions = this.getAllEndPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); - let newCharacter = _.find(endPositions.reverse(), - index => ((index < this.character && !inclusive - && new Position(currentLine, index).getRightThroughLineBreaks().compareTo(this)) - || (index <= this.character && inclusive) - ) || currentLine !== this.line); + let endPositions = this.getAllEndPositions( + TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, + regex + ); + let newCharacter = _.find( + endPositions.reverse(), + index => + (index < this.character && + !inclusive && + new Position(currentLine, index).getRightThroughLineBreaks().compareTo(this)) || + (index <= this.character && inclusive) || + currentLine !== this.line + ); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter).getRightThroughLineBreaks(); } } - if ((paragraphBegin.line + 1 === this.line || paragraphBegin.line === this.line)) { + if (paragraphBegin.line + 1 === this.line || paragraphBegin.line === this.line) { return paragraphBegin; } else { return new Position(paragraphBegin.line + 1, 0); @@ -917,10 +908,17 @@ export class Position extends vscode.Position { // A paragraph and section boundary is also a sentence boundary. let paragraphEnd = this.getCurrentParagraphEnd(); for (let currentLine = this.line; currentLine <= paragraphEnd.line; currentLine++) { - let endPositions = this.getAllEndPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); - let newCharacter = _.find(endPositions, - index => ((index > this.character && !inclusive) || - (index >= this.character && inclusive)) || currentLine !== this.line); + let endPositions = this.getAllEndPositions( + TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, + regex + ); + let newCharacter = _.find( + endPositions, + index => + (index > this.character && !inclusive) || + (index >= this.character && inclusive) || + currentLine !== this.line + ); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter).getRightThroughLineBreaks(); @@ -933,10 +931,17 @@ export class Position extends vscode.Position { private getCurrentSentenceEndWithRegex(regex: RegExp, inclusive: boolean): Position { let paragraphEnd = this.getCurrentParagraphEnd(); for (let currentLine = this.line; currentLine <= paragraphEnd.line; currentLine++) { - let allPositions = this.getAllPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); - let newCharacter = _.find(allPositions, - index => ((index > this.character && !inclusive) || - (index >= this.character && inclusive)) || currentLine !== this.line); + let allPositions = this.getAllPositions( + TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, + regex + ); + let newCharacter = _.find( + allPositions, + index => + (index > this.character && !inclusive) || + (index >= this.character && inclusive) || + currentLine !== this.line + ); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter); @@ -953,10 +958,17 @@ export class Position extends vscode.Position { return paragraphEnd; } else { for (let currentLine = this.line; currentLine <= paragraphEnd.line; currentLine++) { - let nonWhitePositions = this.getAllPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, /\S/g); - let newCharacter = _.find(nonWhitePositions, - index => ((index > this.character && !inclusive) || - (index >= this.character && inclusive)) || currentLine !== this.line); + let nonWhitePositions = this.getAllPositions( + TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, + /\S/g + ); + let newCharacter = _.find( + nonWhitePositions, + index => + (index > this.character && !inclusive) || + (index >= this.character && inclusive) || + currentLine !== this.line + ); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter); @@ -964,7 +976,7 @@ export class Position extends vscode.Position { } } - throw new Error("This should never happen..."); + throw new Error('This should never happen...'); } private findHelper(char: string, count: number, direction: number): Position | undefined { @@ -990,29 +1002,37 @@ export class Position extends vscode.Position { public tilForwards(char: string, count: number = 1): Position | null { const position = this.findHelper(char, count, +1); - if (!position) { return null; } + if (!position) { + return null; + } return new Position(this.line, position.character - 1); } public tilBackwards(char: string, count: number = 1): Position | null { const position = this.findHelper(char, count, -1); - if (!position) { return null; } + if (!position) { + return null; + } return new Position(this.line, position.character + 1); } public findForwards(char: string, count: number = 1): Position | null { const position = this.findHelper(char, count, +1); - if (!position) { return null; } + if (!position) { + return null; + } return new Position(this.line, position.character); } public findBackwards(char: string, count: number = 1): Position | null { const position = this.findHelper(char, count, -1); - if (!position) { return null; } + if (!position) { + return null; + } return position; } -} \ No newline at end of file +} diff --git a/src/common/motion/range.ts b/src/common/motion/range.ts index 87910a46755..239b6349acf 100644 --- a/src/common/motion/range.ts +++ b/src/common/motion/range.ts @@ -1,8 +1,7 @@ -"use strict"; +'use strict'; -import * as vscode from "vscode"; -import { Position, PositionDiff } from "./position"; -import { IMovement } from './../../actions/motion'; +import * as vscode from 'vscode'; +import { Position, PositionDiff } from './position'; export class Range { private _start: Position; @@ -18,20 +17,19 @@ export class Range { constructor(start: Position, stop: Position) { this._start = start; - this._stop = stop; + this._stop = stop; } /** * Create a range from a VSCode selection. */ public static FromVSCodeSelection(e: vscode.Selection): Range { - return new Range( - Position.FromVSCodePosition(e.start), - Position.FromVSCodePosition(e.end) - ); + return new Range(Position.FromVSCodePosition(e.start), Position.FromVSCodePosition(e.end)); } - public static *IterateRanges(list: Range[]): Iterable<{ start: Position; stop: Position; range: Range, i: number }> { + public static *IterateRanges( + list: Range[] + ): Iterable<{ start: Position; stop: Position; range: Range; i: number }> { for (let i = 0; i < list.length; i++) { yield { i, @@ -42,35 +40,16 @@ export class Range { } } - /** - * Create a range from an IMovement. - */ - public static FromIMovement(i: IMovement): Range { - // TODO: This shows a very clear need for refactoring after multi-cursor is merged! - - return new Range( - i.start, - i.stop - ); - } - public getRight(count = 1): Range { - return new Range( - this._start.getRight(count), - this._stop.getRight(count) - ); + return new Range(this._start.getRight(count), this._stop.getRight(count)); } public getDown(count = 1): Range { - return new Range( - this._start.getDownByCount(count), - this._stop.getDownByCount(count), - ); + return new Range(this._start.getDownByCount(count), this._stop.getDownByCount(count)); } public equals(other: Range): boolean { - return this._start.isEqual(other._start) && - this._stop.isEqual(other._stop); + return this._start.isEqual(other._start) && this._stop.isEqual(other._stop); } /** @@ -90,7 +69,7 @@ export class Range { } public toString(): string { - return `[ ${ this.start.toString() } | ${ this.stop.toString() }]`; + return `[ ${this.start.toString()} | ${this.stop.toString()}]`; } public overlaps(other: Range): boolean { @@ -98,9 +77,6 @@ export class Range { } public add(diff: PositionDiff): Range { - return new Range( - this.start.add(diff), - this.stop.add(diff) - ); + return new Range(this.start.add(diff), this.stop.add(diff)); } -} \ No newline at end of file +} diff --git a/src/common/number/numericString.ts b/src/common/number/numericString.ts deleted file mode 100644 index 7bbb0bf8cb8..00000000000 --- a/src/common/number/numericString.ts +++ /dev/null @@ -1,75 +0,0 @@ -export class NumericString { - radix: number; - value: number; - prefix: string; - suffix: string; - - private static matchings: { regex: RegExp, base: number, prefix: string }[] = [ - { regex: /^([-+])?0([0-7]+)$/g, base: 8, prefix: "0" }, - { regex: /^([-+])?(\d+)$/g, base: 10, prefix: "" }, - { regex: /^([-+])?0x([\da-fA-F]+)$/g, base: 16, prefix: "0x" }, - { regex: /\d/, base: 10, prefix: "" } - ]; - - static parse(input: string): NumericString | null { - for (const { regex, base, prefix } of NumericString.matchings) { - const match = regex.exec(input); - if (match == null) { - continue; - } - - // Regex to determine if this number has letters around it, - // if it doesn't then that is easy and no prefix or suffix is needed - let findNondigits = /[^\d-+]+/g; - - // Regex to find any leading characters before the number - let findPrefix = /^[^\d-+]+(?=[0-9]+)/g; - - // Regex to find any trailing characters after the number - let findSuffix = /[^\d]*[\d]*(.*)/g; - let newPrefix = prefix; - let newSuffix = ""; - let newNum = input; - - // Only use this section if this is a number surrounded by letters - if (findNondigits.exec(input) !== null && NumericString.matchings[NumericString.matchings.length - 1].regex === regex) { - let prefixFound = findPrefix.exec(input); - let suffixFound = findSuffix.exec(input); - - // Find the prefix if it exists - if (prefixFound !== null) { - newPrefix = prefixFound.toString(); - } - - // Find the suffix if it exists - if (suffixFound !== null) { - newSuffix = suffixFound[1].toString(); - } - - // Obtain just the number with no extra letters - newNum = newNum.slice(newPrefix.length, newNum.length - newSuffix.length); - } - - return new NumericString(parseInt(newNum, base), base, newPrefix, newSuffix); - } - return null; - } - - constructor(value: number, radix: number, prefix: string, suffix: string) { - this.value = value; - this.radix = radix; - this.prefix = prefix; - this.suffix = suffix; - } - - public toString(): string { - // Allow signed hex represented as twos complement - if (this.radix === 16) { - if (this.value < 0) { - this.value = 0xFFFFFFFF + this.value + 1; - } - } - - return this.prefix + this.value.toString(this.radix) + this.suffix; - } -} diff --git a/src/configuration/configuration.ts b/src/configuration/configuration.ts index 0eb400e5b0a..47cc37a900d 100644 --- a/src/configuration/configuration.ts +++ b/src/configuration/configuration.ts @@ -1,28 +1,15 @@ -"use strict"; +'use strict'; import * as vscode from 'vscode'; -import { taskQueue } from '../../src/taskQueue'; +import { IgnoredKeys } from '../../srcNV/screen'; import { Globals } from '../../src/globals'; export type OptionValue = number | string | boolean; export type ValueMapping = { - [key: number]: OptionValue - [key: string]: OptionValue + [key: number]: OptionValue; + [key: string]: OptionValue; }; -export interface IHandleKeys { - [key: string]: boolean; -} - -export interface IStatusBarColors { - normal: string; - insert: string; - visual: string; - visualline: string; - visualblock: string; - replace: string; -} - /** * Every Vim option we support should * 1. Be added to contribution section of `package.json`. @@ -61,7 +48,7 @@ class ConfigurationClass { /** * Load Vim options from User Settings. */ - let vimOptions = vscode.workspace.getConfiguration("vim"); + let vimOptions = vscode.workspace.getConfiguration('vim'); /* tslint:disable:forin */ // Disable forin rule here as we make accessors enumerable.` for (const option in this) { @@ -71,56 +58,21 @@ class ConfigurationClass { } } - // is special, change it to " " internally if it is used as leader - if (this.leader.toLowerCase() === "") { - this.leader = " "; - } - // Get the cursor type from vscode - const cursorStyleString = vscode.workspace.getConfiguration().get("editor.cursorStyle") as string; + const cursorStyleString = vscode.workspace + .getConfiguration() + .get('editor.cursorStyle') as string; this.userCursor = this.cursorStyleFromString(cursorStyleString); - - // Get configuration setting for handled keys, this allows user to disable - // certain key comboinations - const handleKeys = vscode.workspace.getConfiguration('vim') - .get("handleKeys", []); - - for (const bracketedKey of this.boundKeyCombinations) { - // Set context for key that is not used - // This either happens when user sets useCtrlKeys to false (ctrl keys are not used then) - // Or if user usese vim.handleKeys configuration option to set certain combinations to false - // By default, all key combinations are used so start with true - let useKey = true; - - // Check for configuration setting disabling combo - if (handleKeys[bracketedKey] !== undefined) { - if (handleKeys[bracketedKey] === false) { - useKey = false; - } - } else if (!this.useCtrlKeys && (bracketedKey.slice(1, 3) === "C-")) { - // Check for useCtrlKeys and if it is a based keybinding. - // However, we need to still capture due to overrideCopy. - if (bracketedKey === '' && this.overrideCopy) { - useKey = true; - } else { - useKey = false; - } - } - - // Set the context of whether or not this key will be used based on criteria from above - vscode.commands.executeCommand('setContext', 'vim.use' + bracketedKey, useKey); - } } private cursorStyleFromString(cursorStyle: string): vscode.TextEditorCursorStyle { - const cursorType = { - "line": vscode.TextEditorCursorStyle.Line, - "block": vscode.TextEditorCursorStyle.Block, - "underline": vscode.TextEditorCursorStyle.Underline, - "line-thin": vscode.TextEditorCursorStyle.LineThin, - "block-outline": vscode.TextEditorCursorStyle.BlockOutline, - "underline-thin": vscode.TextEditorCursorStyle.UnderlineThin, + line: vscode.TextEditorCursorStyle.Line, + block: vscode.TextEditorCursorStyle.Block, + underline: vscode.TextEditorCursorStyle.Underline, + 'line-thin': vscode.TextEditorCursorStyle.LineThin, + 'block-outline': vscode.TextEditorCursorStyle.BlockOutline, + 'underline-thin': vscode.TextEditorCursorStyle.UnderlineThin, }; if (cursorType[cursorStyle] !== undefined) { @@ -130,216 +82,24 @@ class ConfigurationClass { } } - - /** - * Use the system's clipboard when copying. - */ - useSystemClipboard = false; - - /** - * Enable ctrl- actions that would override existing VSCode actions. - */ - useCtrlKeys = false; - - /** - * Override default VSCode copy behavior. - */ - overrideCopy = true; - - /** - * Width in characters to word-wrap to. - */ - textwidth = 80; - /** - * Should we highlight incremental search matches? + * Keys to be ignored and NOT handled by the extensions */ - hlsearch = false; - - /** - * Ignore case when searching with / or ?. - */ - ignorecase = true; - - /** - * In / or ?, default to ignorecase=true unless the user types a capital - * letter. - */ - smartcase = true; - - /** - * Indent automatically? - */ - autoindent = true; - - /** - * Use EasyMotion plugin? - */ - easymotion = false; - - /** - * Use surround plugin? - */ - surround = true; - - /** - * Easymotion marker appearance settings - */ - easymotionMarkerBackgroundColor = "#000000"; - easymotionMarkerForegroundColorOneChar = "#ff0000"; - easymotionMarkerForegroundColorTwoChar = "#ffa500"; - easymotionMarkerWidthPerChar = 8; - easymotionMarkerHeight = 14; - easymotionMarkerFontFamily = "Consolas"; - easymotionMarkerFontSize = "14"; - easymotionMarkerFontWeight = "normal"; - easymotionMarkerYOffset = 11; - - /** - * Timeout in milliseconds for remapped commands. - */ - timeout = 1000; - - /** - * Display partial commands on status bar? - */ - showcmd = true; - - /** - * What key should map to in key remappings? - */ - leader = "\\"; - - /** - * How much search or command history should be remembered - */ - history = 50; - - /** - * Show results of / or ? search as user is typing? - */ - incsearch = true; - - /** - * Start in insert mode? - */ - startInInsertMode = false; - - /** - * Enable changing of the status bar color based on mode - */ - statusBarColorControl = false; - - /** - * Status bar colors to change to based on mode - */ - statusBarColors: IStatusBarColors = { - "normal": "#005f5f", - "insert": "#5f0000", - "visual": "#5f00af", - "visualline": "#005f87", - "visualblock": "#86592d", - "replace": "#000000", + ignoreKeys: IgnoredKeys = + { + all: [''], + normal: [''], + insert: [''], + visual: [''] }; - /** - * Color of search highlights. - */ - searchHighlightColor = "rgba(150, 150, 255, 0.3)"; - - /** - * Size of a tab character. - */ - @overlapSetting({ codeName: "tabSize", default: 8 }) - tabstop: number; /** * Type of cursor user is using native to vscode */ - userCursor: number; - - /** - * Use spaces when the user presses tab? - */ - @overlapSetting({ codeName: "insertSpaces", default: false }) - expandtab: boolean; + userCursor: number | undefined; - @overlapSetting({ codeName: "lineNumbers", default: true, codeValueMapping: { true: "on", false: "off" } }) - number: boolean; - - /** - * Show relative line numbers? - */ - @overlapSetting({ codeName: "lineNumbers", default: false, codeValueMapping: { true: "relative", false: "off" } }) - relativenumber: boolean; - - iskeyword: string = "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"; - - /** - * Array of all key combinations that were registered in angle bracket notation - */ - boundKeyCombinations: string[] = []; - - /** - * In visual mode, start a search with * or # using the current selection - */ - visualstar = false; - - /** - * Uses a hack to fix moving around folds. - */ - foldfix = false; - - enableNeovim = true; - - neovimPath = "nvim"; - - disableAnnoyingNeovimMessage = false; -} - -function overlapSetting(args: { codeName: string, default: OptionValue, codeValueMapping?: ValueMapping }) { - return function (target: any, propertyKey: string) { - Object.defineProperty(target, propertyKey, { - get: function () { - if (this["_" + propertyKey] !== undefined) { - return this["_" + propertyKey]; - } - - if (args.codeValueMapping) { - let val = vscode.workspace.getConfiguration("editor").get(args.codeName); - - if (val !== undefined) { - return args.codeValueMapping[val as string]; - } - } else { - return vscode.workspace.getConfiguration("editor").get(args.codeName, args.default); - } - }, - set: function (value) { - this["_" + propertyKey] = value; - - taskQueue.enqueueTask({ - promise: async () => { - if (value === undefined || Globals.isTesting) { - return; - } - - let codeValue = value; - - if (args.codeValueMapping) { - codeValue = args.codeValueMapping[value]; - } - - await vscode.workspace.getConfiguration("editor").update(args.codeName, codeValue, true); - }, - isRunning: false, - queue: "config" - }); - }, - enumerable: true, - configurable: true - }); - }; + neovimPath: string = ''; } -export const Configuration = ConfigurationClass.getInstance(); \ No newline at end of file +export const Configuration = ConfigurationClass.getInstance(); diff --git a/src/error.ts b/src/error.ts deleted file mode 100644 index 1b7f26196b1..00000000000 --- a/src/error.ts +++ /dev/null @@ -1,60 +0,0 @@ -"use strict"; - -import * as util from "./util"; - -interface IErrorMessage { - [index: number] : string; -} - -export enum ErrorCode { - E32 = 32, - E37 = 37, - E208 = 208, - E348 = 348, - E444 = 444, - E488 = 488 -} - -export const ErrorMessage : IErrorMessage = { - 32: "No file name", - 37: "No write since last change (add ! to override)", - 208: "Error writing to file", - 348: "No string under cursor", - 444: "Cannot close last window", - 488: "Trailing characters" -}; - -export class VimError extends Error { - private _code : number; - private _message : string; - - constructor(code : number, message : string) { - super(); - this._code = code; - this._message = message; - } - - static fromCode(code : ErrorCode) : VimError { - if (ErrorMessage[code]) { - return new VimError(code, ErrorMessage[code]); - } - - throw new Error("unknown error code: " + code); - } - - get code() : number { - return this._code; - } - - get message() : string { - return this._message; - } - - display() : void { - util.showError(this.toString()); - } - - toString() : string { - return "E" + this.code.toString() + ": " + this.message; - } -} \ No newline at end of file diff --git a/src/globals.ts b/src/globals.ts index 964db1f9412..2121a3cfcfb 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -6,8 +6,6 @@ export class Globals { // true for running tests, false during regular runtime public static isTesting = false; - public static modeHandlerForTesting: any = undefined; - public static WhitespaceRegExp = new RegExp("^ *$"); // false for disabling Vim temporarily diff --git a/src/history/historyTracker.ts b/src/history/historyTracker.ts deleted file mode 100644 index 8291b11d5dd..00000000000 --- a/src/history/historyTracker.ts +++ /dev/null @@ -1,576 +0,0 @@ -/** - * HistoryTracker is a handrolled undo/redo tracker for VSC. We currently - * track history as a list of "steps", each of which consists of 1 or more - * "changes". - * - * A Change is something like adding or deleting a few letters. - * - * A Step is multiple Changes. - * - * Undo/Redo will advance forward or backwards through Steps. - */ - -import * as vscode from "vscode"; -import * as _ from "lodash"; - -import { Position } from './../common/motion/position'; -import { TextEditor } from './../textEditor'; -import { RecordedState, VimState } from './../mode/modeHandler'; - -import DiffMatchPatch = require("diff-match-patch"); - -const diffEngine = new DiffMatchPatch.diff_match_patch(); -diffEngine.Diff_Timeout = 1; // 1 second - -export class DocumentChange { - start : Position; - text : string; - isAdd : boolean; - - constructor(start: Position, text: string, isAdd: boolean) { - this.start = start; - this.text = text; - this.isAdd = isAdd; - } - - /** - * Run this change. - */ - public async do(undo = false): Promise { - if ((this.isAdd && !undo) || (!this.isAdd && undo)) { - await TextEditor.insert(this.text, this.start, false); - } else { - await TextEditor.delete(new vscode.Range(this.start, this.end())); - } - } - - /** - * Run this change in reverse. - */ - public async undo(): Promise { - return this.do(true); - } - - /** - * the position after advancing start by text - */ - public end(): Position { - return this.start.advancePositionByText(this.text); - } -} - -interface IMark { - name: string; - position: Position; - isUppercaseMark: boolean; -} - -class HistoryStep { - /** - * The insertions and deletions that occured in this history step. - */ - changes: DocumentChange[]; - - - /** - * Whether the user is still inserting or deleting for this history step. - */ - isFinished: boolean; - - /** - * The cursor position at the start of this history step. - */ - cursorStart: Position[] | undefined; - - /** - * The cursor position at the end of this history step so far. - */ - cursorEnd: Position[] | undefined; - - /** - * The position of every mark at the start of this history step. - */ - marks: IMark[] = []; - - vimState: VimState; - - constructor(init: { - changes?: DocumentChange[], - isFinished?: boolean, - cursorStart?: Position[] | undefined, - cursorEnd?: Position[] | undefined, - marks?: IMark[], - }) { - this.changes = init.changes = []; - this.isFinished = init.isFinished || false; - this.cursorStart = init.cursorStart || undefined; - this.cursorEnd = init.cursorEnd || undefined; - this.marks = init.marks || []; - } - - /** - * merge collapses individual character changes into larger blocks of changes - */ - public merge(): void { - if (this.changes.length < 2) { - return; - } - - // merged will replace this.changes - var merged: DocumentChange[] = []; - // manually reduce() this.changes with variables `current` and `next` - // we can't use reduce() directly because the loop can emit multiple elements - var current = this.changes[0]; - for (const next of this.changes.slice(1)) { - if (current.text.length === 0) { - // current is eliminated, replace it with top of merged, or adopt next as current - // see also add+del case - if (merged.length > 0) { - current = merged.pop()!; - } else { - current = next; - continue; - } - } - // merge logic. also compares start & end() Positions to ensure this is the same location - if (current.isAdd && next.isAdd && current.end().isEqual(next.start)) { - // merge add+add together - current.text += next.text; - } else if (!current.isAdd && !next.isAdd && next.end().isEqual(current.start)) { - // merge del+del together, but in reverse so it still reads forward - next.text += current.text; - current = next; - } else if (current.isAdd && !next.isAdd && current.end().isEqual(next.end())) { - // collapse add+del into add. this might make current.text.length === 0, see beginning of loop - current.text = current.text.slice(0, -next.text.length); - } else { - // del+add must be two separate DocumentChanges. e.g. start with "a|b", do `ix` you end up with "|xb" - // also handles multiple changes in distant locations in the document - merged.push(current); - current = next; - } - } - merged.push(current); - this.changes = merged; - } -} - -export class HistoryTracker { - public lastContentChanges: vscode.TextDocumentContentChangeEvent[]; - public currentContentChanges: vscode.TextDocumentContentChangeEvent[]; - - // Current index in changelist for navigation, resets when a new change is made - public changelistIndex = 0; - - public lastInvokedMacro: RecordedState; - - /** - * The entire Undo/Redo stack. - */ - private historySteps: HistoryStep[] = []; - - /** - * Our index in the Undo/Redo stack. - */ - private currentHistoryStepIndex = 0; - - /** - * The text of the document the last time we diffed against it. - */ - private oldText: string; - - private vimState: VimState; - - private get currentHistoryStep(): HistoryStep { - if (this.currentHistoryStepIndex === -1) { - console.log("Tried to modify history at index -1"); - - throw new Error(); - } - - return this.historySteps[this.currentHistoryStepIndex]; - } - - constructor(vimState: VimState) { - this.vimState = vimState; - - this._initialize(); - } - - public getAllText(): string { - return this.vimState.editor.document.getText(); - } - - public clear() { - this.historySteps = []; - this.currentHistoryStepIndex = 0; - this._initialize(); - } - - /** - * We add an initial, unrevertable step, which inserts the entire document. - */ - private _initialize() { - this.historySteps.push(new HistoryStep({ - changes : [new DocumentChange(new Position(0, 0), this.getAllText(), true)], - isFinished : true, - cursorStart: [ new Position(0, 0) ], - cursorEnd: [ new Position(0, 0) ] - })); - - this.finishCurrentStep(); - - this.oldText = this.getAllText(); - this.currentContentChanges = []; - this.lastContentChanges = []; - } - - private _addNewHistoryStep(): void { - this.historySteps.push(new HistoryStep({ - marks: this.currentHistoryStep.marks - })); - - this.currentHistoryStepIndex++; - } - - /** - * Marks refer to relative locations in the document, rather than absolute ones. - * - * This big gnarly method updates our marks such that they continue to mark - * the same character when the user does a document edit that would move the - * text that was marked. - */ - private updateAndReturnMarks(): IMark[] { - const previousMarks = this.currentHistoryStep.marks; - let newMarks: IMark[] = []; - - // clone old marks into new marks - for (const mark of previousMarks) { - newMarks.push({ - name : mark.name, - position : mark.position, - isUppercaseMark : mark.isUppercaseMark - }); - } - - for (const change of this.currentHistoryStep.changes) { - for (const newMark of newMarks) { - // Run through each character added/deleted, and see if it could have - // affected the position of this mark. - - let pos: Position = change.start; - - if (change.isAdd) { - // (Yes, I could merge these together, but that would obfusciate the logic.) - - for (const ch of change.text) { - - // Update mark - - if (pos.compareTo(newMark.position) <= 0) { - if (ch === "\n") { - newMark.position = new Position(newMark.position.line + 1, newMark.position.character); - } else if (ch !== "\n" && pos.line === newMark.position.line) { - newMark.position = new Position(newMark.position.line, newMark.position.character + 1); - } - } - - // Advance position - - if (ch === "\n") { - pos = new Position(pos.line + 1, 0); - } else { - pos = new Position(pos.line, pos.character + 1); - } - } - } else { - for (const ch of change.text) { - - // Update mark - - if (pos.compareTo(newMark.position) < 0) { - if (ch === "\n") { - newMark.position = new Position(newMark.position.line - 1, newMark.position.character); - } else if (pos.line === newMark.position.line) { - newMark.position = new Position(newMark.position.line, newMark.position.character - 1); - } - } - - // De-advance position - // (What's the opposite of advance? Retreat position?) - - if (ch === "\n") { - // The 99999 is a bit of a hack here. It's very difficult and - // completely unnecessary to get the correct position, so we - // just fake it. - pos = new Position(Math.max(pos.line - 1, 0), 99999); - } else { - pos = new Position(pos.line, Math.max(pos.character - 1, 0)); - } - } - } - } - } - - // Ensure the position of every mark is within the range of the document. - - for (const mark of newMarks) { - if (mark.position.compareTo(mark.position.getDocumentEnd()) > 0) { - mark.position = mark.position.getDocumentEnd(); - } - } - - return newMarks; - } - - /** - * Adds a mark. - */ - addMark(position: Position, markName: string): void { - const newMark: IMark = { - position, - name: markName, - isUppercaseMark: markName === markName.toUpperCase() - }; - const previousIndex = _.findIndex(this.currentHistoryStep.marks, mark => mark.name === markName); - - if (previousIndex !== -1) { - this.currentHistoryStep.marks[previousIndex] = newMark; - } else { - this.currentHistoryStep.marks.push(newMark); - } - } - - /** - * Retrieves a mark. - */ - getMark(markName: string): IMark { - return _.find(this.currentHistoryStep.marks, mark => mark.name === markName); - } - - getMarks(): IMark[] { - return this.currentHistoryStep.marks; - } - - /** - * Adds an individual Change to the current Step. - * - * Determines what changed by diffing the document against what it - * used to look like. - */ - addChange(cursorPosition = [ new Position(0, 0) ]): void { - const newText = this.getAllText(); - - if (newText === this.oldText) { return; } - - // Determine if we should add a new Step. - - if (this.currentHistoryStepIndex === this.historySteps.length - 1 && - this.currentHistoryStep.isFinished) { - - this._addNewHistoryStep(); - } else if (this.currentHistoryStepIndex !== this.historySteps.length - 1) { - this.historySteps = this.historySteps.slice(0, this.currentHistoryStepIndex + 1); - - this._addNewHistoryStep(); - } - - // TODO: This is actually pretty stupid! Since we already have the cursorPosition, - // and most diffs are just +/- a few characters, we can just do a direct comparison rather - // than using jsdiff. - - // The difficulty is with a few rare commands like :%s/one/two/g that make - // multiple changes in different places simultaneously. For those, we could require - // them to call addChange manually, I guess... - - const diffs = diffEngine.diff_main(this.oldText, newText); - - /* - this.historySteps.push(new HistoryStep({ - changes : [new DocumentChange(new Position(0, 0), TextEditor.getAllText(), true)], - isFinished : true, - cursorStart: new Position(0, 0) - })); - */ - - let currentPosition = new Position(0, 0); - - for (const diff of diffs) { - const [whatHappened, text] = diff; - const added = whatHappened === DiffMatchPatch.DIFF_INSERT; - const removed = whatHappened === DiffMatchPatch.DIFF_DELETE; - - let change: DocumentChange; - // let lastChange = this.currentHistoryStep.changes.length > 1 && - // this.currentHistoryStep.changes[this.currentHistoryStep.changes.length - 2]; - - if (added || removed) { - change = new DocumentChange(currentPosition, text, !!added); - - this.currentHistoryStep.changes.push(change); - - if (change && this.currentHistoryStep.cursorStart === undefined) { - this.currentHistoryStep.cursorStart = cursorPosition; - } - } - - if (!removed) { - currentPosition = currentPosition.advancePositionByText(text); - } - } - - this.currentHistoryStep.cursorEnd = cursorPosition; - this.oldText = newText; - - // A change has been made, reset the changelist navigation index to the end - this.changelistIndex = this.historySteps.length - 1; - } - - /** - * Both undoes and completely removes the last n changes applied. - */ - async undoAndRemoveChanges(n: number): Promise { - if (this.currentHistoryStep.changes.length < n) { - console.log("Something bad happened in removeChange"); - - return; - } - - for (let i = 0; i < n; i++) { - await this.currentHistoryStep.changes.pop()!.undo(); - } - - this.ignoreChange(); - } - - /** - * Tells the HistoryTracker that although the document has changed, we should simply - * ignore that change. Most often used when the change was itself triggered by - * the HistoryTracker. - */ - ignoreChange(): void { - this.oldText = this.getAllText(); - } - - /** - * Until we mark it as finished, the active Step will - * accrue multiple changes. This function will mark it as finished, - * and the next time we add a change, it'll be added to a new Step. - */ - finishCurrentStep(): void { - if (this.currentHistoryStep.changes.length === 0 || - this.currentHistoryStep.isFinished) { - return; - } - - this.currentHistoryStep.isFinished = true; - - this.currentHistoryStep.merge(); - - this.currentHistoryStep.marks = this.updateAndReturnMarks(); - } - - /** - * Essentially Undo or ctrl+z. Returns undefined if there's no more steps - * back to go. - */ - async goBackHistoryStep(): Promise { - let step: HistoryStep; - - if (this.currentHistoryStepIndex === 0) { - return undefined; - } - - if (this.currentHistoryStep.changes.length === 0) { - this.currentHistoryStepIndex--; - } - - if (this.currentHistoryStepIndex === 0) { - return undefined; - } - - step = this.currentHistoryStep; - - for (const change of step.changes.slice(0).reverse()) { - await change!.undo(); - } - - this.currentHistoryStepIndex--; - - return step && step.cursorStart; - } - - /** - * Essentially Redo or ctrl+y. Returns undefined if there's no more steps - * forward to go. - */ - async goForwardHistoryStep(): Promise { - let step: HistoryStep; - - if (this.currentHistoryStepIndex === this.historySteps.length - 1) { - return undefined; - } - - this.currentHistoryStepIndex++; - - step = this.currentHistoryStep; - - for (const change of step.changes) { - await change.do(); - } - - return step.cursorStart; - } - - getLastHistoryEndPosition(): Position[] | undefined { - if (this.currentHistoryStepIndex === 0) { - return undefined; - } - - return this.historySteps[this.currentHistoryStepIndex].cursorEnd; - } - - setLastHistoryEndPosition(pos: Position[]) { - this.historySteps[this.currentHistoryStepIndex].cursorEnd = pos; - } - - getChangePositionAtindex(index: number): Position[] | undefined { - if (this.currentHistoryStepIndex === 0) { - return undefined; - } - - let pos = this.getLastHistoryEndPosition(); - pos = undefined; - - if (this.historySteps[index] !== undefined) { - if (this.historySteps[index].changes.length > 0) { - if (this.historySteps[index].changes[0].isAdd) { - pos = [this.historySteps[index].changes[0].end()]; - } else { - pos = [this.historySteps[index].changes[0].start]; - } - } - } - - return pos; - } - - /** - * Handy for debugging the undo/redo stack. + means our current position, check - * means active. - */ - toString(): string { - let result = ""; - - for (let i = 0; i < this.historySteps.length; i++) { - const step = this.historySteps[i]; - - result += step.changes.map(x => x.text.replace(/\n/g, "\\n")).join(""); - if (this.currentHistoryStepIndex === i) { result += "+"; } - if (step.isFinished) { result += "✓"; } - result += "| "; - } - - return result; - } -} \ No newline at end of file diff --git a/src/mode/mode.ts b/src/mode/mode.ts deleted file mode 100644 index cca7f578830..00000000000 --- a/src/mode/mode.ts +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; - -export enum ModeName { - Normal, - Insert, - Visual, - VisualBlock, - VisualLine, - SearchInProgressMode, - Replace, - EasyMotionMode, - SurroundInputMode, -} - -export enum VSCodeVimCursorType { - Block, - Line, - LineThin, - Underline, - TextDecoration, - Native -} - -export abstract class Mode { - private _isActive: boolean; - private _name: ModeName; - - public text: string; - public cursorType: VSCodeVimCursorType; - - public isVisualMode = false; - - constructor(name: ModeName) { - this._name = name; - this._isActive = false; - } - - get name(): ModeName { - return this._name; - } - - get isActive() : boolean { - return this._isActive; - } - - set isActive(val : boolean) { - this._isActive = val; - } -} \ No newline at end of file diff --git a/src/mode/modeEasyMotion.ts b/src/mode/modeEasyMotion.ts deleted file mode 100644 index 90a6cd8d51f..00000000000 --- a/src/mode/modeEasyMotion.ts +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -import { ModeName, Mode } from './mode'; -import { VSCodeVimCursorType } from './mode'; - -export class EasyMotionMode extends Mode { - public text = "EasyMotion Mode"; - public cursorType = VSCodeVimCursorType.Block; - - constructor() { - super(ModeName.EasyMotionMode); - } -} diff --git a/src/mode/modeHandler.ts b/src/mode/modeHandler.ts deleted file mode 100644 index 7670e8c0365..00000000000 --- a/src/mode/modeHandler.ts +++ /dev/null @@ -1,1939 +0,0 @@ -"use strict"; -import { SurroundInputMode } from './surroundInputMode'; - -import * as vscode from 'vscode'; -import * as _ from 'lodash'; - -import { EditorIdentity } from './../../extension'; -import { - isTextTransformation, - TextTransformations, - areAnyTransformationsOverlapping, - Transformation, -} from './../transformations/transformations'; -import { Mode, ModeName, VSCodeVimCursorType } from './mode'; -import { InsertModeRemapper, OtherModesRemapper } from './remapper'; -import { NormalMode } from './modeNormal'; -import { InsertMode } from './modeInsert'; -import { VisualBlockMode} from './modeVisualBlock'; -import { VisualMode } from './modeVisual'; -import { taskQueue } from './../taskQueue'; -import { ReplaceMode } from './modeReplace'; -import { EasyMotionMode } from './modeEasyMotion'; -import { SearchInProgressMode } from './modeSearchInProgress'; -import { TextEditor } from './../textEditor'; -import { VisualLineMode } from './modeVisualLine'; -import { HistoryTracker } from './../history/historyTracker'; -import { EasyMotion } from './../actions/plugins/easymotion/easymotion'; -import { Actions, KeypressState, BaseAction } from './../actions/base'; -import { BaseOperator } from './../actions/operator'; -import { BaseMovement, isIMovement } from './../actions/motion'; -import { - BaseCommand, DocumentContentChangeAction, CommandQuitRecordMacro } from './../actions/commands/actions'; -import { - CommandInsertInInsertMode, CommandInsertPreviousText -} from './../actions/commands/insert'; -import { Position, PositionDiff } from './../common/motion/position'; -import { Range } from './../common/motion/range'; -import { RegisterMode, Register } from './../register/register'; -import { showCmdLine } from '../../src/cmd_line/main'; -import { Configuration } from '../../src/configuration/configuration'; -import { PairMatcher } from './../common/matching/matcher'; -import { Globals } from '../../src/globals'; -import { ReplaceState } from './../state/replaceState'; -import { GlobalState } from './../state/globalState'; -import { Nvim } from "promised-neovim-client"; -import { allowVSCodeToPropagateCursorUpdatesAndReturnThem } from "../util"; - -export class ViewChange { - public command: string; - public args: any; -} - -/** - * The VimState class holds permanent state that carries over from action - * to action. - * - * Actions defined in actions.ts are only allowed to mutate a VimState in order to - * indicate what they want to do. - */ -export class VimState { - private _id = Math.floor(Math.random() * 10000) % 10000; - - public get id(): number { return this._id; } - - /** - * The column the cursor wants to be at, or Number.POSITIVE_INFINITY if it should always - * be the rightmost column. - * - * Example: If you go to the end of a 20 character column, this value - * will be 20, even if you press j and the next column is only 5 characters. - * This is because if the third column is 25 characters, the cursor will go - * back to the 20th column. - */ - public desiredColumn = 0; - - public historyTracker: HistoryTracker; - - public easyMotion: EasyMotion; - - /** - * Just for debugging!! - */ - public identity: EditorIdentity; - - public editor: vscode.TextEditor; - - /** - * For timing out remapped keys like jj to esc. - */ - public lastKeyPressedTimestamp = 0; - - /** - * Are multiple cursors currently present? - */ - public isMultiCursor = false; - - // Is the multicursor something like visual block "multicursor", where - // natively in vim there would only be one cursor whose changes were applied - // to all lines after edit. - public isFakeMultiCursor = false; - - /** - * Tracks movements that can be repeated with ; and , (namely t, T, f, and F). - */ - public static lastRepeatableMovement : BaseMovement | undefined = undefined; - - public lastMovementFailed: boolean = false; - - public alteredHistory = false; - - public isRunningDotCommand = false; - - public focusChanged = false; - - public surround: undefined | { - active: boolean; - operator: "change" | "delete" | "yank"; - target: string | undefined; - replacement: string | undefined; - range: Range | undefined; - isVisualLine: boolean; - } = undefined; - - /** - * Used for command like which allows you to return to insert after a command - */ - public returnToInsertAfterCommand = false; - public actionCount = 0; - - /** - * Every time we invoke a VS Code command which might trigger Code's view update, - * we should postpone its view updating phase to avoid conflicting with our internal view updating mechanism. - * This array is used to cache every VS Code view updating event and they will be triggered once we run the inhouse `viewUpdate`. - */ - public postponedCodeViewChanges: ViewChange[] = []; - - /** - * Used to prevent non-recursive remappings from looping. - */ - public isCurrentlyPerformingRemapping = false; - - /** - * All the keys we've pressed so far. - */ - public keyHistory: string[] = []; - - public globalState: GlobalState = new GlobalState; - - /** - * The position the cursor will be when this action finishes. - */ - public get cursorPosition(): Position { - return this.allCursors[0].stop; - } - public set cursorPosition(value: Position) { - this.allCursors[0] = this.allCursors[0].withNewStop(value); - } - - /** - * The effective starting position of the movement, used along with cursorPosition to determine - * the range over which to run an Operator. May rarely be different than where the cursor - * actually starts e.g. if you use the "aw" text motion in the middle of a word. - */ - public get cursorStartPosition(): Position { - return this.allCursors[0].start; - } - public set cursorStartPosition(value: Position) { - this.allCursors[0] = this.allCursors[0].withNewStart(value); - } - - /** - * In Multi Cursor Mode, the position of every cursor. - */ - private _allCursors: Range[] = [ new Range(new Position(0, 0), new Position(0, 0)) ]; - - public get allCursors(): Range[] { - return this._allCursors; - } - - public set allCursors(value: Range[]) { - for (const cursor of value) { - if (!cursor.start.isValid() || !cursor.stop.isValid()) { - console.log("invalid value for set cursor position. This is probably bad?"); - } - } - - this._allCursors = value; - - this.isMultiCursor = this._allCursors.length > 1; - } - - public cursorPositionJustBeforeAnythingHappened = [ new Position(0, 0) ]; - - public isRecordingMacro: boolean = false; - public isReplayingMacro: boolean = false; - - public replaceState: ReplaceState | undefined = undefined; - - /** - * Stores last visual mode for gv - */ - public lastVisualMode: ModeName; - - /** - * Last selection that was active - */ - public lastVisualSelectionStart: Position; - public lastVisualSelectionEnd: Position; - - /** - * Was the previous mouse click past EOL - */ - public lastClickWasPastEol: boolean = false; - - /** - * The mode Vim will be in once this action finishes. - */ - private _currentMode: ModeName; - - public get currentMode(): number { - return this._currentMode; - } - - public set currentMode(value: number) { - this._currentMode = value; - - vscode.commands.executeCommand('setContext', 'vim.mode', ModeName[value]); - } - - public currentModeName(): string { - return ModeName[this._currentMode]; - } - - public getModeObject(modeHandler: ModeHandler): Mode { - return modeHandler.modeList.find(mode => mode.isActive)!; - } - - public currentRegisterMode = RegisterMode.FigureItOutFromCurrentMode; - - public effectiveRegisterMode(): RegisterMode { - if (this.currentRegisterMode === RegisterMode.FigureItOutFromCurrentMode) { - if (this.currentMode === ModeName.VisualLine) { - return RegisterMode.LineWise; - } else if (this.currentMode === ModeName.VisualBlock) { - return RegisterMode.BlockWise; - } else { - return RegisterMode.CharacterWise; - } - } else { - return this.currentRegisterMode; - } - } - - /** - * The top left of a selected block of text. Useful for Visual Block mode. - */ - public get topLeft(): Position { - return VisualBlockMode.getTopLeftPosition(this.cursorStartPosition, this.cursorPosition); - } - - /** - * The bottom right of a selected block of text. Useful for Visual Block mode. - */ - public get bottomRight(): Position { - return VisualBlockMode.getBottomRightPosition(this.cursorStartPosition, this.cursorPosition); - } - - public registerName = '"'; - - public commandInitialText = ""; - - public recordedState = new RecordedState(); - - public recordedMacro = new RecordedState(); - - /** - * Programmatically triggering an edit will unfortunately ALSO trigger our mouse update - * function. We use this variable to determine if the update function was triggered - * by us or by a mouse action. - */ - public whatILastSetTheSelectionTo: vscode.Selection; - - public nvim: Nvim; -} - -/** - * The RecordedState class holds the current action that the user is - * doing. Example: Imagine that the user types: - * - * 5"qdw - * - * Then the relevent state would be - * * count of 5 - * * copy into q register - * * delete operator - * * word movement - * - * - * Or imagine the user types: - * - * vw$}}d - * - * Then the state would be - * * Visual mode action - * * (a list of all the motions you ran) - * * delete operator - */ -export class RecordedState { - constructor() { - this.registerName = Configuration.useSystemClipboard ? '*' : '"'; - } - - /** - * The keys the user has pressed that have not caused an action to be - * executed yet. Used for showcmd and command remapping. - */ - public commandList: string[] = []; - - /** - * String representation of the exact keys that the user entered. Used for - * showcmd. - */ - public get commandString(): string { - let result = ""; - - for (const key of this.commandList) { - if (key === Configuration.leader) { - result += ""; - } else { - result += key; - } - } - - return result; - } - /** - * get the current command without the prefixed count. - * For instance: if the current commandList is ['2', 'h'], returns only ['h']. - */ - public getCurrentCommandWithoutCountPrefix(): string[] { - const commandList = this.commandList; - const result = []; - let previousWasCount = true; - - for (const commandKey of commandList) { - if (previousWasCount && commandKey.match(/[0-9]/)) { - continue; - } else { - previousWasCount = false; - result.push(commandKey); - } - } - - return result; - } - - /** - * Keeps track of keys pressed for the next action. Comes in handy when parsing - * multiple length movements, e.g. gg. - */ - public actionKeys: string[] = []; - - /** - * Every action that has been run. - */ - public actionsRun: BaseAction[] = []; - - public hasRunOperator = false; - - public hasRunSurround = false; - public surroundKeys: string[] = []; - public surroundKeyIndexStart = 0; - - /** - * This is kind of a hack and should be associated with something like this: - * - * https://github.com/VSCodeVim/Vim/issues/805 - */ - public operatorPositionDiff: PositionDiff | undefined; - - public isInsertion = false; - - /** - * The text transformations that we want to run. They will all be run after the action has been processed. - * - * Running an individual action will generally queue up to one of these, but if you're in - * multi-cursor mode, you'll queue one per cursor, or more. - * - * Note that the text transformations are run in parallel. This is useful in most cases, - * but will get you in trouble in others. - */ - public transformations: Transformation[] = []; - - /** - * The operator (e.g. d, y, >) the user wants to run, if there is one. - */ - public get operator(): BaseOperator { - let list = _.filter(this.actionsRun, a => a instanceof BaseOperator).reverse(); - return list[0] as any; - } - - public get operators(): BaseOperator[] { - return _.filter(this.actionsRun, a => a instanceof BaseOperator).reverse() as any; - } - - /** - * The command (e.g. i, ., R, /) the user wants to run, if there is one. - */ - public get command(): BaseCommand { - const list = _.filter(this.actionsRun, a => a instanceof BaseCommand); - - // TODO - disregard , then assert this is of length 1. - - return list[0] as any; - } - - public get hasRunAMovement(): boolean { - return _.filter(this.actionsRun, a => a.isMotion).length > 0; - } - - /** - * The number of times the user wants to repeat this action. - */ - public count: number = 0; - - /** - * The register name for this action. - */ - public registerName: string; - - public clone(): RecordedState { - const res = new RecordedState(); - - // TODO: Actual clone. - - res.actionKeys = this.actionKeys.slice(0); - res.actionsRun = this.actionsRun.slice(0); - res.hasRunOperator = this.hasRunOperator; - res.hasRunSurround = this.hasRunSurround; - res.surroundKeys = this.surroundKeys; - - return res; - } - - public operatorReadyToExecute(mode: ModeName): boolean { - // Visual modes do not require a motion -- they ARE the motion. - return this.operator && - !this.hasRunOperator && - mode !== ModeName.SearchInProgressMode && - (this.hasRunAMovement || - (mode === ModeName.Visual || mode === ModeName.VisualLine ) || - this.operators.length > 1 && this.operators.reverse()[0].constructor === this.operators.reverse()[1].constructor); - } - - public get isInInitialState(): boolean { - return this.operator === undefined && - this.actionsRun.length === 0 && - this.count === 1; - } -} - -interface IViewState { - selectionStart: Position; - selectionStop : Position; - currentMode : ModeName; -} - -export class ModeHandler implements vscode.Disposable { - public static IsTesting = false; - - private _toBeDisposed: vscode.Disposable[] = []; - private _modes: Mode[]; - private static _statusBarItem: vscode.StatusBarItem; - private _vimState: VimState; - private _insertModeRemapper: InsertModeRemapper; - private _otherModesRemapper: OtherModesRemapper; - private _otherModesNonRecursive: OtherModesRemapper; - private _insertModeNonRecursive: InsertModeRemapper; - - public get vimState(): VimState { - return this._vimState; - } - - /** - * Identity associated with this ModeHandler. Only used for debugging. - */ - public identity: EditorIdentity; - - private _caretDecoration = vscode.window.createTextEditorDecorationType({ - dark: { - // used for dark colored themes - backgroundColor: 'rgba(224, 224, 224, 0.4)', - borderColor: 'rgba(0, 0, 0, 1.0)' - }, - light: { - // used for light colored themes - backgroundColor: 'rgba(32, 32, 32, 0.4)', - borderColor: 'rgba(0, 0, 0, 1.0)' - }, - borderStyle: 'solid', - borderWidth: '1px' - }); - - private _searchHighlightDecoration: vscode.TextEditorDecorationType; - - private get currentModeName(): ModeName { - return this.currentMode.name; - } - - public get modeList(): Mode[] { - return this._modes; - } - - /** - * isTesting speeds up tests drastically by turning off our checks for - * mouse events. - */ - constructor() { - ModeHandler.IsTesting = Globals.isTesting; - - this._vimState = new VimState(); - this._vimState.editor = vscode.window.activeTextEditor!; - - this.identity = new EditorIdentity(vscode.window.activeTextEditor); - - this._vimState.identity = this.identity; - this.createRemappers(); - - this._modes = [ - new NormalMode(this), - new InsertMode(), - new VisualMode(), - new VisualBlockMode(), - new VisualLineMode(), - new SearchInProgressMode(), - new ReplaceMode(), - new EasyMotionMode(), - new SurroundInputMode(), - ]; - this.vimState.historyTracker = new HistoryTracker(this.vimState); - this.vimState.easyMotion = new EasyMotion(); - if (Configuration.startInInsertMode) { - this._vimState.currentMode = ModeName.Insert; - } else { - this._vimState.currentMode = ModeName.Normal; - } - - this._searchHighlightDecoration = vscode.window.createTextEditorDecorationType({ - backgroundColor: Configuration.searchHighlightColor - }); - - this.setCurrentModeByName(this._vimState); - - // Sometimes, Visual Studio Code will start the cursor in a position which - // is not (0, 0) - e.g., if you previously edited the file and left the - // cursor somewhere else when you closed it. This will set our cursor's - // position to the position that VSC set it to. - - // This also makes things like gd work. - // For whatever reason, the editor positions aren't updated until after the - // stack clears, which is why this setTimeout is necessary - setTimeout(() => { - this.syncCursors(); - }, 0); - - // Handle scenarios where mouse used to change current position. - const disposer = vscode.window.onDidChangeTextEditorSelection((e: vscode.TextEditorSelectionChangeEvent) => { - if (!Globals.active) { - return; - } - - taskQueue.enqueueTask({ - promise: () => this.handleSelectionChange(e), - isRunning: false, - - /** - * We don't want these to become backlogged! If they do, we'll update - * the selection to an incorrect value and see a jittering cursor. - */ - highPriority: true, - }); - }); - - this._toBeDisposed.push(disposer); - } - - /** - * create remappers after a configuration change - */ - createRemappers() { - this._insertModeRemapper = new InsertModeRemapper(true); - this._otherModesRemapper = new OtherModesRemapper(true); - this._insertModeNonRecursive = new InsertModeRemapper(false); - this._otherModesNonRecursive = new OtherModesRemapper(false); - } - - /** - * This is easily the worst function in VSCodeVim. - * - * We need to know when VSCode has updated our selection, so that we can sync - * that internally. Unfortunately, VSCode has a habit of calling this - * function at weird times, or or with incomplete information, so we have to - * do a lot of voodoo to make sure we're updating the cursors correctly. - * - * Even worse, we don't even know how to test this stuff. - * - * Anyone who wants to change the behavior of this method should make sure - * all selection related test cases pass. Follow this spec - * https://gist.github.com/rebornix/d21d1cc060c009d4430d3904030bd4c1 to - * perform the manual testing. - */ - private async handleSelectionChange(e: vscode.TextEditorSelectionChangeEvent): Promise { - let selection = e.selections[0]; - - if (ModeHandler.IsTesting) { - return; - } - - if (e.textEditor !== this.vimState.editor) { - return; - } - - if (this._vimState.focusChanged) { - this._vimState.focusChanged = false; - - return; - } - - if (this.currentModeName === ModeName.EasyMotionMode) { - // AArrgghhhh - johnfn - - return; - } - - if ((e.selections.length !== this.vimState.allCursors.length || this.vimState.isMultiCursor) && - this.vimState.currentMode !== ModeName.VisualBlock) { - // Number of selections changed, make sure we know about all of them still - this.vimState.allCursors = e.textEditor.selections.map(x => - new Range(Position.FromVSCodePosition(x.start), Position.FromVSCodePosition(x.end))); - - await this.updateView(this.vimState); - - return; - } - - /** - * We only trigger our view updating process if it's a mouse selection. - * Otherwise we only update our internal cursor postions accordingly. - */ - if (e.kind !== vscode.TextEditorSelectionChangeKind.Mouse) { - if (selection) { - if (this._vimState.getModeObject(this).isVisualMode) { - /** - * In Visual Mode, our `cursorPosition` and `cursorStartPosition` can not refect `active`, - * `start`, `end` and `anchor` information in a selection. - * See `Fake block cursor with text decoration` section of `updateView` method. - */ - return; - } - - this._vimState.cursorPosition = Position.FromVSCodePosition(selection.active); - this._vimState.cursorStartPosition = Position.FromVSCodePosition(selection.start); - } - return; - } - - if (this._vimState.isMultiCursor && e.selections.length === 1) { - this._vimState.isMultiCursor = false; - } - - // See comment about whatILastSetTheSelectionTo. - if (this._vimState.whatILastSetTheSelectionTo.isEqual(selection)) { - return; - } - - if (this._vimState.currentMode === ModeName.SearchInProgressMode) { - return; - } - - let toDraw = false; - - if (selection) { - let newPosition = new Position(selection.active.line, selection.active.character); - - // Only check on a click, not a full selection (to prevent clicking past EOL) - if (newPosition.character >= newPosition.getLineEnd().character && selection.isEmpty) { - if (this._vimState.currentMode !== ModeName.Insert) { - - this._vimState.lastClickWasPastEol = true; - - // This prevents you from mouse clicking past the EOL - newPosition = new Position(newPosition.line, Math.max(newPosition.getLineEnd().character - 1, 0)); - - // Switch back to normal mode since it was a click not a selection - this._vimState.currentMode = ModeName.Normal; - this.setCurrentModeByName(this._vimState); - - toDraw = true; - } - } else if (selection.isEmpty) { - this._vimState.lastClickWasPastEol = false; - } - - this._vimState.cursorPosition = newPosition; - this._vimState.cursorStartPosition = newPosition; - this._vimState.desiredColumn = newPosition.character; - - // start visual mode? - - - if (selection.anchor.line === selection.active.line - && selection.anchor.character >= newPosition.getLineEnd().character - 1 - && selection.active.character >= newPosition.getLineEnd().character - 1) { - // This prevents you from selecting EOL - } else if (!selection.anchor.isEqual(selection.active)) { - var selectionStart = new Position(selection.anchor.line, selection.anchor.character); - - if (selectionStart.character > selectionStart.getLineEnd().character) { - selectionStart = new Position(selectionStart.line, selectionStart.getLineEnd().character); - } - - this._vimState.cursorStartPosition = selectionStart; - - if (selectionStart.compareTo(newPosition) > 0) { - this._vimState.cursorStartPosition = this._vimState.cursorStartPosition.getLeft(); - } - - // If we prevented from clicking past eol but it is part of this selection, include the last char - if (this._vimState.lastClickWasPastEol) { - const newStart = new Position(selection.anchor.line, selection.anchor.character + 1); - this._vimState.editor.selection = new vscode.Selection(newStart, selection.end); - this._vimState.cursorStartPosition = selectionStart; - this._vimState.lastClickWasPastEol = false; - } - - if (!this._vimState.getModeObject(this).isVisualMode) { - this._vimState.currentMode = ModeName.Visual; - this.setCurrentModeByName(this._vimState); - - // double click mouse selection causes an extra character to be selected so take one less character - } - } else { - if (this._vimState.currentMode !== ModeName.Insert) { - this._vimState.currentMode = ModeName.Normal; - this.setCurrentModeByName(this._vimState); - } - } - - await this.updateView(this._vimState, {drawSelection: toDraw, revealRange: true}); - } - } - - /** - * The active mode. - */ - get currentMode(): Mode { - return this._modes.find(mode => mode.isActive)!; - } - - setCurrentModeByName(vimState: VimState): void { - let activeMode: Mode; - - this._vimState.currentMode = vimState.currentMode; - - for (let mode of this._modes) { - if (mode.name === vimState.currentMode) { - activeMode = mode; - } - - mode.isActive = (mode.name === vimState.currentMode); - } - } - - async handleKeyEvent(key: string): Promise { - const now = Number(new Date()); - - // Rewrite some commands. The conditions when you trigger a "copy" rather than a ctrl-c are - // too sophisticated to be covered by the "when" condition in package.json - - if (Configuration.overrideCopy) { - if (key === "") { - key = ""; - } - - if (process.platform !== "darwin" && key === "" && !Configuration.useCtrlKeys) { - key = ""; - } - } - if (key === "" && !Configuration.useCtrlKeys) { - key = ""; - } - this._vimState.cursorPositionJustBeforeAnythingHappened = this._vimState.allCursors.map(x => x.stop); - this._vimState.recordedState.commandList.push(key); - - try { - // Take the count prefix out to perform the correct remapping. - const keys = this._vimState.recordedState.getCurrentCommandWithoutCountPrefix(); - const withinTimeout = now - this._vimState.lastKeyPressedTimestamp < Configuration.timeout; - - let handled = false; - - /** - * Check that - * - * 1) We are not already performing a nonrecursive remapping. - * 2) We haven't timed out of our previous remapping. - * 3) We are not in the middle of executing another command. - */ - - if (!this._vimState.isCurrentlyPerformingRemapping && - (withinTimeout || keys.length === 1)) { - - - // User remappings bork the tests. If the the remappings start getting tested - // at some point, will probably need a new solution. - if (!ModeHandler.IsTesting) { - handled = handled || await this._insertModeRemapper.sendKey(keys, this, this.vimState); - handled = handled || await this._otherModesRemapper.sendKey(keys, this, this.vimState); - handled = handled || await this._insertModeNonRecursive.sendKey(keys, this, this.vimState); - handled = handled || await this._otherModesNonRecursive.sendKey(keys, this, this.vimState); - } - } - - if (!handled) { - this._vimState = await this.handleKeyEventHelper(key, this._vimState); - } else { - this._vimState.recordedState.commandList = []; - } - } catch (e) { - console.log('error.stack'); - console.log(e); - console.log(e.stack); - } - - this._vimState.lastKeyPressedTimestamp = now; - this._renderStatusBar(); - - return true; - } - - - async handleKeyEventHelper(key: string, vimState: VimState): Promise { - - // Just nope right out of here. - if (vscode.window.activeTextEditor !== this.vimState.editor) { return this.vimState; } - - // Catch any text change not triggered by us (example: tab completion). - vimState.historyTracker.addChange(this._vimState.cursorPositionJustBeforeAnythingHappened); - - let recordedState = vimState.recordedState; - - recordedState.actionKeys.push(key); - vimState.keyHistory.push(key); - - let result = Actions.getRelevantAction(recordedState.actionKeys, vimState); - - const isPotentialRemapping = this._insertModeNonRecursive.couldRemappingApply || - this._insertModeRemapper.couldRemappingApply || - this._otherModesRemapper.couldRemappingApply || - this._otherModesNonRecursive.couldRemappingApply; - - if (result === KeypressState.NoPossibleMatch && !isPotentialRemapping) { - vimState.recordedState = new RecordedState(); - vimState.recordedState.commandList = []; - - return vimState; - } else if (result === KeypressState.WaitingOnKeys) { - - return vimState; - } - - let action = result as BaseAction; - let actionToRecord: BaseAction | undefined = action; - - if (recordedState.actionsRun.length === 0) { - recordedState.actionsRun.push(action); - } else { - let lastAction = recordedState.actionsRun[recordedState.actionsRun.length - 1]; - - if (lastAction instanceof DocumentContentChangeAction) { - lastAction.keysPressed.push(key); - - if (action instanceof CommandInsertInInsertMode || action instanceof CommandInsertPreviousText) { - // delay the macro recording - actionToRecord = undefined; - } else { - // Push document content change to the stack - lastAction.contentChanges = lastAction.contentChanges.concat( - vimState.historyTracker.currentContentChanges.map(x => ({ - textDiff: x, - positionDiff: new PositionDiff(0, 0) - })) - ); - vimState.historyTracker.currentContentChanges = []; - recordedState.actionsRun.push(action); - } - } else { - if (action instanceof CommandInsertInInsertMode || action instanceof CommandInsertPreviousText) { - // This means we are already in Insert Mode but there is still not DocumentContentChangeAction in stack - vimState.historyTracker.currentContentChanges = []; - let newContentChange = new DocumentContentChangeAction(); - newContentChange.keysPressed.push(key); - recordedState.actionsRun.push(newContentChange); - actionToRecord = newContentChange; - } else { - recordedState.actionsRun.push(action); - } - } - } - - if (vimState.isRecordingMacro && actionToRecord && !(actionToRecord instanceof CommandQuitRecordMacro)) { - vimState.recordedMacro.actionsRun.push(actionToRecord); - } - - vimState = await this.runAction(vimState, recordedState, action); - - if (vimState.currentMode === ModeName.Insert) { - recordedState.isInsertion = true; - } - - // Update view - await this.updateView(vimState); - - return vimState; - } - - async runAction(vimState: VimState, recordedState: RecordedState, action: BaseAction): Promise { - let ranRepeatableAction = false; - let ranAction = false; - - // If arrow keys or mouse was used prior to entering characters while in insert mode, create an undo point - // this needs to happen before any changes are made - - /* - - TODO: This causes . to crash vscodevim for some reason. - - if (!vimState.isMultiCursor) { - let prevPos = vimState.historyTracker.getLastHistoryEndPosition(); - if (prevPos !== undefined && !vimState.isRunningDotCommand) { - if (vimState.cursorPositionJustBeforeAnythingHappened[0].line !== prevPos[0].line || - vimState.cursorPositionJustBeforeAnythingHappened[0].character !== prevPos[0].character) { - vimState.globalState.previousFullAction = recordedState; - vimState.historyTracker.finishCurrentStep(); - } - } - } - */ - - if (vimState.currentMode === ModeName.Visual) { - vimState.allCursors = - vimState.allCursors.map( - x => x.start.isEarlierThan(x.stop) ? x.withNewStop(x.stop.getLeftThroughLineBreaks(true)) : x); - } - if (action instanceof BaseMovement) { - ({ vimState, recordedState } = await this.executeMovement(vimState, action)); - ranAction = true; - } - - if (action instanceof BaseCommand) { - vimState = await action.execCount(vimState.cursorPosition, vimState); - - await this.executeCommand(vimState); - - if (action.isCompleteAction) { - ranAction = true; - } - - if (action.canBeRepeatedWithDot) { - ranRepeatableAction = true; - } - } - - if (action instanceof DocumentContentChangeAction) { - vimState = await action.exec(vimState.cursorPosition, vimState); - } - - // Update mode (note the ordering allows you to go into search mode, - // then return and have the motion immediately applied to an operator). - const prevState = this.currentModeName; - if (vimState.currentMode !== this.currentModeName) { - this.setCurrentModeByName(vimState); - - // We don't want to mark any searches as a repeatable action - if (vimState.currentMode === ModeName.Normal && prevState !== ModeName.SearchInProgressMode && - vimState.currentMode !== ModeName.SearchInProgressMode) { - ranRepeatableAction = true; - } - } - - if (recordedState.operatorReadyToExecute(vimState.currentMode)) { - vimState = await this.executeOperator(vimState); - - vimState.recordedState.hasRunOperator = true; - ranRepeatableAction = vimState.recordedState.operator.canBeRepeatedWithDot; - ranAction = true; - } - - if (vimState.currentMode === ModeName.Visual) { - vimState.allCursors = - vimState.allCursors.map( - x => x.start.isEarlierThan(x.stop) ? - x.withNewStop(x.stop.isLineEnd() ? x.stop.getRightThroughLineBreaks() : x.stop.getRight()) - : x); - } - // And then we have to do it again because an operator could - // have changed it as well. (TODO: do you even decomposition bro) - - if (vimState.currentMode !== this.currentModeName) { - this.setCurrentModeByName(vimState); - - if (vimState.currentMode === ModeName.Normal) { - ranRepeatableAction = true; - } - } - - if (ranAction && vimState.currentMode !== ModeName.Insert) { - vimState.recordedState.commandList = []; - } - - ranRepeatableAction = (ranRepeatableAction && vimState.currentMode === ModeName.Normal) || this.createUndoPointForBrackets(vimState); - ranAction = ranAction && (vimState.currentMode === ModeName.Normal || vimState.currentMode === ModeName.Visual); - - // Record down previous action and flush temporary state - if (ranRepeatableAction) { - vimState.globalState.previousFullAction = vimState.recordedState; - - if (recordedState.isInsertion) { - Register.putByKey(recordedState, '.'); - } - } - - // Updated desired column - const movement = action instanceof BaseMovement ? action : undefined; - - if ((movement && !movement.doesntChangeDesiredColumn) || - (!movement && vimState.currentMode !== ModeName.VisualBlock)) { - // We check !operator here because e.g. d$ should NOT set the desired column to EOL. - - if (movement && movement.setsDesiredColumnToEOL && !recordedState.operator) { - vimState.desiredColumn = Number.POSITIVE_INFINITY; - } else { - vimState.desiredColumn = vimState.cursorPosition.character; - } - } - - if (ranAction) { - vimState.recordedState = new RecordedState(); - - // Return to insert mode after 1 command in this case for - if (vimState.returnToInsertAfterCommand) { - if (vimState.actionCount > 0) { - vimState.currentMode = ModeName.Insert; - vimState.returnToInsertAfterCommand = false; - vimState.actionCount = 0; - this.setCurrentModeByName(vimState); - } else { - vimState.actionCount++; - } - } - } - - // track undo history - if (!this.vimState.focusChanged) { - // important to ensure that focus didn't change, otherwise - // we'll grab the text of the incorrect active window and assume the - // whole document changed! - - if (this._vimState.alteredHistory) { - this._vimState.alteredHistory = false; - vimState.historyTracker.ignoreChange(); - } else { - vimState.historyTracker.addChange(this._vimState.cursorPositionJustBeforeAnythingHappened); - } - } - - // Don't record an undo point for every action of a macro, only at the very end - if (ranRepeatableAction && !vimState.isReplayingMacro) { - vimState.historyTracker.finishCurrentStep(); - } - - recordedState.actionKeys = []; - vimState.currentRegisterMode = RegisterMode.FigureItOutFromCurrentMode; - - if (this.currentModeName === ModeName.Normal) { - vimState.cursorStartPosition = vimState.cursorPosition; - } - - // Ensure cursor is within bounds - - for (const { stop, i } of Range.IterateRanges(vimState.allCursors)) { - if (stop.line >= TextEditor.getLineCount()) { - vimState.allCursors[i] = vimState.allCursors[i].withNewStop( - vimState.cursorPosition.getDocumentEnd() - ); - } - - const currentLineLength = TextEditor.getLineAt(stop).text.length; - - if (vimState.currentMode === ModeName.Normal && - stop.character >= currentLineLength && currentLineLength > 0) { - - vimState.allCursors[i] = vimState.allCursors[i].withNewStop( - stop.getLineEnd().getLeftThroughLineBreaks(true) - ); - } - } - - // Update the current history step to have the latest cursor position - - vimState.historyTracker.setLastHistoryEndPosition(vimState.allCursors.map(x => x.stop)); - - if (vimState.getModeObject(this).isVisualMode) { - // Store selection for commands like gv - this._vimState.lastVisualMode = this._vimState.currentMode; - this._vimState.lastVisualSelectionStart = this._vimState.cursorStartPosition; - this._vimState.lastVisualSelectionEnd = this._vimState.cursorPosition; - } - - - // Make sure no two cursors are at the same location. - // This is a consequence of the fact that allCursors is not a Set. - - // TODO: It should be a set. - - const resultingList: Range[] = []; - - for (const cursor of vimState.allCursors) { - let shouldAddToList = true; - - for (const alreadyAddedCursor of resultingList) { - if (cursor.equals(alreadyAddedCursor)) { - shouldAddToList = false; - break; - } - } - - if (shouldAddToList) { - resultingList.push(cursor); - } - } - - vimState.allCursors = resultingList; - - return vimState; - } - - private async executeMovement(vimState: VimState, movement: BaseMovement) - : Promise<{ vimState: VimState, recordedState: RecordedState }> { - - vimState.lastMovementFailed = false; - let recordedState = vimState.recordedState; - - for (let i = 0; i < vimState.allCursors.length; i++) { - /** - * Essentially what we're doing here is pretending like the - * current VimState only has one cursor (the cursor that we just - * iterated to). - * - * We set the cursor position to be equal to the iterated one, - * and then set it back immediately after we're done. - * - * The slightly more complicated logic here allows us to write - * Action definitions without having to think about multiple - * cursors in almost all cases. - */ - let cursorPosition = vimState.allCursors[i].stop; - const old = vimState.cursorPosition; - - vimState.cursorPosition = cursorPosition; - const result = await movement.execActionWithCount(cursorPosition, vimState, recordedState.count); - vimState.cursorPosition = old; - - if (result instanceof Position) { - vimState.allCursors[i] = vimState.allCursors[i].withNewStop(result); - - if (!vimState.getModeObject(this).isVisualMode && - !vimState.recordedState.operator) { - vimState.allCursors[i] = vimState.allCursors[i].withNewStart(result); - } - } else if (isIMovement(result)) { - if (result.failed) { - vimState.recordedState = new RecordedState(); - vimState.lastMovementFailed = true; - } - - vimState.allCursors[i] = Range.FromIMovement(result); - - if (result.registerMode) { - vimState.currentRegisterMode = result.registerMode; - } - } - - if (movement.canBeRepeatedWithSemicolon(vimState, result)) { - VimState.lastRepeatableMovement = movement; - } - } - - vimState.recordedState.count = 0; - - // Keep the cursor within bounds - if (vimState.currentMode !== ModeName.Normal || recordedState.operator) { - let stop = vimState.cursorPosition; - - // Vim does this weird thing where it allows you to select and delete - // the newline character, which it places 1 past the last character - // in the line. This is why we use > instead of >=. - - if (stop.character > Position.getLineLength(stop.line)) { - vimState.cursorPosition = stop.getLineEnd(); - } - } - - return { vimState, recordedState }; - } - - private async executeOperator(vimState: VimState): Promise { - let recordedState = vimState.recordedState; - - if (!recordedState.operator) { - throw new Error("what in god's name"); - } - - let resultVimState = vimState; - - // TODO - if actions were more pure, this would be unnecessary. - const cachedMode = this._vimState.getModeObject(this); - const cachedRegister = vimState.currentRegisterMode; - - const resultingCursors: Range[] = []; - let i = 0; - - let resultingModeName: ModeName; - let startingModeName = vimState.currentMode; - - for (let { start, stop } of vimState.allCursors) { - if (start.compareTo(stop) > 0) { - [start, stop] = [stop, start]; - } - - if (!cachedMode.isVisualMode && cachedRegister !== RegisterMode.LineWise) { - stop = stop.getLeftThroughLineBreaks(true); - } - - if (this.currentModeName === ModeName.VisualLine) { - start = start.getLineBegin(); - stop = stop.getLineEnd(); - - vimState.currentRegisterMode = RegisterMode.LineWise; - } - - recordedState.operator.multicursorIndex = i++; - - resultVimState.currentMode = startingModeName; - - // We run the repeat version of an operator if the last 2 operators are the same. - if (recordedState.operators.length > 1 - && recordedState.operators.reverse()[0].constructor === recordedState.operators.reverse()[1].constructor) { - resultVimState = await recordedState.operator.runRepeat(resultVimState, start, recordedState.count); - } else { - resultVimState = await recordedState.operator.run(resultVimState, start, stop); - } - - for (const transformation of resultVimState.recordedState.transformations) { - if (isTextTransformation(transformation) && transformation.cursorIndex === undefined) { - transformation.cursorIndex = recordedState.operator.multicursorIndex; - } - } - - resultingModeName = resultVimState.currentMode; - - let resultingRange = new Range( - resultVimState.cursorStartPosition, - resultVimState.cursorPosition - ); - - resultingCursors.push(resultingRange); - } - - if (vimState.recordedState.transformations.length > 0) { - await this.executeCommand(vimState); - } else { - // Keep track of all cursors (in the case of multi-cursor). - - resultVimState.allCursors = resultingCursors; - - const selections: vscode.Selection[] = []; - - for (const cursor of vimState.allCursors) { - selections.push(new vscode.Selection( - cursor.start, - cursor.stop, - )); - } - - this.vimState.editor.selections = selections; - } - - return resultVimState; - } - - private async executeCommand(vimState: VimState): Promise { - const transformations = vimState.recordedState.transformations; - - if (transformations.length === 0) { - return vimState; - } - - const textTransformations: TextTransformations[] = transformations.filter(x => isTextTransformation(x)) as any; - const otherTransformations = transformations.filter(x => !isTextTransformation(x)); - - let accumulatedPositionDifferences: { [key: number]: PositionDiff[] } = {}; - - if (textTransformations.length > 0) { - if (areAnyTransformationsOverlapping(textTransformations)) { - console.log( - `Text transformations are overlapping. Falling back to serial - transformations. This is generally a very bad sign. Try to make - your text transformations operate on non-overlapping ranges.`); - - // TODO: Select one transformation for every cursor and run them all - // in parallel. Repeat till there are no more transformations. - - for (const command of textTransformations) { - await this._vimState.editor.edit(edit => { - switch (command.type) { - case "insertText": - edit.insert(command.position, command.text); - break; - case "replaceText": - edit.replace(new vscode.Selection(command.end, command.start), command.text); - break; - - case "deleteText": - edit.delete(new vscode.Range(command.position, command.position.getLeftThroughLineBreaks())); - break; - - case "deleteRange": - edit.delete(new vscode.Selection(command.range.start, command.range.stop)); - break; - } - - if (command.cursorIndex === undefined) { - throw new Error("No cursor index - this should never ever happen!"); - } - - if (command.diff) { - if (!accumulatedPositionDifferences[command.cursorIndex]) { - accumulatedPositionDifferences[command.cursorIndex] = []; - } - - accumulatedPositionDifferences[command.cursorIndex].push(command.diff); - } - }); - } - } else { - // This is the common case! - - /** - * batch all text operations together as a single operation - * (this is primarily necessary for multi-cursor mode, since most - * actions will trigger at most one text operation). - */ - await this._vimState.editor.edit(edit => { - for (const command of textTransformations) { - switch (command.type) { - case "insertText": - edit.insert(command.position, command.text); - break; - - case "replaceText": - edit.replace(new vscode.Selection(command.end, command.start), command.text); - break; - - case "deleteText": - let matchRange = PairMatcher.immediateMatchingBracket(command.position); - if (matchRange) { edit.delete(matchRange); } - edit.delete(new vscode.Range(command.position, command.position.getLeftThroughLineBreaks())); - break; - - case "deleteRange": - edit.delete(new vscode.Selection(command.range.start, command.range.stop)); - break; - } - - if (command.cursorIndex === undefined) { - throw new Error("No cursor index - this should never ever happen!"); - } - - if (command.diff) { - if (!accumulatedPositionDifferences[command.cursorIndex]) { - accumulatedPositionDifferences[command.cursorIndex] = []; - } - - accumulatedPositionDifferences[command.cursorIndex].push(command.diff); - } - } - }); - } - } - - for (const command of otherTransformations) { - switch (command.type) { - case "insertTextVSCode": - await TextEditor.insert(command.text); - - vimState.cursorStartPosition = Position.FromVSCodePosition(this._vimState.editor.selection.start); - vimState.cursorPosition = Position.FromVSCodePosition(this._vimState.editor.selection.end); - break; - - case "showCommandLine": - await showCmdLine(vimState.commandInitialText, this); - break; - - case "dot": - if (!vimState.globalState.previousFullAction) { - return vimState; // TODO(bell) - } - - const clonedAction = vimState.globalState.previousFullAction.clone(); - - await this.rerunRecordedState(vimState, vimState.globalState.previousFullAction); - - vimState.globalState.previousFullAction = clonedAction; - break; - case "macro": - let recordedMacro = (await Register.getByKey(command.register)).text as RecordedState; - - vimState.isReplayingMacro = true; - - if (command.replay === "contentChange") { - vimState = await this.runMacro(vimState, recordedMacro); - } else { - let keyStrokes: string[] = []; - for (let action of recordedMacro.actionsRun) { - keyStrokes = keyStrokes.concat(action.keysPressed); - } - this.vimState.recordedState = new RecordedState(); - await this.handleMultipleKeyEvents(keyStrokes); - } - - vimState.isReplayingMacro = false; - vimState.historyTracker.lastInvokedMacro = recordedMacro; - - if (vimState.lastMovementFailed) { - // movement in last invoked macro failed then we should stop all following repeating macros. - // Besides, we should reset `lastMovementFailed`. - vimState.lastMovementFailed = false; - return vimState; - } - - break; - case "contentChange": - for (const change of command.changes) { - await TextEditor.insert(change.text); - vimState.cursorPosition = Position.FromVSCodePosition(this._vimState.editor.selection.start); - } - const newPos = vimState.cursorPosition.add(command.diff); - this._vimState.editor.selection = new vscode.Selection(newPos, newPos); - break; - case "tab": - await vscode.commands.executeCommand('tab'); - if (command.diff) { - if (command.cursorIndex === undefined) { - throw new Error("No cursor index - this should never ever happen!"); - } - - if (!accumulatedPositionDifferences[command.cursorIndex]) { - accumulatedPositionDifferences[command.cursorIndex] = []; - } - - accumulatedPositionDifferences[command.cursorIndex].push(command.diff); - } - - break; - } - } - - const selections = this._vimState.editor.selections.map(x => { - let y = Range.FromVSCodeSelection(x); - y = y.start.isEarlierThan(y.stop) ? y.withNewStop(y.stop.getLeftThroughLineBreaks(true)) : y; - return new vscode.Selection(new vscode.Position(y.start.line, y.start.character), new vscode.Position(y.stop.line, y.stop.character)); - }); - const firstTransformation = transformations[0]; - const manuallySetCursorPositions = ((firstTransformation.type === "deleteRange" || - firstTransformation.type === "replaceText" || firstTransformation.type === "insertText") - && firstTransformation.manuallySetCursorPositions); - - - // We handle multiple cursors in a different way in visual block mode, unfortunately. - // TODO - refactor that out! - if (vimState.currentMode !== ModeName.VisualBlock && - !manuallySetCursorPositions) { - vimState.allCursors = []; - - const resultingCursors: Range[] = []; - - for (let i = 0; i < selections.length; i++) { - let sel = Range.FromVSCodeSelection(selections[i]); - - let resultStart = Position.FromVSCodePosition(sel.start); - let resultEnd = Position.FromVSCodePosition(sel.stop); - - if (accumulatedPositionDifferences[i] && accumulatedPositionDifferences[i].length > 0) { - for (const diff of accumulatedPositionDifferences[i]) { - resultStart = resultStart.add(diff); - resultEnd = resultEnd .add(diff); - } - - sel = new Range( - resultStart, - resultEnd - ); - } else { - sel = new Range( - Position.FromVSCodePosition(sel.start), - Position.FromVSCodePosition(sel.stop), - ); - } - - if (vimState.recordedState.operatorPositionDiff) { - sel = sel.add(vimState.recordedState.operatorPositionDiff); - } - - resultingCursors.push(sel); - } - - vimState.recordedState.operatorPositionDiff = undefined; - - vimState.allCursors = resultingCursors; - } else { - if (accumulatedPositionDifferences[0] !== undefined) { - if (accumulatedPositionDifferences[0].length > 0) { - vimState.cursorPosition = vimState.cursorPosition.add(accumulatedPositionDifferences[0][0]); - vimState.cursorStartPosition = vimState.cursorStartPosition.add(accumulatedPositionDifferences[0][0]); - } - } - } - - /** - * This is a bit of a hack because Visual Block Mode isn't fully on board with - * the new text transformation style yet. - * - * (TODO) - */ - if (firstTransformation.type === 'deleteRange') { - if (firstTransformation.collapseRange) { - vimState.cursorPosition = new Position(vimState.cursorPosition.line, vimState.cursorStartPosition.character); - } - } - - vimState.recordedState.transformations = []; - - return vimState; - } - - async rerunRecordedState(vimState: VimState, recordedState: RecordedState): Promise { - const actions = recordedState.actionsRun.slice(0); - const hasRunSurround = recordedState.hasRunSurround; - const surroundKeys = recordedState.surroundKeys; - - vimState.isRunningDotCommand = true; - recordedState = new RecordedState(); - vimState.recordedState = recordedState; - - // Replay surround if applicable, otherwise rerun actions - if (hasRunSurround) { - await this.handleMultipleKeyEvents(surroundKeys); - } else { - let i = 0; - for (let action of actions) { - recordedState.actionsRun = actions.slice(0, ++i); - vimState = await this.runAction(vimState, recordedState, action); - - if (vimState.lastMovementFailed) { - return vimState; - } - - await this.updateView(vimState); - } - recordedState.actionsRun = actions; - } - vimState.isRunningDotCommand = false; - - return vimState; - } - - async runMacro(vimState: VimState, recordedMacro: RecordedState): Promise { - const actions = recordedMacro.actionsRun.slice(0); - let recordedState = new RecordedState(); - vimState.recordedState = recordedState; - vimState.isRunningDotCommand = true; - - for (let action of actions) { - recordedState.actionsRun.push(action); - vimState.keyHistory = vimState.keyHistory.concat(action.keysPressed); - - vimState = await this.runAction(vimState, recordedState, action); - - // We just finished a full action; let's clear out our current state. - if (vimState.recordedState.actionsRun.length === 0) { - recordedState = new RecordedState(); - vimState.recordedState = recordedState; - } - - if (vimState.lastMovementFailed) { - break; - } - - await this.updateView(vimState); - } - - vimState.isRunningDotCommand = false; - vimState.cursorPositionJustBeforeAnythingHappened = vimState.allCursors.map(x => x.stop); - return vimState; - } - - public async updateView(vimState: VimState, - args: {drawSelection: boolean, revealRange: boolean} = {drawSelection: true, revealRange: true}): Promise { - // Draw selection (or cursor) - - if (args.drawSelection) { - let selections: vscode.Selection[]; - - if (!vimState.isMultiCursor) { - let start = vimState.cursorStartPosition; - let stop = vimState.cursorPosition; - - if (vimState.currentMode === ModeName.Visual) { - /** - * Always select the letter that we started visual mode on, no matter - * if we are in front or behind it. Imagine that we started visual mode - * with some text like this: - * - * abc|def - * - * (The | represents the cursor.) If we now press w, we'll select def, - * but if we hit b we expect to select abcd, so we need to getRight() on the - * start of the selection when it precedes where we started visual mode. - */ - - if (start.compareTo(stop) > 0) { - start = start.getRightThroughLineBreaks(); - } - - selections = [ new vscode.Selection(start, stop) ]; - } else if (vimState.currentMode === ModeName.VisualLine) { - selections = [new vscode.Selection( - Position.EarlierOf(start, stop).getLineBegin(), - Position.LaterOf(start, stop).getLineEnd() - )]; - - // Maintain cursor position based on which direction the selection is going - if (start.line <= stop.line) { - vimState.cursorStartPosition = selections[0].start as Position; - vimState.cursorPosition = selections[0].end as Position; - } else { - vimState.cursorStartPosition = selections[0].end as Position; - vimState.cursorPosition = selections[0].start as Position; - } - - // Adjust the selection so that active and anchor are correct, this - // makes relative line numbers display correctly - if ((selections[0].start.line <= selections[0].end.line) && - (vimState.cursorPosition.line <= vimState.cursorStartPosition.line)) { - - selections = [new vscode.Selection(selections[0].end, selections[0].start)]; - } - - } else if (vimState.currentMode === ModeName.VisualBlock) { - selections = []; - - for (const { start: lineStart, end } of Position.IterateLine(vimState)) { - selections.push(new vscode.Selection( - lineStart, - end - )); - } - } else { - selections = [ new vscode.Selection(stop, stop) ]; - } - } else { - // MultiCursor mode is active. - - if (vimState.currentMode === ModeName.Visual) { - selections = []; - - for (let { start: cursorStart, stop: cursorStop } of vimState.allCursors) { - if (cursorStart.compareTo(cursorStop) > 0) { - cursorStart = cursorStart.getRight(); - } - - selections.push(new vscode.Selection(cursorStart, cursorStop)); - } - } else if (vimState.currentMode === ModeName.Normal || - vimState.currentMode === ModeName.Insert || - vimState.currentMode === ModeName.SearchInProgressMode) { - selections = []; - - for (const { stop: cursorStop } of vimState.allCursors) { - selections.push(new vscode.Selection(cursorStop, cursorStop)); - } - } else { - console.error("This is pretty bad!"); - - selections = []; - } - } - - this._vimState.whatILastSetTheSelectionTo = selections[0]; - if (vimState.recordedState.actionsRun.filter(x => x instanceof DocumentContentChangeAction).length === 0) { - this._vimState.editor.selections = selections; - } - } - - // Scroll to position of cursor - if (this._vimState.currentMode === ModeName.SearchInProgressMode) { - const nextMatch = vimState.globalState.searchState!.getNextSearchMatchPosition(vimState.cursorPosition).pos; - - this._vimState.editor.revealRange(new vscode.Range(nextMatch, nextMatch)); - } else { - if (args.revealRange) { - this._vimState.editor.revealRange(new vscode.Range(vimState.cursorPosition, vimState.cursorPosition)); - } - } - - let cursorRange: vscode.Range[] = []; - - // Use native cursor if possible. Default to Block. - let cursorStyle = vscode.TextEditorCursorStyle.Block; - switch (this.currentMode.cursorType) { - case VSCodeVimCursorType.Line: - cursorStyle = vscode.TextEditorCursorStyle.Line; - break; - case VSCodeVimCursorType.TextDecoration: - case VSCodeVimCursorType.LineThin: - cursorStyle = vscode.TextEditorCursorStyle.LineThin; - break; - case VSCodeVimCursorType.Underline: - cursorStyle = vscode.TextEditorCursorStyle.Underline; - break; - case VSCodeVimCursorType.Native: - cursorStyle = Configuration.userCursor; - break; - } - - let options = this._vimState.editor.options; - options.cursorStyle = cursorStyle; - this._vimState.editor.options = options; - - if (this.currentMode.cursorType === VSCodeVimCursorType.TextDecoration && - this.currentMode.name !== ModeName.Insert) { - - // Fake block cursor with text decoration. Unfortunately we can't have a cursor - // in the middle of a selection natively, which is what we need for Visual Mode. - if (this.currentModeName === ModeName.Visual) { - for (const { start: cursorStart, stop: cursorStop } of vimState.allCursors) { - if (cursorStart.isEarlierThan(cursorStop)) { - cursorRange.push(new vscode.Range(cursorStop.getLeft(), cursorStop)); - } else { - cursorRange.push(new vscode.Range(cursorStop, cursorStop.getRight())); - } - } - } else { - for (const { stop: cursorStop } of vimState.allCursors) { - cursorRange.push(new vscode.Range(cursorStop, cursorStop.getRight())); - } - } - } - - this._vimState.editor.setDecorations(this._caretDecoration, cursorRange); - - - // Draw marks - // I should re-enable this with a config setting at some point - - /* - - for (const mark of this.vimState.historyTracker.getMarks()) { - rangesToDraw.push(new vscode.Range(mark.position, mark.position.getRight())); - } - - */ - - // Draw search highlight - - let searchRanges: vscode.Range[] = []; - - if ( - (Configuration.incsearch && this.currentMode.name === ModeName.SearchInProgressMode) || - ((Configuration.hlsearch && vimState.globalState.hl) && vimState.globalState.searchState)) { - - const searchState = vimState.globalState.searchState!; - - searchRanges.push.apply(searchRanges, searchState.matchRanges); - - const { pos, match } = searchState.getNextSearchMatchPosition(vimState.cursorPosition); - - if (match) { - searchRanges.push(new vscode.Range( - pos, - pos.getRight(searchState.searchString.length))); - } - } - - this._vimState.editor.setDecorations(this._searchHighlightDecoration, searchRanges); - - for (let i = 0; i < this.vimState.postponedCodeViewChanges.length; i++) { - let viewChange = this.vimState.postponedCodeViewChanges[i]; - await vscode.commands.executeCommand(viewChange.command, viewChange.args); - vimState.allCursors = await allowVSCodeToPropagateCursorUpdatesAndReturnThem(); - } - - // If user wants to change status bar color based on mode - if (Configuration.statusBarColorControl) { - const colorToSet = Configuration.statusBarColors[this._vimState.currentModeName().toLowerCase()]; - if (colorToSet !== undefined) { - this.setStatusBarColor(colorToSet); - } - } - - this.vimState.postponedCodeViewChanges = []; - - if (this.currentMode.name === ModeName.SearchInProgressMode) { - this.setStatusBarText(`Searching for: ${this.vimState.globalState.searchState!.searchString}`); - } else if (this.currentMode.name === ModeName.EasyMotionMode) { - // Update all EasyMotion decorations - this._vimState.easyMotion.updateDecorations(); - - this.setStatusBarText(`Current depth: ${this.vimState.easyMotion.accumulation}`); - } else { - this._renderStatusBar(); - } - - vscode.commands.executeCommand('setContext', 'vim.useCtrlKeys', Configuration.useCtrlKeys); - vscode.commands.executeCommand('setContext', 'vim.overrideCopy', Configuration.overrideCopy); - vscode.commands.executeCommand('setContext', 'vim.overrideCtrlC', Configuration.overrideCopy || Configuration.useCtrlKeys); - vscode.commands.executeCommand('setContext', 'vim.platform', process.platform); - } - - private _renderStatusBar(): void { - const modeText = `-- ${this.currentMode.text.toUpperCase()} ${this._vimState.isMultiCursor ? 'MULTI CURSOR' : ''} --`; - const macroText = ` ${this._vimState.isRecordingMacro ? 'Recording @' + this._vimState.recordedMacro.registerName : ''}`; - let currentCommandText = ` ${ this._vimState.recordedState.commandString }`; - - if (this._vimState.currentMode === ModeName.Insert) { - currentCommandText = ""; - } - - if (this._vimState.currentMode === ModeName.SearchInProgressMode) { - currentCommandText = ` ${ this._vimState.globalState.searchState!.searchString }`; - } - - if (this._vimState.currentMode === ModeName.SurroundInputMode) { - if (this._vimState.surround !== undefined) { - const surroundText = this._vimState.surround.replacement; - if (surroundText !== undefined) { - currentCommandText = surroundText; - } - } - } - - this.setStatusBarText(`${ modeText }${ currentCommandText }${ macroText }`); - } - - async handleMultipleKeyEvents(keys: string[]): Promise { - for (const key of keys) { - await this.handleKeyEvent(key!); - } - } - - /** - * Set the text in the status bar on the bottom of the screen. - */ - setStatusBarText(text: string): void { - if (!ModeHandler._statusBarItem) { - ModeHandler._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); - } - - ModeHandler._statusBarItem.text = text || ''; - ModeHandler._statusBarItem.show(); - } - - setStatusBarColor(color: string): void { - vscode.workspace.getConfiguration("workbench").update("colorCustomizations", - { - "statusBar.background": `${color}`, - "statusBar.noFolderBackground": `${color}`, - "statusBar.debuggingBackground": `${color}` - }, true); - } - - // Return true if a new undo point should be created based on brackets and parenthesis - private createUndoPointForBrackets(vimState: VimState): boolean { - // }])> keys all start a new undo state when directly next to an {[(< opening character - const key = vimState.recordedState.actionKeys[vimState.recordedState.actionKeys.length - 1]; - - if (key === undefined) { - return false; - } - - if (vimState.currentMode === ModeName.Insert) { - // Check if the keypress is a closing bracket to a corresponding opening bracket right next to it - let result = PairMatcher.nextPairedChar(vimState.cursorPosition, key, false); - if (result !== undefined) { - if (vimState.cursorPosition.compareTo(result) === 0) { - return true; - } - } - - result = PairMatcher.nextPairedChar(vimState.cursorPosition.getLeft(), key, true); - if (result !== undefined) { - if (vimState.cursorPosition.getLeftByCount(2).compareTo(result) === 0) { - return true; - } - } - } - - return false; - } - - dispose() { - this._vimState.nvim.quit(); - for (const disposable of this._toBeDisposed) { - disposable.dispose(); - } - } - - // Syncs cursors between vscode representation and vim representation - syncCursors() { - if (this._vimState.editor) { - this._vimState.cursorStartPosition = Position.FromVSCodePosition(this._vimState.editor.selection.start); - this._vimState.cursorPosition = Position.FromVSCodePosition(this._vimState.editor.selection.start); - this._vimState.desiredColumn = this._vimState.cursorPosition.character; - - this._vimState.whatILastSetTheSelectionTo = this._vimState.editor.selection; - } - } -} diff --git a/src/mode/modeInsert.ts b/src/mode/modeInsert.ts deleted file mode 100644 index 408f1bbcbd7..00000000000 --- a/src/mode/modeInsert.ts +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -import { ModeName, Mode } from './mode'; -import { VSCodeVimCursorType } from './mode'; - -export class InsertMode extends Mode { - public text = "Insert Mode"; - public cursorType = VSCodeVimCursorType.Native; - - constructor() { - super(ModeName.Insert); - } -} diff --git a/src/mode/modeNormal.ts b/src/mode/modeNormal.ts deleted file mode 100644 index 852f2b0841b..00000000000 --- a/src/mode/modeNormal.ts +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; - -import { ModeName, Mode } from './mode'; -import { ModeHandler } from './modeHandler'; -import { VSCodeVimCursorType } from './mode'; - -export class NormalMode extends Mode { - private _modeHandler: ModeHandler; - - public text = "Normal Mode"; - public cursorType = VSCodeVimCursorType.Block; - - constructor(modeHandler: ModeHandler) { - super(ModeName.Normal); - - this._modeHandler = modeHandler; - } -} diff --git a/src/mode/modeReplace.ts b/src/mode/modeReplace.ts deleted file mode 100644 index 07fc2b697df..00000000000 --- a/src/mode/modeReplace.ts +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -import { ModeName, Mode } from './mode'; -import { VSCodeVimCursorType } from './mode'; - -export class ReplaceMode extends Mode { - public text = "Replace"; - public cursorType = VSCodeVimCursorType.Underline; - - constructor() { - super(ModeName.Replace); - } -} diff --git a/src/mode/modeSearchInProgress.ts b/src/mode/modeSearchInProgress.ts deleted file mode 100644 index e81f320819d..00000000000 --- a/src/mode/modeSearchInProgress.ts +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -import { ModeName, Mode } from './mode'; -import { VSCodeVimCursorType } from './mode'; - -export class SearchInProgressMode extends Mode { - public text = "Search In Progress"; - public cursorType = VSCodeVimCursorType.Block; - - constructor() { - super(ModeName.SearchInProgressMode); - } -} diff --git a/src/mode/modeVisual.ts b/src/mode/modeVisual.ts deleted file mode 100644 index 8b2edd0d18e..00000000000 --- a/src/mode/modeVisual.ts +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; - -import { ModeName, Mode } from './mode'; -import { VSCodeVimCursorType } from './mode'; - -export class VisualMode extends Mode { - public text = "Visual Mode"; - public cursorType = VSCodeVimCursorType.TextDecoration; - public isVisualMode = true; - - constructor() { - super(ModeName.Visual); - } -} diff --git a/src/mode/modeVisualBlock.ts b/src/mode/modeVisualBlock.ts deleted file mode 100644 index 93910accb0c..00000000000 --- a/src/mode/modeVisualBlock.ts +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; - -import { ModeName, Mode } from './mode'; -import { VSCodeVimCursorType } from './mode'; -import { Position } from './../common/motion/position'; - -export class VisualBlockMode extends Mode { - public text = "Visual Block Mode"; - public cursorType = VSCodeVimCursorType.TextDecoration; - public isVisualMode = true; - - constructor() { - super(ModeName.VisualBlock); - } - - public static getTopLeftPosition(start: Position, stop: Position): Position { - return new Position( - Math.min(start.line, stop.line), - Math.min(start.character, stop.character) - ); - } - - public static getBottomRightPosition(start: Position, stop: Position): Position { - return new Position( - Math.max(start.line, stop.line), - Math.max(start.character, stop.character) - ); - } -} - -export enum VisualBlockInsertionType { - /** - * Triggered with I - */ - Insert, - - /** - * Triggered with A - */ - Append, -} \ No newline at end of file diff --git a/src/mode/modeVisualLine.ts b/src/mode/modeVisualLine.ts deleted file mode 100644 index 198458fe4c1..00000000000 --- a/src/mode/modeVisualLine.ts +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; - -import { ModeName, Mode } from './mode'; -import { VSCodeVimCursorType } from './mode'; - -export class VisualLineMode extends Mode { - public text = "Visual Line Mode"; - public cursorType = VSCodeVimCursorType.Block; - public isVisualMode = true; - - constructor() { - super(ModeName.VisualLine); - } -} diff --git a/src/mode/remapper.ts b/src/mode/remapper.ts deleted file mode 100644 index b9e49a7379b..00000000000 --- a/src/mode/remapper.ts +++ /dev/null @@ -1,185 +0,0 @@ -import * as vscode from 'vscode'; -import * as _ from 'lodash'; -import { ModeName } from './mode'; -import { ModeHandler, VimState } from './modeHandler'; -import { AngleBracketNotation } from './../notation'; -import { runCmdLine } from '../../src/cmd_line/main'; - -export interface ICodeKeybinding { - after?: string[]; - commands?: { command: string; args: any[] }[]; -} - -interface IKeybinding { - before : string[]; - after? : string[]; - commands?: { command: string; args: any[] }[]; -} - -class Remapper { - private _remappings: IKeybinding[] = []; - - /** - * Modes that this Remapper is active for. - */ - private _remappedModes: ModeName[]; - private _recursive: boolean; - - /** - * Have the keys pressed so far potentially be a remap - */ - private _couldRemappingApply = false; - public get couldRemappingApply(): boolean { - return this._couldRemappingApply; - } - - constructor(configKey: string, remappedModes: ModeName[], recursive: boolean) { - this._recursive = recursive; - this._remappedModes = remappedModes; - - let remappings = vscode.workspace.getConfiguration('vim') - .get(configKey, []); - - for (let remapping of remappings) { - let before: string[] = []; - remapping.before.forEach(item => before.push(AngleBracketNotation.Normalize(item))); - - let after: string[] = []; - if (remapping.after) { - remapping.after.forEach(item => after.push(AngleBracketNotation.Normalize(item))); - } - - this._remappings.push( { - before: before, - after: after, - commands: remapping.commands, - }); - } - } - - private _longestKeySequence(): number { - if (this._remappings.length > 0) { - return _.maxBy(this._remappings, map => map.before.length).before.length; - } else { - return 1; - } - } - - public async sendKey(keys: string[], modeHandler: ModeHandler, vimState: VimState): Promise { - if (this._remappedModes.indexOf(vimState.currentMode) === -1) { - return false; - } - - const longestKeySequence = this._longestKeySequence(); - - let remapping: IKeybinding | undefined; - - /** - * Check to see if the keystrokes match any user-specified remapping. - * - * In non-Insert mode, we have to precisely match the entire keysequence, - * but in insert mode, we allow the users to precede the remapped command - * with extraneous keystrokes ("hello world jj") - */ - - if (this._remappedModes.indexOf(ModeName.Insert) === -1) { - remapping = _.find(this._remappings, map => { - return map.before.join("") === keys.join(""); - }); - } else { - for (let sliceLength = 1; sliceLength <= longestKeySequence; sliceLength++) { - const slice = keys.slice(-sliceLength); - const result = _.find(this._remappings, map => map.before.join("") === slice.join("")); - - if (result) { - remapping = result; - - break; - } - } - } - - if (remapping) { - // If we remapped e.g. jj to esc, we have to revert the inserted "jj" - - if (this._remappedModes.indexOf(ModeName.Insert) >= 0) { - // Revert every single inserted character. This is actually a bit of - // a hack since we aren't guaranteed that each insertion inserted - // only a single character. - - // We subtract 1 because we haven't actually applied the last key. - - // TODO(johnfn) - study - actions need to be paired up with text - // changes... this is a complicated problem. - - await vimState.historyTracker.undoAndRemoveChanges( - Math.max(0, (remapping.before.length - 1) * vimState.allCursors.length)); - } - - vimState.isCurrentlyPerformingRemapping = false; - - // We need to remove the keys that were remapped into different keys - // from the state. - const numToRemove = remapping.before.length - 1; - vimState.recordedState.actionKeys = vimState.recordedState.actionKeys.slice(0, -numToRemove); - vimState.keyHistory = vimState.keyHistory.slice(0, -numToRemove); - - if (remapping.after) { - const count = vimState.recordedState.count || 1; - vimState.recordedState.count = 0; - - for (let i = 0; i < count; i++) { - await modeHandler.handleMultipleKeyEvents(remapping.after); - } - } - - if (remapping.commands) { - for (const command of remapping.commands) { - // Check if this is a vim command by looking for : - if (command.command.slice(0, 1) === ":") { - await runCmdLine(command.command.slice(1, command.command.length), modeHandler); - await modeHandler.updateView(modeHandler.vimState); - } else { - await vscode.commands.executeCommand(command.command, command.args); - } - } - } - - vimState.isCurrentlyPerformingRemapping = false; - return true; - } else { - // Check to see if a remapping could potentially be applied when more keys are received - for (let remap of this._remappings) { - if (keys.join("") === remap.before.slice(0, keys.length).join("")) { - this._couldRemappingApply = true; - break; - } else { - this._couldRemappingApply = false; - } - } - } - - return false; - } -} - -export class InsertModeRemapper extends Remapper { - constructor(recursive: boolean) { - super( - "insertModeKeyBindings" + (recursive ? "" : "NonRecursive"), - [ModeName.Insert], - recursive - ); - } -} - -export class OtherModesRemapper extends Remapper { - constructor(recursive: boolean) { - super( - "otherModesKeyBindings" + (recursive ? "" : "NonRecursive"), - [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock], - recursive - ); - } -} - diff --git a/src/mode/surroundInputMode.ts b/src/mode/surroundInputMode.ts deleted file mode 100644 index 474882cbf85..00000000000 --- a/src/mode/surroundInputMode.ts +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -import { ModeName, Mode } from './mode'; -import { VSCodeVimCursorType } from './mode'; - -export class SurroundInputMode extends Mode { - public text = "Surround Input Mode"; - public cursorType = VSCodeVimCursorType.Block; - - constructor() { - super(ModeName.SurroundInputMode); - } -} diff --git a/src/neovim/nvimUtil.ts b/src/neovim/nvimUtil.ts deleted file mode 100644 index 942333766a2..00000000000 --- a/src/neovim/nvimUtil.ts +++ /dev/null @@ -1,110 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import { VimState } from "../mode/modeHandler"; -import { Position } from './../common/motion/position'; -import { TextEditor } from "../textEditor"; -import { Configuration } from '../configuration/configuration'; -import { spawn } from "child_process"; -import { attach } from "promised-neovim-client"; -import { Register, RegisterMode } from "../register/register"; -import { ModeName } from "../mode/mode"; - -export class Neovim { - - static async initNvim(vimState: VimState) { - const proc = spawn(Configuration.neovimPath, ['-u', 'NONE', '-N', '--embed'], { cwd: __dirname }); - proc.on('error', function (err) { - console.log(err); - vscode.window.showErrorMessage("Unable to setup neovim instance! Check your path."); - Configuration.enableNeovim = false; - }); - vimState.nvim = await attach(proc.stdin, proc.stdout); - } - - // Data flows from VS to Vim - static async syncVSToVim(vimState: VimState) { - const nvim = vimState.nvim; - const buf = await nvim.getCurrentBuf(); - if (Configuration.expandtab) { - await vscode.commands.executeCommand("editor.action.indentationToTabs"); - } - await buf.setLines(0, -1, true, TextEditor.getText().split('\n')); - const [rangeStart, rangeEnd] = [Position.EarlierOf(vimState.cursorPosition, vimState.cursorStartPosition), - Position.LaterOf(vimState.cursorPosition, vimState.cursorStartPosition)]; - await nvim.callFunction("setpos", [".", [0, vimState.cursorPosition.line + 1, vimState.cursorPosition.character, false]]); - await nvim.callFunction("setpos", ["'<", [0, rangeStart.line + 1, rangeEnd.character, false]]); - await nvim.callFunction("setpos", ["'>", [0, rangeEnd.line + 1, rangeEnd.character, false]]); - for (const mark of vimState.historyTracker.getMarks()) { - await nvim.callFunction("setpos", [`'${mark.name}`, [0, mark.position.line + 1, mark.position.character, false]]); - } - - const effectiveRegisterMode = (register: RegisterMode) => { - if (register === RegisterMode.FigureItOutFromCurrentMode) { - if (vimState.currentMode === ModeName.VisualLine) { - return RegisterMode.LineWise; - } else if (vimState.currentMode === ModeName.VisualBlock) { - return RegisterMode.BlockWise; - } else { - return RegisterMode.CharacterWise; - } - } else { - return register; - } - }; - - // We only copy over " register for now, due to our weird handling of macros. - let reg = await Register.get(vimState); - let vsRegTovimReg = [undefined, "c", "l", "b"]; - await nvim.callFunction("setreg", ['"', reg.text as string, vsRegTovimReg[effectiveRegisterMode(reg.registerMode)] as string]); - } - - // Data flows from Vim to VS - static async syncVimToVs(vimState: VimState) { - const nvim = vimState.nvim; - const buf = await nvim.getCurrentBuf(); - - await TextEditor.replace( - new vscode.Range(0, 0, TextEditor.getLineCount() - 1, - TextEditor.getLineMaxColumn(TextEditor.getLineCount() - 1)), - (await buf.getLines(0, -1, false)).join('\n') - ); - - let [row, character] = (await nvim.callFunction("getpos", ["."]) as Array).slice(1, 3); - vimState.editor.selection = new vscode.Selection(new Position(row - 1, character), new Position(row - 1, character)); - - if (Configuration.expandtab) { - await vscode.commands.executeCommand("editor.action.indentationToSpaces"); - } - // We're only syncing back the default register for now, due to the way we could - // be storing macros in registers. - const vimRegToVsReg = { "v": RegisterMode.CharacterWise, "V": RegisterMode.LineWise, "\x16": RegisterMode.BlockWise }; - vimState.currentRegisterMode = vimRegToVsReg[await nvim.callFunction("getregtype", ['"']) as string]; - Register.put(await nvim.callFunction("getreg", ['"']) as string, vimState); - } - - static async command(vimState: VimState, command: string) { - const nvim = vimState.nvim; - await this.syncVSToVim(vimState); - command = ":" + command + "\n"; - command = command.replace('<', ''); - - await nvim.input(command); - if ((await nvim.getMode()).blocking) { - await nvim.input(''); - } - await this.syncVimToVs(vimState); - - return; - } - - static async input(vimState: VimState, keys: string) { - const nvim = vimState.nvim; - await this.syncVSToVim(vimState); - await nvim.input(keys); - await this.syncVimToVs(vimState); - - return; - } - -} \ No newline at end of file diff --git a/src/notation.ts b/src/notation.ts deleted file mode 100644 index 251c204a984..00000000000 --- a/src/notation.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Configuration } from './configuration/configuration'; -import * as _ from "lodash"; - -export class AngleBracketNotation { - - // Mapping from the nomalized string to regex strings that could match it. - private static _notationMap : { [key: string] : string[]; } = { - 'C-': ['ctrl\\+', 'c\\-'], - 'D-': ['cmd\\+', 'd\\-'], - 'Esc': ['escape', 'esc'], - 'BS': ['backspace', 'bs'], - 'Del': ['delete', 'del'], - }; - - /** - * Normalizes key to AngleBracketNotation - * For instance, , Ctrl+x, normalized to - */ - public static Normalize(key: string): string { - if (!this.isSurroundedByAngleBrackets(key) && key.length > 1) { - key = `<${ key.toLocaleLowerCase() }>`; - } - - // Special cases that we handle incorrectly (internally) - if (key.toLowerCase() === "") { - return " "; - } - - if (key.toLowerCase() === "") { - return "\n"; - } - - if (key.toLowerCase() === "") { - return Configuration.leader; - } - - if (_.includes(["", "", "", ""], key.toLocaleLowerCase())) { - key = key.toLocaleLowerCase(); - } - - for (const notationMapKey in this._notationMap) { - if (this._notationMap.hasOwnProperty(notationMapKey)) { - const regex = new RegExp(this._notationMap[notationMapKey].join('|'), 'gi'); - if (regex.test(key)) { - key = key.replace(regex, notationMapKey); - break; - } - } - } - - return key; - } - - private static isSurroundedByAngleBrackets(key: string): boolean { - return key.startsWith('<') && key.endsWith('>'); - } -} diff --git a/src/register/register.ts b/src/register/register.ts deleted file mode 100644 index 03a9e7a90bc..00000000000 --- a/src/register/register.ts +++ /dev/null @@ -1,382 +0,0 @@ -import { VimState, RecordedState } from './../mode/modeHandler'; -import { YankOperator, BaseOperator, DeleteOperator } from './../actions/operator'; -import { CommandYankFullLine, BaseCommand, CommandRegister } from './../actions/commands/actions'; -import * as util from './../util'; - -/** - * There are two different modes of copy/paste in Vim - copy by character - * and copy by line. Copy by line typically happens in Visual Line mode, but - * also shows up in some other actions that work over lines (most noteably dd, - * yy). - */ -export enum RegisterMode { - FigureItOutFromCurrentMode, - CharacterWise, - LineWise, - BlockWise, -} - -export type RegisterContent = string | string[] | RecordedState; - -export interface IRegisterContent { - text : RegisterContent; - registerMode : RegisterMode; - isClipboardRegister: boolean; -} - -export class Register { - /** - * The '"' is the unnamed register. - * The '*' and '+' are special registers for accessing the system clipboard. - * TODO: Read-Only registers - * '.' register has the last inserted text. - * '%' register has the current file path. - * ':' is the most recently executed command. - * '#' is the name of last edited file. (low priority) - */ - private static registers: { [key: string]: IRegisterContent } = { - '"': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '.': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '*': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: true }, - '+': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: true }, - '_': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '0': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '1': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '2': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '3': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '4': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '5': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '6': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '7': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '8': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false }, - '9': { text: "", registerMode: RegisterMode.CharacterWise, isClipboardRegister: false } - }; - - public static isBlackHoleRegister(registerName: string): boolean { - return (registerName === "_"); - } - - public static isClipboardRegister(registerName: string): boolean { - const register = Register.registers[registerName]; - return register && register.isClipboardRegister; - } - - /** - * ". readonly register: last content change. - */ - public static lastContentChange: RecordedState; - - public static isValidRegister(register: string): boolean { - return register in Register.registers || - Register.isValidLowercaseRegister(register) || - Register.isValidUppercaseRegister(register); - } - - public static isValidRegisterForMacro(register: string): boolean { - return /^[a-zA-Z0-9]+$/.test(register); - } - - private static isValidLowercaseRegister(register: string): boolean { - return /^[a-z]+$/.test(register); - } - - private static isValidUppercaseRegister(register: string): boolean { - return /^[A-Z]+$/.test(register); - } - - /** - * Puts content in a register. If none is specified, uses the default - * register ". - */ - public static put(content: RegisterContent, vimState: VimState, multicursorIndex?: number): void { - const register = vimState.recordedState.registerName; - - if (!Register.isValidRegister(register)) { - throw new Error(`Invalid register ${register}`); - } - - if (Register.isBlackHoleRegister(register)) { - return; - } - - if (vimState.isMultiCursor) { - if (Register.isValidUppercaseRegister(register)) { - Register.appendMulticursorRegister(content, register, vimState, multicursorIndex as number); - } else { - Register.putMulticursorRegister(content, register, vimState, multicursorIndex as number); - } - } else { - if (Register.isValidUppercaseRegister(register)) { - Register.appendNormalRegister(content, register, vimState); - } else { - Register.putNormalRegister(content, register, vimState); - } - } - } - - /** - * Puts the content at the specified index of the multicursor Register. - * - * `REMARKS:` This procedure assumes that you pass an valid register. - */ - private static putMulticursorRegister(content: RegisterContent, register: string, vimState: VimState, multicursorIndex: number): void { - if (multicursorIndex === 0) { - Register.registers[register.toLowerCase()] = { - text : [], - registerMode : vimState.effectiveRegisterMode(), - isClipboardRegister: Register.isClipboardRegister(register), - }; - } - - let registerContent = Register.registers[register.toLowerCase()]; - - if (!Array.isArray(registerContent.text)) { - registerContent.text = []; - } - - (registerContent.text as string[]).push(content as string); - - if (multicursorIndex === vimState.allCursors.length - 1) { - if (registerContent.isClipboardRegister) { - let clipboardText: string = ""; - - for ( const line of (registerContent.text as string[])) { - clipboardText += line + "\n"; - } - clipboardText = clipboardText.replace(/\n$/, ""); - - util.clipboardCopy(clipboardText); - } - - Register.ProcessNumberedRegister(registerContent.text, vimState); - } - } - - /** - * Appends the content at the specified index of the multicursor Register. - * - * `REMARKS:` This Procedure assume that you pass an valid uppercase register. - */ - private static appendMulticursorRegister(content: RegisterContent, register: string, vimState: VimState, multicursorIndex: number): void { - let appendToRegister = Register.registers[register.toLowerCase()]; - - // Only append if appendToRegister is multicursor register - // and line count match, otherwise replace register - if (multicursorIndex === 0) { - let createEmptyRegister: boolean = false; - - if (typeof appendToRegister.text === 'string') { - createEmptyRegister = true; - } else { - if ((appendToRegister.text as string[]).length !== vimState.allCursors.length) { - createEmptyRegister = true; - } - } - - if (createEmptyRegister) { - Register.registers[register.toLowerCase()] = { - text : Array(vimState.allCursors.length).fill(''), - registerMode : vimState.effectiveRegisterMode(), - isClipboardRegister: Register.isClipboardRegister(register), - }; - - appendToRegister = Register.registers[register.toLowerCase()]; - } - } - - let currentRegisterMode = vimState.effectiveRegisterMode(); - if (appendToRegister.registerMode === RegisterMode.CharacterWise && currentRegisterMode === RegisterMode.CharacterWise) { - appendToRegister.text[multicursorIndex] += content; - } else { - appendToRegister.text[multicursorIndex] += '\n' + content; - appendToRegister.registerMode = currentRegisterMode; - } - } - - /** - * Puts the content in the specified Register. - * - * `REMARKS:` This Procedure assume that you pass an valid register. - */ - private static putNormalRegister(content: RegisterContent, register: string, vimState: VimState): void { - if (Register.isClipboardRegister(register)) { - util.clipboardCopy(content.toString()); - } - - Register.registers[register.toLowerCase()] = { - text: content, - registerMode: vimState.effectiveRegisterMode(), - isClipboardRegister: Register.isClipboardRegister(register), - }; - - Register.ProcessNumberedRegister(content, vimState); - } - - /** - * Appends the content at the specified index of the multicursor Register. - * - * `REMARKS:` This Procedure assume that you pass an valid uppercase register. - */ - private static appendNormalRegister(content: RegisterContent, register: string, vimState: VimState): void { - let appendToRegister = Register.registers[register.toLowerCase()]; - let currentRegisterMode = vimState.effectiveRegisterMode(); - - // Check if appending to a multicursor register or normal - if (appendToRegister.text instanceof Array) { - if (appendToRegister.registerMode === RegisterMode.CharacterWise && currentRegisterMode === RegisterMode.CharacterWise) { - for (let i = 0; i < appendToRegister.text.length; i++) { - appendToRegister.text[i] += content; - } - } else { - for (let i = 0; i < appendToRegister.text.length; i++) { - appendToRegister.text[i] += '\n' + content; - } - appendToRegister.registerMode = currentRegisterMode; - } - } else if (typeof appendToRegister.text === 'string') { - if (appendToRegister.registerMode === RegisterMode.CharacterWise && currentRegisterMode === RegisterMode.CharacterWise) { - appendToRegister.text = appendToRegister.text + content; - } else { - appendToRegister.text += '\n' + content; - appendToRegister.registerMode = currentRegisterMode; - } - } - } - - public static putByKey(content: RegisterContent, register = '"', registerMode = RegisterMode.FigureItOutFromCurrentMode): void { - if (!Register.isValidRegister(register)) { - throw new Error(`Invalid register ${register}`); - } - - if (Register.isClipboardRegister(register)) { - util.clipboardCopy(content.toString()); - } - - if (Register.isBlackHoleRegister(register)) { - return; - } - - Register.registers[register] = { - text : content, - registerMode : registerMode || RegisterMode.FigureItOutFromCurrentMode, - isClipboardRegister: Register.isClipboardRegister(register), - }; - } - - /** - * Handles special cases for Yank- and DeleteOperator. - */ - private static ProcessNumberedRegister(content: RegisterContent, vimState: VimState): void { - // Find the BaseOperator of the current actions - const baseOperator = vimState.recordedState.actionsRun.find( (value) => { - return value instanceof BaseOperator || - value instanceof BaseCommand; - }); - - if (baseOperator instanceof YankOperator || baseOperator instanceof CommandYankFullLine) { - // 'yank' to 0 only if no register was specified - const registerCommand = vimState.recordedState.actionsRun.find( (value) => { - return value instanceof CommandRegister; - }); - - if (!registerCommand) { - Register.registers['0'].text = content; - Register.registers['0'].registerMode = vimState.effectiveRegisterMode(); - } - } else if (baseOperator instanceof DeleteOperator && !(vimState.isRecordingMacro || vimState.isReplayingMacro)) { - // shift 'delete-history' register - for (let index = 9; index > 1; index--) { - Register.registers[String(index)].text = Register.registers[String(index - 1)].text; - Register.registers[String(index)].registerMode = Register.registers[String(index - 1)].registerMode; - } - - // Paste last delete into register '1' - Register.registers['1'].text = content; - Register.registers['1'].registerMode = vimState.effectiveRegisterMode(); - } - } - - /** - * Gets content from a register. If none is specified, uses the default - * register ". - */ - public static async get(vimState: VimState): Promise { - const register = vimState.recordedState.registerName; - return Register.getByKey(register, vimState); - } - - public static async getByKey(register: string, vimState?: VimState): Promise { - if (!Register.isValidRegister(register)) { - throw new Error(`Invalid register ${register}`); - } - - let lowercaseRegister = register.toLowerCase(); - - // Clipboard registers are always defined, so if a register doesn't already - // exist we can be sure it's not a clipboard one - if (!Register.registers[lowercaseRegister]) { - Register.registers[lowercaseRegister] = { - text : "", - registerMode : RegisterMode.CharacterWise, - isClipboardRegister: false - }; - } - - /* Read from system clipboard */ - if (Register.isClipboardRegister(register)) { - let text = util.clipboardPaste(); - - // Harmonize newline character - text = text.replace(/\r\n/g, '\n'); - - let registerText: string | string[]; - if (vimState && vimState.isMultiCursor) { - registerText = text.split('\n'); - if (registerText.length !== vimState.allCursors.length) { - registerText = text; - } - } else { - registerText = text; - } - - Register.registers[lowercaseRegister].text = registerText; - return Register.registers[register]; - } else { - let text = Register.registers[lowercaseRegister].text; - - let registerText: RegisterContent; - if (text instanceof RecordedState) { - registerText = text; - } else { - if (vimState && vimState.isMultiCursor && (typeof text === 'object')) { - if ((text as string[]).length === vimState.allCursors.length) { - registerText = text; - } else { - registerText = (text as string[]).join('\n'); - } - } else { - if (typeof text === 'object') { - registerText = (text as string[]).join('\n'); - } else { - registerText = text; - } - } - } - - return { - text : registerText, - registerMode : Register.registers[lowercaseRegister].registerMode, - isClipboardRegister: Register.registers[lowercaseRegister].isClipboardRegister - }; - } - } - - public static has(register: string): boolean { - return Register.registers[register] !== undefined; - } - - public static getKeys(): string[] { - return Object.keys(Register.registers); - } -} - diff --git a/src/state/globalState.ts b/src/state/globalState.ts deleted file mode 100644 index 28bc38beafa..00000000000 --- a/src/state/globalState.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { SearchState } from './searchState'; -import { RecordedState } from '../mode/modeHandler'; - -/** - * State which stores global state (across editors) - */ -export class GlobalState { - /** - * The keystroke sequence that made up our last complete action (that can be - * repeated with '.'). - */ - private static _previousFullAction: RecordedState | undefined = undefined; - - /** - * Previous searches performed - */ - private static _searchStatePrevious: SearchState[] = []; - - /** - * Last search state for running n and N commands - */ - private static _searchState: SearchState | undefined = undefined; - - /** - * Index used for navigating search history with and when searching - */ - private static _searchStateIndex: number = 0; - - /** - * Used internally for nohl. - */ - private static _hl = true; - - /** - * Getters and setters for changing global state - */ - public get searchStatePrevious(): SearchState[]{ - return GlobalState._searchStatePrevious; - } - - public set searchStatePrevious(states: SearchState[]) { - GlobalState._searchStatePrevious = GlobalState._searchStatePrevious.concat(states); - } - - public get previousFullAction(): RecordedState | undefined { - return GlobalState._previousFullAction; - } - - public set previousFullAction(state : RecordedState | undefined) { - GlobalState._previousFullAction = state; - } - - public get searchState(): SearchState | undefined { - return GlobalState._searchState; - } - - public set searchState(state : SearchState | undefined) { - GlobalState._searchState = state; - } - - public get searchStateIndex(): number { - return GlobalState._searchStateIndex; - } - - public set searchStateIndex(state : number) { - GlobalState._searchStateIndex = state; - } - - public get hl(): boolean { - return GlobalState._hl; - } - - public set hl(enabled: boolean) { - GlobalState._hl = enabled; - } -} diff --git a/src/state/replaceState.ts b/src/state/replaceState.ts deleted file mode 100644 index 47a171dd530..00000000000 --- a/src/state/replaceState.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Position } from './../common/motion/position'; -import { TextEditor } from './../textEditor'; - -/** - * State involved with entering Replace mode (R). - */ -export class ReplaceState { - /** - * The location of the cursor where you begun to replace characters. - */ - public replaceCursorStartPosition: Position; - - public originalChars: string[] = []; - - /** - * The characters the user inserted in replace mode. Useful for when - * we repeat a replace action with . - */ - public newChars: string[] = []; - - /** - * Number of times we're going to repeat this replace action. - */ - public timesToRepeat: number; - - constructor(startPosition: Position, timesToRepeat: number = 1) { - this.replaceCursorStartPosition = startPosition; - this.timesToRepeat = timesToRepeat; - - let text = TextEditor.getLineAt(startPosition).text.substring(startPosition.character); - for (let [key, value] of text.split("").entries()) { - this.originalChars[key + startPosition.character] = value; - } - } -} diff --git a/src/state/searchState.ts b/src/state/searchState.ts deleted file mode 100644 index 8d329565319..00000000000 --- a/src/state/searchState.ts +++ /dev/null @@ -1,206 +0,0 @@ -import * as vscode from 'vscode'; -import { Position } from './../common/motion/position'; -import { TextEditor } from './../textEditor'; -import { Configuration } from '../../src/configuration/configuration'; -import { ModeName } from './../mode/mode'; - -export enum SearchDirection { - Forward = 1, - Backward = -1 -} - -/** - * State involved with beginning a search (/). - */ -export class SearchState { - private static readonly MAX_SEARCH_RANGES = 1000; - private static specialCharactersRegex: RegExp = /[\-\[\]{}()*+?.,\\\^$|#\s]/g; - public previousMode = ModeName.Normal; - - private _matchRanges: vscode.Range[] = []; - - /** - * Every range in the document that matches the search string. - */ - public get matchRanges(): vscode.Range[] { - return this._matchRanges; - } - - private _searchCursorStartPosition: Position; - public get searchCursorStartPosition(): Position { - return this._searchCursorStartPosition; - } - - private _cachedDocumentVersion: number; - private _cachedDocumentName: String; - private _searchDirection: SearchDirection = SearchDirection.Forward; - private isRegex: boolean; - - private _searchString = ""; - public get searchString(): string { - return this._searchString; - } - - public set searchString(search: string){ - if (this._searchString !== search) { - this._searchString = search; - this._recalculateSearchRanges({ forceRecalc: true }); - } - } - - private _recalculateSearchRanges({ forceRecalc }: { forceRecalc?: boolean } = {}): void { - const search = this.searchString; - if (search === "") { return; } - - // checking if the tab that is worked on has changed, or the file version has changed - const shouldRecalculate = (this._cachedDocumentName !== TextEditor.getDocumentName()) || - (this._cachedDocumentVersion !== TextEditor.getDocumentVersion()) || forceRecalc; - - if (shouldRecalculate) { - // Calculate and store all matching ranges - this._cachedDocumentVersion = TextEditor.getDocumentVersion(); - this._cachedDocumentName = TextEditor.getDocumentName(); - this._matchRanges = []; - - /* - * Decide whether the search is case sensitive. - * If ignorecase is false, the search is case sensitive. - * If ignorecase is true, the search should be case insensitive. - * If both ignorecase and smartcase are true, the search is case sensitive only when the search string contains UpperCase character. - */ - let ignorecase = Configuration.ignorecase; - - if (ignorecase && Configuration.smartcase && /[A-Z]/.test(search)) { - ignorecase = false; - } - - let searchRE = search; - if (!this.isRegex) { - searchRE = search.replace(SearchState.specialCharactersRegex, "\\$&"); - } - - const regexFlags = ignorecase ? 'gi' : 'g'; - - let regex: RegExp; - try { - regex = new RegExp(searchRE, regexFlags); - } catch (err) { - // Couldn't compile the regexp, try again with special characters escaped - searchRE = search.replace(SearchState.specialCharactersRegex, "\\$&"); - regex = new RegExp(searchRE, regexFlags); - } - // We store the entire text file as a string inside text, and run the - // regex against it many times to find all of our matches. In order to - // transform from the absolute position in the string to a Position - // object, we store a prefix sum of the line lengths, and binary search - // through it in order to find the current line and character. - const finalPos = new Position(TextEditor.getLineCount() - 1, 0).getLineEndIncludingEOL(); - const text = TextEditor.getText(new vscode.Range(new Position(0 , 0), finalPos)); - const lineLengths = text.split("\n").map(x => x.length + 1); - let sumLineLengths = []; - let curLength = 0; - for (const length of lineLengths){ - sumLineLengths.push(curLength); - curLength += length; - } - const absPosToPosition = (val: number, l: number, r: number, arr: Array): Position => { - const mid = Math.floor((l + r) / 2); - if (l === r - 1) { - return new Position(l, val - arr[mid]); - } - if (arr[mid] > val) { - return absPosToPosition(val, l, mid, arr); - } else { - return absPosToPosition(val, mid, r, arr); - } - }; - const selection = vscode.window.activeTextEditor!.selection; - const startPos = sumLineLengths[Math.min(selection.start.line, selection.end.line)] + selection.active.character; - regex.lastIndex = startPos; - let result = regex.exec(text); - let wrappedOver = false; - - do { - // We need to wrap around to the back if we reach the end. - if (!result && !wrappedOver) { - regex.lastIndex = 0; - wrappedOver = true; - result = regex.exec(text); - } - if (!result) { - break; - } - if (this._matchRanges.length >= SearchState.MAX_SEARCH_RANGES) { - break; - } - - this.matchRanges.push(new vscode.Range( - absPosToPosition(result.index, 0, sumLineLengths.length, sumLineLengths), - absPosToPosition(result.index + result[0].length, 0, sumLineLengths.length, sumLineLengths) - )); - - if (result.index === regex.lastIndex) { - regex.lastIndex++; - } - result = regex.exec(text); - if (!result && !wrappedOver) { - regex.lastIndex = 0; - wrappedOver = true; - result = regex.exec(text); - } - } while (result && !(wrappedOver && result!.index > startPos)); - - this._matchRanges.sort((x, y) => - (x.start.line < y.start.line) || - (x.start.line === y.start.line && x.start.character < y.start.character) ? -1 : 1); - } - } - - /** - * The position of the next search, or undefined if there is no match. - * - * Pass in -1 as direction to reverse the direction we search. - */ - public getNextSearchMatchPosition(startPosition: Position, direction = 1): { pos: Position, match: boolean } { - this._recalculateSearchRanges(); - - if (this._matchRanges.length === 0) { - // TODO(bell) - return { pos: startPosition, match: false }; - } - - const effectiveDirection = direction * this._searchDirection; - - if (effectiveDirection === SearchDirection.Forward) { - for (let matchRange of this._matchRanges) { - if (matchRange.start.compareTo(startPosition) > 0) { - return { pos: Position.FromVSCodePosition(matchRange.start), match: true }; - } - } - - // Wrap around - // TODO(bell) - return { pos: Position.FromVSCodePosition(this._matchRanges[0].start), match: true }; - } else { - for (let matchRange of this._matchRanges.slice(0).reverse()) { - if (matchRange.start.compareTo(startPosition) < 0) { - return { pos: Position.FromVSCodePosition(matchRange.start), match: true }; - } - } - - // TODO(bell) - return { - pos: Position.FromVSCodePosition(this._matchRanges[this._matchRanges.length - 1].start), - match: true - }; - } - } - - constructor(direction: SearchDirection, startPosition: Position, searchString = "", { isRegex = false } = {}, currentMode: ModeName) { - this._searchDirection = direction; - this._searchCursorStartPosition = startPosition; - this.isRegex = isRegex; - this.searchString = searchString; - this.previousMode = currentMode; - } -} \ No newline at end of file diff --git a/src/taskQueue.ts b/src/taskQueue.ts deleted file mode 100644 index 922712a6d1d..00000000000 --- a/src/taskQueue.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as _ from "lodash"; - -export interface IEnqueuedTask { - promise : () => Promise; - isRunning: boolean; - queue?: string; - highPriority?: boolean; -} - -/** - * TaskQueue - * - * Enqueue promises here. They will be run sequentially. - */ -class TaskQueue { - private _taskQueue: { - [key: string]: { - tasks: IEnqueuedTask[]; - highPriorityCount: number; - } - } = {}; - - private async _runTasks(queueName: string): Promise { - while (this._taskQueue[queueName].tasks.length > 0) { - let task: IEnqueuedTask = this._taskQueue[queueName].tasks[0]; - - try { - task.isRunning = true; - await task.promise(); - task.isRunning = false; - } catch (e) { - console.log(e); - console.log(e.stack); - } finally { - this.removeTask(task); - - if (task.highPriority) { - this._taskQueue[queueName].highPriorityCount--; - } - } - } - } - - public get tasks(): number { - let result = 0; - - for (const list in this._taskQueue) { - if (this._taskQueue.hasOwnProperty(list)) { - result += this._taskQueue[list].tasks.length; - } - } - - return result; - } - - /** - * Removes a task from the task queue. - * - * (Keep in mind that if the task is already running, the semantics of - * promises don't allow you to stop it.) - */ - public removeTask(task: IEnqueuedTask): void { - let queueName = task.queue || "default"; - this._taskQueue[queueName].tasks.splice(_.findIndex(this._taskQueue[queueName].tasks, t => t === task), 1); - } - - /** - * Adds a task to the task queue. - */ - public enqueueTask(task: IEnqueuedTask): void { - let queueName = task.queue || "default"; - let otherTaskRunning = this._taskQueue[queueName] && _.filter(this._taskQueue[queueName].tasks, x => x.isRunning).length > 0; - - if (this._taskQueue[queueName]) { - if (task.highPriority) { - // Insert task as the last high priotity task. - - const numHighPriority = this._taskQueue[queueName].highPriorityCount; - - this._taskQueue[queueName].tasks.splice(numHighPriority, 0, task); - this._taskQueue[queueName].highPriorityCount++; - } else { - this._taskQueue[queueName].tasks.push(task); - } - } else { - this._taskQueue[queueName] = { - tasks: [task], - highPriorityCount: 0, - }; - } - - if (!otherTaskRunning) { - this._runTasks(queueName); - } - } -} - -export let taskQueue = new TaskQueue(); \ No newline at end of file diff --git a/src/textEditor.ts b/src/textEditor.ts index 19db609b24f..8f827d36187 100644 --- a/src/textEditor.ts +++ b/src/textEditor.ts @@ -1,10 +1,7 @@ -"use strict"; -import { ReplaceTextTransformation } from './transformations/transformations'; -import { VimState } from './mode/modeHandler'; +'use strict'; import * as vscode from 'vscode'; import { Position, PositionDiff } from './common/motion/position'; -import { Configuration } from './configuration/configuration'; import { Globals } from './globals'; export class TextEditor { @@ -14,8 +11,11 @@ export class TextEditor { * Do not use this method! It has been deprecated. Use InsertTextTransformation * (or possibly InsertTextVSCodeTransformation) instead. */ - static async insert(text: string, at: Position | undefined = undefined, - letVSCodeHandleKeystrokes: boolean | undefined = undefined): Promise { + static async insert( + text: string, + at: Position | undefined = undefined, + letVSCodeHandleKeystrokes: boolean | undefined = undefined + ): Promise { // If we insert "blah(" with default:type, VSCode will insert the closing ). // We *probably* don't want that to happen if we're inserting a lot of text. if (letVSCodeHandleKeystrokes === undefined) { @@ -66,10 +66,10 @@ export class TextEditor { * Removes all text in the entire document. */ static async deleteDocument(): Promise { - const start = new vscode.Position(0, 0); + const start = new vscode.Position(0, 0); const lastLine = vscode.window.activeTextEditor!.document.lineCount - 1; - const end = vscode.window.activeTextEditor!.document.lineAt(lastLine).range.end; - const range = new vscode.Range(start, end); + const end = vscode.window.activeTextEditor!.document.lineAt(lastLine).range.end; + const range = new vscode.Range(start, end); return vscode.window.activeTextEditor!.edit(editBuilder => { editBuilder.delete(range); @@ -86,24 +86,6 @@ export class TextEditor { }); } - /** - * This is the correct replace method to use. (Notice how it's not async? Yep) - */ - static replaceText(vimState: VimState, text: string, start: Position, end: Position, - diff: PositionDiff | undefined = undefined): void { - - const trans: ReplaceTextTransformation = { - type: "replaceText", - text, - start, - end, - }; - - if (diff) { trans.diff = diff; } - - vimState.recordedState.transformations.push(trans); - } - static readLine(): string { const lineNo = vscode.window.activeTextEditor!.selection.active.line; @@ -156,7 +138,7 @@ export class TextEditor { * Retrieves the current word at position. * If current position is whitespace, selects the right-closest word */ - static getWord(position: Position) : string | undefined { + static getWord(position: Position): string | undefined { let start = position; let end = position.getRight(); @@ -177,81 +159,21 @@ export class TextEditor { return word; } - static isFirstLine(position : vscode.Position): boolean { + static isFirstLine(position: vscode.Position): boolean { return position.line === 0; } - static isLastLine(position : vscode.Position): boolean { - return position.line === (vscode.window.activeTextEditor!.document.lineCount - 1); - } - - static getIndentationLevel(line: string): number { - let tabSize = Configuration.tabstop; - - let firstNonWhiteSpace = 0; - let checkLine = line.match(/^\s*/); - if (checkLine) { - firstNonWhiteSpace = checkLine[0].length; - } - - let visibleColumn: number = 0; - - if (firstNonWhiteSpace >= 0) { - for (const char of line.substring(0, firstNonWhiteSpace)) { - switch (char) { - case '\t': - visibleColumn += tabSize; - break; - case ' ': - visibleColumn += 1; - break; - default: - break; - } - } - } else { - return -1; - } - - return visibleColumn; - } - - static setIndentationLevel(line: string, screenCharacters: number): string { - let tabSize = Configuration.tabstop; - let insertTabAsSpaces = Configuration.expandtab; - - if (screenCharacters < 0) { - screenCharacters = 0; - } - - let indentString = ""; - - if (insertTabAsSpaces) { - indentString += new Array(screenCharacters + 1).join(" "); - } else { - if (screenCharacters / tabSize > 0) { - indentString += new Array(Math.floor(screenCharacters / tabSize) + 1).join("\t"); - } - - indentString += new Array(screenCharacters % tabSize + 1).join(" "); - } - - let firstNonWhiteSpace = 0; - let lineCheck = line.match(/^\s*/); - if (lineCheck) { - firstNonWhiteSpace = lineCheck[0].length; - } - - return indentString + line.substring(firstNonWhiteSpace, line.length); + static isLastLine(position: vscode.Position): boolean { + return position.line === vscode.window.activeTextEditor!.document.lineCount - 1; } - static getPositionAt(offset: number) : Position { + static getPositionAt(offset: number): Position { const pos = vscode.window.activeTextEditor!.document.positionAt(offset); return new Position(pos.line, pos.character); } - static getOffsetAt(position: Position) : number { + static getOffsetAt(position: Position): number { return vscode.window.activeTextEditor!.document.offsetAt(position); } } @@ -273,10 +195,20 @@ export type RevealLineAtArgument = 'top' | 'center' | 'bottom'; /** * Positions in the view for cursor move command. */ -export type CursorMovePosition = 'left' | 'right' | 'up' | 'down' | - 'wrappedLineStart' |'wrappedLineFirstNonWhitespaceCharacter' | - 'wrappedLineColumnCenter' | 'wrappedLineEnd' | 'wrappedLineLastNonWhitespaceCharacter' | - 'viewPortTop' | 'viewPortCenter' | 'viewPortBottom' | 'viewPortIfOutside'; +export type CursorMovePosition = + | 'left' + | 'right' + | 'up' + | 'down' + | 'wrappedLineStart' + | 'wrappedLineFirstNonWhitespaceCharacter' + | 'wrappedLineColumnCenter' + | 'wrappedLineEnd' + | 'wrappedLineLastNonWhitespaceCharacter' + | 'viewPortTop' + | 'viewPortCenter' + | 'viewPortBottom' + | 'viewPortIfOutside'; /** * Units for Cursor move 'by' argument diff --git a/src/transformations/transformations.ts b/src/transformations/transformations.ts deleted file mode 100644 index f1aebf8c5dc..00000000000 --- a/src/transformations/transformations.ts +++ /dev/null @@ -1,309 +0,0 @@ -import { Position, PositionDiff } from "./../common/motion/position"; -import { Range } from "./../common/motion/range"; -import * as vscode from 'vscode'; - -/** - * This file contains definitions of objects that represent text - * additions/deletions/replacements on the document. You'll add them - * to vimState.recordedState.transformations and then they will be applied - * later on. - * - * We do it in this way so they can all be processed in parallel and merged - * if necessary. - */ - -/** - * Represents inserting text at a position in the document. - */ -export interface InsertTextTransformation { - /** - * Type of this insertion (used for type checking with discriminated - * union types). - */ - type : "insertText"; - - /** - * Text content of this insertion. - */ - text : string; - - /** - * The location to insert the text. - */ - position: Position; - - /** - * The index of the cursor that this transformation applies to. - */ - cursorIndex?: number; - - /** - * A position diff that will be added to the position of the cursor after - * the replace transformation has been applied. - * - * If you don't know what this is, just ignore it. You probably don't need it. - */ - diff?: PositionDiff; - - - manuallySetCursorPositions?: boolean; -} - -export interface ReplaceTextTransformation { - type: "replaceText"; - - /** - * Text to insert. - */ - text: string; - - /** - * Start of location to replace. - */ - start: Position; - - /** - * End of location to replace. - */ - end: Position; - - /** - * The index of the cursor that this transformation applies to. - */ - cursorIndex?: number; - - /** - * A position diff that will be added to the position of the cursor after - * the replace transformation has been applied. - * - * If you don't know what this is, just ignore it. You probably don't need it. - */ - diff?: PositionDiff; - - /** - * Please don't use this! It's a hack. - */ - manuallySetCursorPositions?: boolean; -} - -/** - * Represents inserting a character and allowing visual studio to do - * its post-character stuff if it wants. (e.g., if you type "(" this - * will automatically add the closing ")"). - */ -export interface InsertTextVSCodeTransformation { - type : "insertTextVSCode"; - - /** - * Text to insert. - */ - text : string; - - /** - * The index of the cursor that this transformation applies to. - */ - cursorIndex?: number; - - /** - * A position diff that will be added to the position of the cursor after - * the replace transformation has been applied. - * - * If you don't know what this is, just ignore it. You probably don't need it. - */ - diff?: PositionDiff; -} - -/** - * Represents deleting a character at a position in the document. - */ -export interface DeleteTextTransformation { - type : "deleteText"; - - /** - * Position at which to delete a character. - */ - position : Position; - - /** - * The index of the cursor that this transformation applies to. - */ - cursorIndex?: number; - - /** - * A position diff that will be added to the position of the cursor after - * the replace transformation has been applied. - * - * If you don't know what this is, just ignore it. You probably don't need it. - */ - diff?: PositionDiff; -} - - -/** - * Represents deleting a range of characters. - */ -export interface DeleteTextRangeTransformation { - type : "deleteRange"; - - /** - * Range of characters to delete. - */ - range: Range; - - /** - * A position diff that will be added to the position of the cursor after - * the replace transformation has been applied. - * - * If you don't know what this is, just ignore it. You probably don't need it. - */ - diff?: PositionDiff; - - collapseRange?: boolean; - - /** - * The index of the cursor that this transformation applies to. - */ - cursorIndex?: number; - - /** - * Please don't use this! It's a hack. - */ - manuallySetCursorPositions?: boolean; -} - -export interface MoveCursorTransformation { - type: "moveCursor"; - - cursorIndex?: number; - - /** - * Move the cursor this much. - */ - diff: PositionDiff; -} - -/** - * Represents pressing ':' - */ -export interface ShowCommandLine { - type: "showCommandLine"; -} - -/** - * Represents pressing '.' - */ -export interface Dot { - type: "dot"; -} - -/** - * Represents Tab - */ -export interface Tab { - type: "tab"; - cursorIndex?: number; - - /** - * Move the cursor this much. - */ - diff?: PositionDiff; -} - -/** - * Represents macro - */ -export interface Macro { - type: "macro"; - register: string; - replay: "contentChange" | "keystrokes"; -} - -/** - * Represents updating document content changes - */ -export interface ContentChangeTransformation { - type: "contentChange"; - changes: vscode.TextDocumentContentChangeEvent[]; - diff: PositionDiff; -} - -export type Transformation - = InsertTextTransformation - | InsertTextVSCodeTransformation - | ReplaceTextTransformation - | DeleteTextRangeTransformation - | DeleteTextTransformation - | MoveCursorTransformation - | ShowCommandLine - | Dot - | Macro - | ContentChangeTransformation - | DeleteTextTransformation - | Tab; - -/** - * Text Transformations - * - * Using these indicates that you want Visual Studio Code to execute your text - * actions as a batch operation. It's a bit tricky because we defer cursor updating - * behavior to whatever the batch operation returns, so if you update the cursor in your - * Action, VSCode will override whatever you did. - * - * If your cursor isn't ending up in the right place, you can adjust it by passing along - * a PositionDiff. - * - * (There are a LOT of weird edge cases with cursor behavior that we don't want to have to reimplement. Trust - * me... I tried.) - */ -export type TextTransformations - = InsertTextTransformation - | InsertTextVSCodeTransformation - | DeleteTextRangeTransformation - | MoveCursorTransformation - | DeleteTextTransformation - | ReplaceTextTransformation; - -export const isTextTransformation = (x: Transformation): x is TextTransformations => { - return x.type === 'insertText' || - x.type === 'replaceText' || - x.type === 'deleteText' || - x.type === 'moveCursor' || - x.type === 'deleteRange'; -}; - -const getRangeFromTextTransformation = (transformation: TextTransformations): Range | undefined => { - switch (transformation.type) { - case 'insertText': - return new Range(transformation.position, transformation.position); - case 'replaceText': - return new Range(transformation.start, transformation.end); - case 'deleteText': - return new Range(transformation.position, transformation.position); - case 'deleteRange': - return transformation.range; - case 'moveCursor': - return undefined; - } - - throw new Error("This should never happen!"); -}; - -export const areAnyTransformationsOverlapping = (transformations: TextTransformations[]) => { - for (let i = 0; i < transformations.length; i++) { - for (let j = i + 1; j < transformations.length; j++) { - const first = transformations[i]; - const second = transformations[j]; - - const firstRange = getRangeFromTextTransformation(first); - const secondRange = getRangeFromTextTransformation(second); - - if (!firstRange || !secondRange) { continue; } - - if (firstRange.overlaps(secondRange)) { - return true; - } - } - } - - return false; -}; \ No newline at end of file diff --git a/srcNV/nvUtil.ts b/srcNV/nvUtil.ts new file mode 100644 index 00000000000..fd040eabf9a --- /dev/null +++ b/srcNV/nvUtil.ts @@ -0,0 +1,230 @@ +'use strict'; + +import * as vscode from 'vscode'; +import { Vim } from '../extension'; +import { TextEditor } from '../src/textEditor'; +import { Position } from '../src/common/motion/position'; +import { Configuration } from '../src/configuration/configuration'; + +type UndoTree = { + entries: Array<{ seq: number; time: number }>; + save_cur: number; + save_last: number; + seq_cur: number; + seq_last: number; + synced: number; + time_cur: number; +}; + +export class NvUtil { + private static _caretDecoration = vscode.window.createTextEditorDecorationType({ + dark: { + // used for dark colored themes + backgroundColor: 'rgba(240, 240, 240, 0.6)', + borderColor: 'rgba(0, 0, 0, 1.0)', + }, + light: { + // used for light colored themes + backgroundColor: 'rgba(32, 32, 32, 0.6)', + borderColor: 'rgba(0, 0, 0, 1.0)', + }, + borderStyle: 'solid', + borderWidth: '1px', + }); + + static async copyTextFromNeovim() { + Vim.numVimChangesToApply++; + let lines = await Vim.nv.buffer.lines; + TextEditor.replace( + new vscode.Range( + 0, + 0, + TextEditor.getLineCount() - 1, + TextEditor.getLineMaxColumn(TextEditor.getLineCount() - 1) + ), + lines.join('\n') + ); + } + + static async setCursorPos(pos: vscode.Position) { + await Vim.nv.call('setpos', ['.', [0, pos.line + 1, pos.character + 1, false]]); + } + + // Must be moving to same line + static async ctrlGMove(start: number, target: number) { + if (start < target) { + // todo(chilli): Causes race condition that seems very tricky to fix :/ + // await Vim.nv.input('U'.repeat(target - start)); + } else if (start > target) { + await Vim.nv.input('U'.repeat(start - target)); + } + } + static atomCall(funcName: string, args?: any[]): Array { + if (args) { + return ['nvim_call_function', [funcName, args]]; + } else { + return ['nvim_call_function', [funcName, []]]; + } + } + + static atomCommand(command: string): Array { + return ['nvim_command', [command]]; + } + + static atomBufSetLines( + lines: Array, + buffer = 0, + start = 0, + end = -1, + strictIndexing = 1 + ) { + return ['nvim_buf_set_lines', [buffer, start, end, strictIndexing, lines]]; + } + + static atomFeedKeys(keys: string, mode = '', escapeCsi = false) { + return ['nvim_feed_keys', [keys, mode, escapeCsi]]; + } + + // An utility function for joining multiple arrays for use in nvim_atomic_call + static atomJoin(...arrays: Array): Array { + let ret: Array = []; + for (const a of arrays) { + if (a[0] instanceof Array) { + ret.concat(a); + } else { + ret = ret.concat([a]); + } + } + return ret; + } + + static async setSelection(pos: vscode.Range) { + await Vim.nv.callAtomic( + NvUtil.atomJoin( + NvUtil.atomCall('setpos', ['.', [0, pos.start.line + 1, pos.start.character + 1, false]]), + NvUtil.atomFeedKeys('v'), + NvUtil.atomCall('setpos', ['.', [0, pos.end.line + 1, pos.end.character + 1, false]]) + ) + ); + } + + private static async getPos(name: string): Promise { + let [row, character] = ((await Vim.nv.callFunction('getpos', [name])) as Array).slice( + 1, + 3 + ); + return new Position(row - 1, character - 1); + } + + static async getCurWant(): Promise { + return (await Vim.nv.call('getcurpos'))[4] - 1; + } + + static async getCursorPos(): Promise { + return this.getPos('.'); + } + + static async getSelectionStartPos(): Promise { + return this.getPos('v'); + } + static async getUndoTree(): Promise { + return (await Vim.nv.call('undotree', [])) as UndoTree; + } + static async changeSelectionFromMode(mode: string) { + return this.changeSelectionFromModeSync( + mode, + await this.getCursorPos(), + await this.getSelectionStartPos(), + await this.getCurWant() + ); + } + + static changeSelectionFromModeSync( + mode: string, + curPos: Position, + startPos: Position, + curWant: number + ) { + const cursorPos = new Position(curPos.line, curPos.character); + let cursorDecorations = []; + switch (mode) { + case 'v': + if (startPos.isBeforeOrEqual(curPos)) { + curPos = curPos.getRightThroughLineBreaks(); + } else { + startPos = startPos.getRightThroughLineBreaks(); + } + vscode.window.activeTextEditor!.options.cursorStyle = vscode.TextEditorCursorStyle.LineThin; + vscode.window.activeTextEditor!.selection = new vscode.Selection(startPos, curPos); + break; + case 'V': + if (startPos.isBeforeOrEqual(curPos)) { + curPos = curPos.getLineEndIncludingEOL(); + startPos = startPos.getLineBegin(); + } else { + curPos = curPos.getLineBegin(); + startPos = startPos.getLineEndIncludingEOL(); + } + vscode.window.activeTextEditor!.options.cursorStyle = vscode.TextEditorCursorStyle.LineThin; + vscode.window.activeTextEditor!.selection = new vscode.Selection(startPos, curPos); + break; + case '\x16': + const top = Position.EarlierOf(curPos, startPos).line; + const bottom = Position.LaterOf(curPos, startPos).line; + const left = Math.min(startPos.character, curWant); + const right = Math.max(startPos.character, curWant) + 1; + let selections = []; + for (let line = top; line <= bottom; line++) { + selections.push( + new vscode.Selection(new Position(line, left), new Position(line, right)) + ); + } + vscode.window.activeTextEditor!.selections = selections; + vscode.window.activeTextEditor!.options.cursorStyle = vscode.TextEditorCursorStyle.LineThin; + + break; + case 'i': + vscode.window.activeTextEditor!.options.cursorStyle = Configuration.userCursor; + vscode.window.activeTextEditor!.selection = new vscode.Selection(curPos, curPos); + break; + case 'R': + vscode.window.activeTextEditor!.options.cursorStyle = + vscode.TextEditorCursorStyle.Underline; + vscode.window.activeTextEditor!.selection = new vscode.Selection(curPos, curPos); + break; + case 'n': + default: + vscode.window.activeTextEditor!.options.cursorStyle = vscode.TextEditorCursorStyle.Block; + vscode.window.activeTextEditor!.selection = new vscode.Selection(curPos, curPos); + break; + } + + switch (mode) { + case 'v': + if (startPos.isEarlierThan(curPos)) { + cursorDecorations.push(new vscode.Range(curPos.getLeft(), curPos)); + } else { + cursorDecorations.push(new vscode.Range(curPos, curPos.getRight())); + } + break; + case 'V': + cursorDecorations.push(new vscode.Range(cursorPos, cursorPos.getRight())); + break; + case '\x16': + cursorDecorations.push(new vscode.Range(curPos, curPos.getRight())); + break; + default: + break; + } + vscode.window.activeTextEditor!.setDecorations(this._caretDecoration, cursorDecorations); + vscode.window.activeTextEditor!.revealRange(new vscode.Range(cursorPos, cursorPos)); + } + + static async updateMode() { + Vim.mode = await Vim.nv.mode; + } + + static async setSettings(arg: Array) { + Vim.nv.command(`set ${arg.join(' ')}`); + } +} diff --git a/srcNV/rpcHandlers.ts b/srcNV/rpcHandlers.ts new file mode 100644 index 00000000000..337204107e5 --- /dev/null +++ b/srcNV/rpcHandlers.ts @@ -0,0 +1,58 @@ +import * as vscode from 'vscode'; +import { Vim } from '../extension'; +import { NvUtil } from './nvUtil'; +import { VimSettings } from './vimSettings'; +import * as fs from 'fs'; + +export class RpcRequest { + static rpcFunctions: { [method: string]: Function } = {}; + + static async enterBuf(args: any, resp: any) { + const filePath = args[1] as string; + const fileURI = vscode.Uri.file(filePath); + console.log(filePath); + if (fs.existsSync(filePath) && fs.lstatSync(filePath).isFile()) { + await vscode.window.showTextDocument(await vscode.workspace.openTextDocument(filePath)); + await NvUtil.changeSelectionFromMode(Vim.mode.mode); + } else { + console.log('Opening non-existing files currently not implemented (and not working well).'); + // await vscode.window.showTextDocument(t); + // console.log(t); + } + resp.send('success'); + } + + static async newTabEntered(_: any, resp: any) { + await Vim.nv.command('tabonly'); + await resp.send('success'); + } + + static async writeBuf(args: Array, resp: any) { + const filePath = vscode.Uri.file(args[1]); + await vscode.commands.executeCommand('workbench.action.files.save', filePath); + // nvim.command('e!'); + await resp.send('success'); + } + + static async closeBuf(args: Array, resp: any) { + const bufName = args[1]; + const filePath = vscode.Uri.file(bufName); + console.log('filepath: ', filePath); + if (args[1] !== vscode.window.activeTextEditor!.document.fileName) { + await vscode.commands.executeCommand('vscode.open', filePath); + } + if (args[1] !== vscode.window.activeTextEditor!.document.fileName) { + resp.send('failure'); + return; + } + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + resp.send('success'); + } + + static async goToDefinition(args: Array, resp: any) { + await Vim.nv.command("normal! m'"); + await vscode.commands.executeCommand('editor.action.goToDeclaration'); + await NvUtil.setCursorPos(vscode.window.activeTextEditor!.selection.active); + resp.send('success'); + } +} diff --git a/srcNV/screen.ts b/srcNV/screen.ts new file mode 100644 index 00000000000..0d9535845e1 --- /dev/null +++ b/srcNV/screen.ts @@ -0,0 +1,374 @@ +import * as vscode from 'vscode'; +import { Position } from '../src/common/motion/position'; +import { TextEditor } from '../src/textEditor'; +import { VimSettings } from './vimSettings'; +import { NvUtil } from './nvUtil'; +import { Vim } from '../extension'; +export class Cell { + v: string; + highlight: any; + constructor(v: string) { + this.v = v; + this.highlight = {}; + } +} +interface ScreenSize { + width: number; + height: number; +} + +export interface IgnoredKeys { + all: string[]; + normal: string[]; + insert: string[]; + visual: string[]; +} +export interface HighlightGroup { + vimColor: number; + decorator?: vscode.TextEditorDecorationType; +} + +export class Screen { + term: Array> = []; + cursX: number; + cursY: number; + size: ScreenSize; + highlighter: any; + cmdline: vscode.StatusBarItem; + wildmenu: vscode.StatusBarItem[]; + wildmenuItems: string[]; + highlightGroups: { + IncSearch: HighlightGroup; + Search: HighlightGroup; + }; + scrollRegion: { + top: number; + bottom: number; + left: number; + right: number; + }; + resize(size: ScreenSize) { + this.size = size; + for (let i = 0; i < this.size.height; i++) { + this.term[i] = []; + for (let j = 0; j < this.size.width; j++) { + this.term[i][j] = new Cell(' '); + } + } + + this.scrollRegion = { + top: 0, + bottom: this.size.height, + left: 0, + right: this.size.width, + }; + } + + clear() { + this.resize(this.size); + } + + scroll(deltaY: number) { + const { top, bottom, left, right } = this.scrollRegion; + + const width = right - left; + const height = bottom - top; + + let yi = [top, bottom]; + if (deltaY < 0) { + yi = [bottom, top - 1]; + } + + for (let y = yi[0]; y !== yi[1]; y = y + Math.sign(deltaY)) { + if (top <= y + deltaY && y + deltaY < bottom) { + for (let x = left; x < right; x++) { + this.term[y][x] = this.term[y + deltaY][x]; + } + } else { + for (let x = left; x < right; x++) { + this.term[y][x] = new Cell(' '); // (right - left); + } + } + } + } + + constructor(size: { width: number; height: number }) { + this.size = size; + this.resize(this.size); + + this.cursX = 0; + this.cursY = 0; + this.highlighter = {}; + this.cmdline = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10001); + this.wildmenu = []; + for (let i = 0; i < 10; i++) { + this.wildmenu.push( + vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10000 - i) + ); + // this.wildmenu[i].show(); + } + // todo(chilli): Offer some way of binding these from the client side. + let hlGroups = { + IncSearch: { + vimColor: 1, + decorator: vscode.window.createTextEditorDecorationType({ + backgroundColor: new vscode.ThemeColor('editor.findMatchBackground'), + }), + }, + Search: { + vimColor: 2, + decorator: vscode.window.createTextEditorDecorationType({ + backgroundColor: new vscode.ThemeColor('editor.findMatchHighlightBackground'), + }), + }, + multiple_cursors_visual: { + vimColor: 3, + decorator: vscode.window.createTextEditorDecorationType({ + backgroundColor: new vscode.ThemeColor('editor.selectionBackground'), + }), + }, + multiple_cursors_cursor: { + vimColor: 4, + decorator: vscode.window.createTextEditorDecorationType({ + backgroundColor: new vscode.ThemeColor('editorCursor.foreground'), + }), + }, + EasyMotionTarget: { + vimColor: 5, + decorator: vscode.window.createTextEditorDecorationType({ + backgroundColor: 'black', + textDecoration: 'none;color: red', + }), + }, + EasyMotionShade: { + vimColor: 6, + decorator: vscode.window.createTextEditorDecorationType({ + textDecoration: 'none;opacity: 0.3', + }), + }, + }; + for (const hlGroup of Object.keys(hlGroups)) { + Vim.nv.command(`highlight ${hlGroup} guibg='#00000${hlGroups[hlGroup].vimColor}'`); + } + this.highlightGroups = hlGroups; + } + private async handleModeChange(mode: [string, number]) { + if (mode[0] === 'insert') { + await NvUtil.setSettings(await VimSettings.insertModeSettings()); + } else { + await NvUtil.updateMode(); + await NvUtil.copyTextFromNeovim(); + await NvUtil.changeSelectionFromMode(Vim.mode.mode); + await NvUtil.setSettings(VimSettings.normalModeSettings); + } + // todo(chilli): Do this in a smarter way that generalizes to more categories ... + const ignoreKeys: IgnoredKeys = vscode.workspace + .getConfiguration('vim') + .get('ignoreKeys') as IgnoredKeys; + if (mode[0] === 'insert') { + for (const key of ignoreKeys.visual.concat(ignoreKeys.normal)) { + vscode.commands.executeCommand('setContext', `vim.use_${key}`, true); + } + for (const key of ignoreKeys.insert) { + vscode.commands.executeCommand('setContext', `vim.use_${key}`, false); + } + } else if (mode[0] === 'visual') { + for (const key of ignoreKeys.normal.concat(ignoreKeys.insert)) { + vscode.commands.executeCommand('setContext', `vim.use_${key}`, true); + } + for (const key of ignoreKeys.visual) { + vscode.commands.executeCommand('setContext', `vim.use_${key}`, false); + } + } else { + // I assume normal is just all "other" modes. + for (const key of ignoreKeys.visual.concat(ignoreKeys.insert)) { + vscode.commands.executeCommand('setContext', `vim.use_${key}`, true); + } + for (const key of ignoreKeys.normal) { + vscode.commands.executeCommand('setContext', `vim.use_${key}`, false); + } + } + for (const key of ignoreKeys.all) { + vscode.commands.executeCommand('setContext', `vim.use_${key}`, false); + } + } + + async redraw(changes: Array) { + let highlightsChanged = false; + for (let change of changes) { + change = change as Array; + const name = change[0]; + const args = change.slice(1); + if (name === 'cursor_goto') { + this.cursY = args[0][0]; + this.cursX = args[0][1]; + } else if (name === 'eol_clear') { + for (let i = 0; i < this.size.width - this.cursX; i++) { + this.term[this.cursY][this.cursX + i].v = ' '; + this.term[this.cursY][this.cursX + i].highlight = {}; + } + highlightsChanged = true; + } else if (name === 'put') { + for (const cs of args) { + for (const c of cs) { + this.term[this.cursY][this.cursX].v = c; + this.term[this.cursY][this.cursX].highlight = this.highlighter; + this.cursX += 1; + } + } + highlightsChanged = true; + } else if (name === 'highlight_set') { + this.highlighter = args[args.length - 1][0]; + } else if (name === 'mode_change') { + this.handleModeChange(args[0]); + } else if (name === 'set_scroll_region') { + this.scrollRegion = { + top: args[0][0], + bottom: args[0][1], + left: args[0][2], + right: args[0][3], + }; + } else if (name === 'resize') { + this.resize({ width: args[0][0], height: args[0][1] }); + } else if (name === 'scroll') { + this.scroll(args[0][0]); + } else if (name === 'cmdline_show') { + let text = ''; + for (let hlText of args[0][0]) { + text += hlText[1]; + } + this.cmdline.text = + args[0][2] + + args[0][3] + + ' '.repeat(args[0][4]) + + text.slice(0, args[0][1]) + + '|' + + text.slice(args[0][1]); + this.cmdline.text += ' '.repeat(30 - this.cmdline.text.length % 30); + this.cmdline.show(); + } else if (name === 'cmdline_hide') { + this.cmdline.hide(); + } else if ( + [ + 'cmdline_pos', + 'cmdline_special_char', + 'cmdline_block_show', + 'cmdline_block_append', + 'cmdline_block_hide', + ].indexOf(name) !== -1 + ) { + // console.log(name); + // console.log(args); + } else if (name === 'wildmenu_show') { + this.wildmenuItems = args[0][0]; + } else if (name === 'wildmenu_hide') { + for (const i of this.wildmenu) { + i.hide(); + } + } else if (name === 'wildmenu_select') { + // There's logic in here to "batch" wildmenu items into groups of 5 each. + const selectIndex = args[0][0]; + const NUM_ITEMS_TO_SHOW = 5; + const startIndex = selectIndex - selectIndex % NUM_ITEMS_TO_SHOW; + const endIndex = selectIndex + 5 - selectIndex % NUM_ITEMS_TO_SHOW; + let offset = startIndex > 0 ? 1 : 0; + if (offset) { + this.wildmenu[0].text = '<'; + } + for (let i = 0; i < NUM_ITEMS_TO_SHOW; i++) { + this.wildmenu[i + offset].text = this.wildmenuItems[startIndex + i]; + if (startIndex + i === selectIndex) { + this.wildmenu[i + offset].color = new vscode.ThemeColor( + 'statusBarItem.prominentBackground' + ); + } else { + this.wildmenu[i + offset].color = undefined; + } + this.wildmenu[i + offset].show(); + } + if (endIndex < this.wildmenuItems.length - 1) { + this.wildmenu[offset + NUM_ITEMS_TO_SHOW].text = '>'; + this.wildmenu[offset + NUM_ITEMS_TO_SHOW].show(); + } + for (let i = offset + NUM_ITEMS_TO_SHOW + 1; i < this.wildmenu.length; i++) { + this.wildmenu[i].hide(); + } + } else { + // console.log(name); + // console.log(args); + } + } + + // If nvim is connected to a TUI, then we can't get external ui for cmdline/wildmenu. + if (Vim.DEBUG) { + this.cmdline.text = this.term[this.size.height - 1].map(x => x.v).join(''); + this.cmdline.show(); + const wildmenuText = this.term[this.size.height - 2] + .map(x => x.v) + .join('') + .replace(/\s+$/, ''); + let wildmenu: string[] = wildmenuText.split(/\s+/); + // Doesn't always work, who cares??? What a pain in the ass. I don't want to not use regex. + let wildmenuIdx = wildmenu.map(x => wildmenuText.indexOf(x)); + if (wildmenu[0] === '<' || wildmenu[wildmenu.length - 1] === '>') { + for (let i = 0; i < wildmenu.length; i++) { + this.wildmenu[i].text = wildmenu[i]; + this.wildmenu[i].show(); + if ( + this.term[this.size.height - 2][wildmenuIdx[i]].highlight.hasOwnProperty('foreground') + ) { + this.wildmenu[i].color = 'red'; + } else { + this.wildmenu[i].color = 'white'; + } + } + for (let i = wildmenu.length; i < this.wildmenu.length; i++) { + this.wildmenu[i].hide(); + } + } else { + for (let i = 0; i < this.wildmenu.length; i++) { + this.wildmenu[i].hide(); + } + } + } + + if (!vscode.workspace.getConfiguration('vim').get('enableHighlights') || !highlightsChanged) { + return; + } + let curPos = await NvUtil.getCursorPos(); + let yOffset = curPos.line - ((await Vim.nv.call('winline')) - 1); + let xOffset = curPos.character - ((await Vim.nv.call('wincol')) - 1); + for (const hlGroup of Object.keys(this.highlightGroups)) { + const group = this.highlightGroups[hlGroup]; + if (group.decorator === undefined) { + continue; + } + let decorations: vscode.Range[] = []; + let result = ''; + for (let i = 0; i < this.size.height; i++) { + let isRange = false; + let start = 0; + for (let j = 0; j < this.size.width; j++) { + result += this.term[i][j].v; + if (!isRange && this.term[i][j].highlight.background === group.vimColor) { + start = j; + isRange = true; + } else if (isRange && !(this.term[i][j].highlight.background === group.vimColor)) { + isRange = false; + decorations.push( + new vscode.Range( + new vscode.Position(i + yOffset, start + xOffset), + new vscode.Position(i + yOffset, j + xOffset) + ) + ); + } + } + result += '\n'; + } + + if (vscode.window.activeTextEditor) { + vscode.window.activeTextEditor!.setDecorations(group.decorator, decorations); + } + } + } +} diff --git a/srcNV/vimSettings.ts b/srcNV/vimSettings.ts new file mode 100644 index 00000000000..ee849faf2c6 --- /dev/null +++ b/srcNV/vimSettings.ts @@ -0,0 +1,33 @@ +import * as vscode from 'vscode'; +import { Vim } from '../extension'; + +export class VimSettings { + static indentexpr: string = ''; + static get normalModeSettings() { + return [ + 'autoindent', + 'cindent', + 'smartindent', + `indentexpr=${this.indentexpr}`, + `shiftwidth=${vscode.window.activeTextEditor!.options.tabSize}`, + ]; + } + static async insertModeSettings() { + return ['noautoindent', 'nocindent', 'nosmartindent', 'indentexpr=', 'shiftwidth=1']; + } + + static async enterFileSettings() { + let result: string[] = []; + const currentFileSettings = vscode.window.activeTextEditor!.options; + if (currentFileSettings.insertSpaces) { + result.push('expandtab'); + } + this.indentexpr = await (await Vim.nv.buffer).getOption('indentexpr'); + + result = result.concat([ + `tabstop=${currentFileSettings.tabSize}`, + `shiftwidth=${currentFileSettings.tabSize}`, + ]); + return result; + } +} diff --git a/srcNV/vscHandlers.ts b/srcNV/vscHandlers.ts new file mode 100644 index 00000000000..86e78ac599b --- /dev/null +++ b/srcNV/vscHandlers.ts @@ -0,0 +1,164 @@ +import { NvUtil } from './nvUtil'; +import { Vim } from '../extension'; +import { Position } from '../src/common/motion/position'; +import * as vscode from 'vscode'; +import { TextEditor } from '../src/textEditor'; +import { VimSettings } from './vimSettings'; + +export class VscHandlers { + // tslint:disable-next-line:no-unused-variable + static async handleSimple(key: string) { + await Vim.nv.input(key); + } + + static async handleKeyEventNV(key: string) { + const prevMode = Vim.mode.mode; + const prevBlocking = Vim.mode.blocking; + async function input(k: string) { + await Vim.nv.input(k === '<' ? '' : k); + await NvUtil.updateMode(); + if (Vim.mode.mode === 'r') { + await Vim.nv.input(''); + } + // Optimization that makes movement very smooth. However, occasionally + // makes it more difficult to debug so it's turned off for now. + // const curTick = await Vim.nv.buffer.changedtick; + // if (curTick === Vim.prevState.bufferTick) { + // await NvUtil.changeSelectionFromMode(Vim.mode.mode); + // return; + // } + // Vim.prevState.bufferTick = curTick; + + const curPos = await NvUtil.getCursorPos(); + const startPos = await NvUtil.getSelectionStartPos(); + const curWant = await NvUtil.getCurWant(); + NvUtil.changeSelectionFromModeSync(Vim.mode.mode, curPos, startPos, curWant); + await NvUtil.copyTextFromNeovim(); + NvUtil.changeSelectionFromModeSync(Vim.mode.mode, curPos, startPos, curWant); + } + if (prevMode !== 'i') { + await input(key); + } else { + if (key.length > 1) { + await input(key); + } else { + await vscode.commands.executeCommand('default:type', { text: key }); + } + } + + await vscode.commands.executeCommand('setContext', 'vim.mode', Vim.mode.mode); + } + + static async handleTextDocumentChange(e: vscode.TextDocumentChangeEvent) { + if (e.contentChanges.length === 0) { + return; + } + const change = e.contentChanges[0]; + const changeBegin = Position.FromVSCodePosition(change.range.start); + const changeEnd = Position.FromVSCodePosition(change.range.end); + const curPos = Position.FromVSCodePosition(vscode.window.activeTextEditor!.selection.active); + const curSel = vscode.window.activeTextEditor!.selection; + const docEnd = new Position(0, 0).getDocumentEnd(); + // This ugly if statement is to differentiate the "real" vscode changes that + // should be included in dot repeat(like autocomplete, auto-close parens, + // all regular typing, etc.) from the vscode changes that should not be + // included (the entire buffer syncing, autoformatting, etc.) + + const isInsertModeChange = () => { + if (e.contentChanges.length > 1 || vscode.window.activeTextEditor!.selections.length > 1) { + return false; + } + if (Vim.mode.mode !== 'i') { + return false; + } + // Handles the case where we press backsapce at the beginning of a line. + if (change.text === '' && changeEnd.character === 0 && change.rangeLength === 1) { + return true; + } + // If the change is spanning multiple lines then it's almost definitely + // not an insert mode change (except for a couple of special cases.) + if (!(changeBegin.line === curPos.line && changeBegin.line === changeEnd.line)) { + return false; + } + // Mainly for mouse cursor selection/multicursor stuff. + if ( + curSel.active.line !== curSel.anchor.line || + curSel.active.character !== curSel.anchor.character + ) { + return false; + } + // Tries to handle the case about editing on the first line. + if (changeBegin.line === 0 && changeBegin.character === 0 && change.rangeLength !== 0) { + if (change.text[change.text.length - 1] === '\n') { + return false; + } else if (TextEditor.getLineCount() === 1) { + return false; + } + } + return true; + }; + await NvUtil.updateMode(); + if (isInsertModeChange()) { + if (!Vim.mode.blocking) { + const nvPos = await NvUtil.getCursorPos(); + if (nvPos.line !== curPos.line) { + await NvUtil.setCursorPos(curPos); + } else { + // Is necessary for parentheses autocompletion but causes issues + // when non-atomic with fast text. + await NvUtil.ctrlGMove(nvPos.character, changeEnd.character); + } + } + await Vim.nv.input(''.repeat(change.rangeLength)); + await Vim.nv.input(change.text.replace('<', '')); + } else { + // Should handle race conditions. If we have more than one Vim copy to + // VSCode that we haven't processed, then we don't copy back to neovim. + // NVM. This doesn't actually work as is. + Vim.numVimChangesToApply--; + if (Vim.numVimChangesToApply !== 0) { + return; + } + // todo: Optimize this to only replace relevant lines. Probably not worth + // doing until diffs come in from the neovim side though, since that's the + // real blocking factor. + // todo(chilli): Tests if change is a change that replaces the entire text (ie: the copy + // from neovim buffer to vscode buffer). It's a hack. Won't work if your + // change (refactor) for example, doesn't modify the length of the file + const isRealChange = change.text.length !== change.rangeLength; + if (isRealChange || true) { + // todo(chilli): Doesn't work if there was just an undo command (undojoin + // fails and prevents the following command from executing) + + const startTime = new Date().getTime(); + const newPos = vscode.window.activeTextEditor!.selection.active; + let t = await Vim.nv.lua('return _vscode_copy_text(...)', [ + TextEditor.getText().split('\n'), + newPos.line + 1, + newPos.character + 1, + ]); + console.log(`timeTaken: ${new Date().getTime() - startTime}`); + // const newPos = vscode.window.activeTextEditor!.selection.active; + // await nvim.command('undojoin'); + // await nvim.buffer.setLines(TextEditor.getText().split('\n'), { + // start: 0, + // end: -1, + // strictIndexing: false, + // }); + // await NvUtil.setCursorPos(newPos); + } + } + } + + static async handleActiveTextEditorChange() { + if (vscode.window.activeTextEditor === undefined) { + return; + } + const active_editor_file = vscode.window.activeTextEditor!.document.fileName; + await Vim.nv.command(`edit! ${active_editor_file}`); + await NvUtil.copyTextFromNeovim(); + await NvUtil.setCursorPos(vscode.window.activeTextEditor!.selection.active); + await NvUtil.setSettings(await VimSettings.enterFileSettings()); + await NvUtil.changeSelectionFromMode(Vim.mode.mode); + } +} diff --git a/test/cmd_line/lexer.test.ts b/test/cmd_line/lexer.test.ts deleted file mode 100644 index be6804cd2f7..00000000000 --- a/test/cmd_line/lexer.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -"use strict"; - -// The module 'assert' provides assertion methods from node -import * as assert from 'assert'; -import * as lexer from '../../src/cmd_line/lexer'; -import {Token, TokenType} from '../../src/cmd_line/token'; - -suite("command-line lexer", () => { - - test("can lex empty string", () => { - var tokens = lexer.lex(""); - assert.equal(tokens.length, 0); - }); - - test("can lex comma", () => { - var tokens = lexer.lex(","); - assert.equal(tokens[0].content, new Token(TokenType.Comma, ',').content); - }); - - test("can lex percent", () => { - var tokens = lexer.lex("%"); - assert.equal(tokens[0].content, new Token(TokenType.Percent, '%').content); - }); - - test("can lex dollar", () => { - var tokens = lexer.lex("$"); - assert.equal(tokens[0].content, new Token(TokenType.Dollar, '$').content); - }); - - test("can lex dot", () => { - var tokens = lexer.lex("."); - assert.equal(tokens[0].content, new Token(TokenType.Dot, '.').content); - }); - - test("can lex one number", () => { - var tokens = lexer.lex("1"); - assert.equal(tokens[0].content, new Token(TokenType.LineNumber, "1").content); - }); - - test("can lex longer number", () => { - var tokens = lexer.lex("100"); - assert.equal(tokens[0].content, new Token(TokenType.LineNumber, "100").content); - }); - - test("can lex plus", () => { - var tokens = lexer.lex("+"); - assert.equal(tokens[0].content, new Token(TokenType.Plus, '+').content); - }); - - test("can lex minus", () => { - var tokens = lexer.lex("-"); - assert.equal(tokens[0].content, new Token(TokenType.Minus, '-').content); - }); - - test("can lex forward search", () => { - var tokens = lexer.lex("/horses/"); - assert.equal(tokens[0].content, new Token(TokenType.ForwardSearch, "horses").content); - }); - - test("can lex forward search escaping", () => { - var tokens = lexer.lex("/hor\\/ses/"); - assert.equal(tokens[0].content, new Token(TokenType.ForwardSearch, "hor/ses").content); - }); - - test("can lex reverse search", () => { - var tokens = lexer.lex("?worms?"); - assert.equal(tokens[0].content, new Token(TokenType.ReverseSearch, "worms").content); - }); - - test("can lex reverse search escaping", () => { - var tokens = lexer.lex("?wor\\?ms?"); - assert.equal(tokens[0].content, new Token(TokenType.ReverseSearch, "wor?ms").content); - }); - - test("can lex command name", () => { - var tokens = lexer.lex("w"); - assert.equal(tokens[0].content, new Token(TokenType.CommandName, "w").content); - }); - - test("can lex command args", () => { - var tokens = lexer.lex("w something"); - assert.equal(tokens[0].content, new Token(TokenType.CommandName, "w").content); - assert.equal(tokens[1].content, new Token(TokenType.CommandArgs, " something").content); - }); - - test("can lex command args with leading whitespace", () => { - var tokens = lexer.lex("q something"); - assert.equal(tokens[0].content, new Token(TokenType.CommandName, "q").content); - assert.equal(tokens[1].content, new Token(TokenType.CommandArgs, " something").content); - }); - - test("can lex long command name and args", () => { - var tokens = lexer.lex("write12 something here"); - assert.equal(tokens[0].content, new Token(TokenType.CommandName, "write").content); - assert.equal(tokens[1].content, new Token(TokenType.CommandArgs, "12 something here").content); - }); - - test("can lex left and right line refs", () => { - var tokens = lexer.lex("20,30"); - assert.equal(tokens[0].content, new Token(TokenType.LineNumber, "20").content); - assert.equal(tokens[1].content, new Token(TokenType.LineNumber, ",").content); - assert.equal(tokens[2].content, new Token(TokenType.LineNumber, "30").content); - }); -}); diff --git a/test/cmd_line/parser.test.ts b/test/cmd_line/parser.test.ts deleted file mode 100644 index 865cc28dac8..00000000000 --- a/test/cmd_line/parser.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import * as parser from '../../src/cmd_line/parser'; -import * as node from '../../src/cmd_line/node'; -import * as token from '../../src/cmd_line/token'; - -suite("command-line parser", () => { - - test("can parse empty string", () => { - var cmd = parser.parse(""); - assert.ok(cmd.isEmpty); - }); - - test("can parse left - dot", () => { - var cmd : node.CommandLine = parser.parse("."); - assert.equal(cmd.range.left[0].type, token.TokenType.Dot); - }); - - test("can parse left - dollar", () => { - var cmd : node.CommandLine = parser.parse("$"); - assert.equal(cmd.range.left[0].type, token.TokenType.Dollar); - }); - - test("can parse left - percent", () => { - var cmd : node.CommandLine = parser.parse("%"); - assert.equal(cmd.range.left[0].type, token.TokenType.Percent); - }); - - test("can parse separator - comma", () => { - var cmd : node.CommandLine = parser.parse(","); - assert.equal(cmd.range.separator.type, token.TokenType.Comma); - }); - - test("can parse right - dollar", () => { - var cmd : node.CommandLine = parser.parse(",$"); - assert.equal(cmd.range.left.length, 0); - assert.equal(cmd.range.right.length, 1); - assert.equal(cmd.range.right[0].type, token.TokenType.Dollar, "unexpected token"); - }); -}); diff --git a/test/cmd_line/read.test.ts b/test/cmd_line/read.test.ts deleted file mode 100644 index edd2539dfa0..00000000000 --- a/test/cmd_line/read.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; - -import { ModeHandler } from '../../src/mode/modeHandler'; -import { setupWorkspace, cleanUpWorkspace, assertEqualLines } from './../testUtils'; -import { runCmdLine } from '../../src/cmd_line/main'; -import { getAndUpdateModeHandler } from "../../extension"; -import { TextEditor } from "../../src/textEditor"; -import { Configuration } from "../../src/configuration/configuration"; - -suite("read", () => { - let modeHandler: ModeHandler; - - suiteSetup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - suiteTeardown(cleanUpWorkspace); - - test("Can read shell command output", async () => { - await runCmdLine('r! echo hey', modeHandler); - assertEqualLines([ - '', - 'hey', - ]); - }); - -}); diff --git a/test/cmd_line/scanner.test.ts b/test/cmd_line/scanner.test.ts deleted file mode 100644 index 61784461de7..00000000000 --- a/test/cmd_line/scanner.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import {Scanner} from '../../src/cmd_line/scanner'; - -suite("command line scanner", () => { - - test("ctor", () => { - var state = new Scanner("dog"); - assert.equal(state.input, "dog"); - }); - - test("can detect EOF with empty input", () => { - var state = new Scanner(""); - assert.ok(state.isAtEof); - }); - - test("next() returns EOF at EOF", () => { - var state = new Scanner(""); - assert.equal(state.next(), Scanner.EOF); - assert.equal(state.next(), Scanner.EOF); - assert.equal(state.next(), Scanner.EOF); - }); - - test("can scan", () => { - var state = new Scanner("dog"); - assert.equal(state.next(), "d"); - assert.equal(state.next(), "o"); - assert.equal(state.next(), "g"); - assert.equal(state.next(), Scanner.EOF); - }); - - test("can emit", () => { - var state = new Scanner("dog cat"); - state.next(); - state.next(); - state.next(); - assert.equal(state.emit(), "dog"); - state.next(); - state.next(); - state.next(); - state.next(); - assert.equal(state.emit(), " cat"); - }); - - test("can ignore", () => { - - var state = new Scanner("dog cat"); - state.next(); - state.next(); - state.next(); - state.next(); - state.ignore(); - state.next(); - state.next(); - state.next(); - assert.equal(state.emit(), "cat"); - }); - - test("can skip whitespace", () => { - var state = new Scanner("dog cat"); - state.next(); - state.next(); - state.next(); - state.ignore(); - state.skipWhiteSpace(); - assert.equal(state.next(), "c"); - }); - - test("can skip whitespace with one char before EOF", () => { - var state = new Scanner("dog c"); - state.next(); - state.next(); - state.next(); - state.ignore(); - state.skipWhiteSpace(); - assert.equal(state.next(), "c"); - }); - - test("can skip whitespace at EOF", () => { - var state = new Scanner("dog "); - state.next(); - state.next(); - state.next(); - state.ignore(); - state.skipWhiteSpace(); - assert.equal(state.next(), Scanner.EOF); - }); - - test("nextWord() return EOF at EOF", () => { - var state = new Scanner(""); - assert.equal(state.nextWord(), Scanner.EOF); - assert.equal(state.nextWord(), Scanner.EOF); - assert.equal(state.nextWord(), Scanner.EOF); - }); - - test("nextWord() return word before trailing spaces", () => { - var state = new Scanner("dog cat"); - assert.equal(state.nextWord(), "dog"); - }); - - test("nextWord() can skip whitespaces and return word ", () => { - var state = new Scanner(" dog cat"); - assert.equal(state.nextWord(), "dog"); - }); - - test("nextWord() return word before EOF", () => { - var state = new Scanner("dog cat"); - state.nextWord(); - assert.equal(state.nextWord(), "cat"); - }); - - test("can expect one of a set", () => { - var state = new Scanner("dog cat"); - state.expectOneOf(["dog", "mule", "monkey"]); - }); - - test("can expect only one of a set", () => { - var state = new Scanner("dog cat"); - assert.throws(() => state.expectOneOf(["mule", "monkey"])); - }); -}); diff --git a/test/cmd_line/sort.test.ts b/test/cmd_line/sort.test.ts deleted file mode 100644 index 5b804659c75..00000000000 --- a/test/cmd_line/sort.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -"use strict"; - -import { ModeHandler } from '../../src/mode/modeHandler'; -import { setupWorkspace, cleanUpWorkspace, assertEqualLines } from './../testUtils'; -import { runCmdLine } from '../../src/cmd_line/main'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("Basic sort", () => { - let modeHandler: ModeHandler; - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test("Sort whole file, asc", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'b', '', 'o', 'a', '', 'o', 'c', '']); - await runCmdLine("sort", modeHandler); - - assertEqualLines([ - "a", - "b", - "c" - ]); - }); - - test("Sort whole file, dsc", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'b', '', 'o', 'a', '', 'o', 'c', '']); - await runCmdLine("sort!", modeHandler); - - assertEqualLines([ - "c", - "b", - "a" - ]); - }); - - test("Sort range, asc", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'b', '', 'o', 'd', '', 'o', 'a', '', 'o', 'c', '']); - await runCmdLine("1,3sort", modeHandler); - - assertEqualLines([ - "a", - "b", - "d", - "c" - ]); - }); - - test("Sort range, dsc", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'b', '', 'o', 'd', '', 'o', 'a', '', 'o', 'c', '']); - await runCmdLine("2,4sort!", modeHandler); - - assertEqualLines([ - "b", - "d", - "c", - "a" - ]); - }); - -}); \ No newline at end of file diff --git a/test/cmd_line/subparser.close.test.ts b/test/cmd_line/subparser.close.test.ts deleted file mode 100644 index 5de40fde1fd..00000000000 --- a/test/cmd_line/subparser.close.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; - -// The module 'assert' provides assertion methods from node -import * as assert from 'assert'; - -import {commandParsers} from '../../src/cmd_line/subparser'; - -suite(":close args parser", () => { - - test("has all aliases", () => { - assert.equal(commandParsers.close.name, commandParsers.clo.name); - }); - - test("can parse empty args", () => { - var args = commandParsers.close(""); - assert.equal(args.arguments.bang, undefined); - assert.equal(args.arguments.range, undefined); - }); - - test("ignores trailing white space", () => { - var args = commandParsers.close(" "); - assert.equal(args.arguments.bang, undefined); - assert.equal(args.arguments.range, undefined); - }); - - test("can parse !", () => { - var args = commandParsers.close("!"); - assert.ok(args.arguments.bang); - assert.equal(args.arguments.range, undefined); - }); - - test("throws if space before !", () => { - assert.throws(() => commandParsers.close(" !")); - }); - - test("ignores space after !", () => { - - var args = commandParsers.close("! "); - assert.equal(args.arguments.bang, true); - assert.equal(args.arguments.range, undefined); - }); - - test("throws if bad input", () => { - assert.throws(() => commandParsers.close("x")); - }); -}); diff --git a/test/cmd_line/subparser.quit.test.ts b/test/cmd_line/subparser.quit.test.ts deleted file mode 100644 index 0e3586ae845..00000000000 --- a/test/cmd_line/subparser.quit.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; - -// The module 'assert' provides assertion methods from node -import * as assert from 'assert'; - -import {commandParsers} from '../../src/cmd_line/subparser'; - -suite(":quit args parser", () => { - - test("has all aliases", () => { - assert.equal(commandParsers.quit.name, commandParsers.q.name); - }); - - test("can parse empty args", () => { - var args = commandParsers.quit(""); - assert.equal(args.arguments.bang, undefined); - assert.equal(args.arguments.range, undefined); - }); - - test("ignores trailing white space", () => { - var args = commandParsers.quit(" "); - assert.equal(args.arguments.bang, undefined); - assert.equal(args.arguments.range, undefined); - }); - - test("can parse !", () => { - var args = commandParsers.quit("!"); - assert.ok(args.arguments.bang); - assert.equal(args.arguments.range, undefined); - }); - - test("throws if space before !", () => { - assert.throws(() => commandParsers.quit(" !")); - }); - - test("ignores space after !", () => { - - var args = commandParsers.quit("! "); - assert.equal(args.arguments.bang, true); - assert.equal(args.arguments.range, undefined); - }); - - test("throws if bad input", () => { - assert.throws(() => commandParsers.quit("x")); - }); -}); diff --git a/test/cmd_line/subparser.substitute.test.ts b/test/cmd_line/subparser.substitute.test.ts deleted file mode 100644 index 72844b52ba2..00000000000 --- a/test/cmd_line/subparser.substitute.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; - -// The module 'assert' provides assertion methods from node -import * as assert from 'assert'; - -import {commandParsers} from '../../src/cmd_line/subparser'; - -suite(":substitute args parser", () => { - - test("can parse pattern ,replace and flags", () => { - var args = commandParsers.s("/a/b/g"); - assert.equal(args.arguments.pattern, "a"); - assert.equal(args.arguments.replace, "b"); - assert.equal(args.arguments.flags, 8); - }); - - test("can parse count", () => { - var args = commandParsers.s("/a/b/g 3"); - assert.equal(args.arguments.count, 3); - }); - - test("can parse custom delimiter", () => { - var args = commandParsers.s("#a#b#g"); - assert.equal(args.arguments.pattern, "a"); - assert.equal(args.arguments.replace, "b"); - assert.equal(args.arguments.flags, 8); - }); - - test("can parse flag KeepPreviousFlags", () => { - var args = commandParsers.s("/a/b/&"); - assert.equal(args.arguments.flags, 1); - }); -}); diff --git a/test/cmd_line/subparser.test.ts b/test/cmd_line/subparser.test.ts deleted file mode 100644 index 24ac2764a5d..00000000000 --- a/test/cmd_line/subparser.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -"use strict"; - -// The module 'assert' provides assertion methods from node -import * as assert from 'assert'; - -import {commandParsers} from '../../src/cmd_line/subparser'; - -suite(":write args parser", () => { - - test("has all aliases", () => { - assert.equal(commandParsers.write.name, commandParsers.w.name); - }); - - test("can parse empty args", () => { - // TODO: perhaps we don't need to export this func at all. - // TODO: this func must return args only, not a command? - // TODO: the range must be passed separately, not as arg. - var args = commandParsers.write(""); - assert.equal(args.arguments.append, undefined); - assert.equal(args.arguments.bang, undefined); - assert.equal(args.arguments.cmd, undefined); - assert.equal(args.arguments.file, undefined); - assert.equal(args.arguments.opt, undefined); - assert.equal(args.arguments.optValue, undefined); - assert.equal(args.arguments.range, undefined); - }); - - test("can parse ++opt", () => { - - var args = commandParsers.write("++enc=foo"); - assert.equal(args.arguments.append, undefined); - assert.equal(args.arguments.bang, undefined); - assert.equal(args.arguments.cmd, undefined); - assert.equal(args.arguments.file, undefined); - assert.equal(args.arguments.opt, 'enc'); - assert.equal(args.arguments.optValue, 'foo'); - assert.equal(args.arguments.range, undefined); - }); - - test("throws if bad ++opt name", () => { - - assert.throws(() => commandParsers.write("++foo=foo")); - }); - - test("can parse bang", () => { - - var args = commandParsers.write("!"); - assert.equal(args.arguments.append, undefined); - assert.equal(args.arguments.bang, true); - assert.equal(args.arguments.cmd, undefined); - assert.equal(args.arguments.file, undefined); - assert.equal(args.arguments.opt, undefined); - assert.equal(args.arguments.optValue, undefined); - assert.equal(args.arguments.range, undefined); - }); - - test("can parse ' !cmd'", () => { - - var args = commandParsers.write(" !foo"); - assert.equal(args.arguments.append, undefined); - assert.equal(args.arguments.bang, undefined); - assert.equal(args.arguments.cmd, 'foo'); - assert.equal(args.arguments.file, undefined); - assert.equal(args.arguments.opt, undefined); - assert.equal(args.arguments.optValue, undefined); - assert.equal(args.arguments.range, undefined); - }); - - test("can parse ' !cmd' when cmd is empty", () => { - - var args = commandParsers.write(" !"); - assert.equal(args.arguments.append, undefined); - assert.equal(args.arguments.bang, undefined); - assert.equal(args.arguments.cmd, undefined); - assert.equal(args.arguments.file, undefined); - assert.equal(args.arguments.opt, undefined); - assert.equal(args.arguments.optValue, undefined); - assert.equal(args.arguments.range, undefined); - }); -}); diff --git a/test/cmd_line/substitute.test.ts b/test/cmd_line/substitute.test.ts deleted file mode 100644 index 06817556d25..00000000000 --- a/test/cmd_line/substitute.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -"use strict"; - -import { ModeHandler } from '../../src/mode/modeHandler'; -import { setupWorkspace, cleanUpWorkspace, assertEqualLines } from './../testUtils'; -import { runCmdLine } from '../../src/cmd_line/main'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("Basic substitute", () => { - let modeHandler: ModeHandler; - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test("Replace single word once", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '']); - await runCmdLine("%s/a/d", modeHandler); - - assertEqualLines([ - "dba" - ]); - }); - - test("Replace with `g` flag", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '']); - await runCmdLine("%s/a/d/g", modeHandler); - - assertEqualLines([ - "dbd" - ]); - }); - - test("Replace multiple lines", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '', 'o', 'a', 'b']); - await runCmdLine("%s/a/d/g", modeHandler); - - assertEqualLines([ - "dbd", - "db" - ]); - }); - - test("Replace across specific lines", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '', 'o', 'a', 'b']); - await runCmdLine("1,1s/a/d/g", modeHandler); - - assertEqualLines([ - "dbd", - "ab" - ]); - }); - - test("Replace current line with no active selection", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '', 'o', 'a', 'b', '']); - await runCmdLine("s/a/d/g", modeHandler); - - assertEqualLines([ - "aba", - "db" - ]); - }); - - test("Replace text in selection", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '', 'o', 'a', 'b', '', '$', 'v', 'k', '0']); - await runCmdLine("'<,'>s/a/d/g", modeHandler); - - assertEqualLines([ - "dbd", - "db" - ]); - }); - - test("Substitute support marks", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'c', '', 'y', 'y', '2', 'p', 'g', 'g', 'm', 'a', 'j', 'm', 'b']); - await runCmdLine("'a,'bs/a/d/g", modeHandler); - - assertEqualLines([ - "dbc", - "dbc", - "abc" - ]); - }); -}); \ No newline at end of file diff --git a/test/cmd_line/writequit.test.ts b/test/cmd_line/writequit.test.ts deleted file mode 100644 index 7080f9a89c6..00000000000 --- a/test/cmd_line/writequit.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -"use strict"; - -import { ModeHandler } from '../../src/mode/modeHandler'; -import { setupWorkspace, cleanUpWorkspace, assertEqualLines, assertEqual } from './../testUtils'; -import { runCmdLine } from '../../src/cmd_line/main'; -import * as vscode from "vscode"; -import {join} from 'path'; -import * as assert from 'assert'; -import { getAndUpdateModeHandler } from "../../extension"; - -async function WaitForVsCodeClose() : Promise { - // cleanUpWorkspace - testUtils.ts - let poll = new Promise((c, e) => { - if (vscode.window.visibleTextEditors.length === 0) { - return c(); - } - - let pollCount = 0; - // TODO: the visibleTextEditors variable doesn't seem to be - // up to date after a onDidChangeActiveTextEditor event, not - // even using a setTimeout 0... so we MUST poll :( - let interval = setInterval(() => { - // if visibleTextEditors is not updated after 1 sec - // we can expect that 'wq' failed - if (pollCount <= 100) { - pollCount++; - if (vscode.window.visibleTextEditors.length > 0) { - return; - } - } - - clearInterval(interval); - c(); - }, 10); - }); - - try { - await poll; - } catch (error) { - assert.fail(null, null, error.toString(), ""); - } -} - -suite("Basic write-quit", () => { - let modeHandler: ModeHandler; - - suiteSetup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - suiteTeardown(cleanUpWorkspace); - - test("Run write and quit", async () => { - await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '']); - - await runCmdLine("wq", modeHandler); - await WaitForVsCodeClose(); - - assertEqual(vscode.window.visibleTextEditors.length, 0, "Window after 1sec still open"); - }); -}); \ No newline at end of file diff --git a/test/error.test.ts b/test/error.test.ts deleted file mode 100644 index 29f4cb79eba..00000000000 --- a/test/error.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import {VimError, ErrorCode, ErrorMessage} from '../src/error'; - -suite("Error", () => { - test("error code has message", () => { - /* tslint:disable:forin */ - for (const errorCodeString in ErrorCode) { - var errorCode = Number(errorCodeString); - if (!isNaN(errorCode)) { - assert.notEqual(ErrorMessage[errorCode], undefined, errorCodeString); - } - } - }); -}); \ No newline at end of file diff --git a/test/extension.test.ts b/test/extension.test.ts deleted file mode 100644 index 2b91444d2fc..00000000000 --- a/test/extension.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import * as vscode from 'vscode'; -import * as _ from 'lodash'; - -suite("package.json", () => { - test("all keys have handlers", async () => { - let pkg = require(__dirname + '/../../package.json'); - assert.ok(pkg); - - let registeredCommands = await vscode.commands.getCommands(); - let keybindings = pkg.contributes.keybindings; - assert.ok(pkg); - - for (let i = 0; i < keybindings.length; i++) { - let keybinding = keybindings[i]; - - var found = registeredCommands.indexOf(keybinding.command) >= -1; - assert.ok(found, "Missing handler for key=" + keybinding.key + ". Expected handler=" + keybinding.command); - } - }); -}); diff --git a/test/index.ts b/test/index.ts deleted file mode 100644 index d425650813b..00000000000 --- a/test/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; - -// -// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING -// -// This file is providing the test runner to use when running extension tests. -// By default the test runner in use is Mocha based. -// -// You can provide your own test runner if you want to override it by exporting -// a function run(testRoot: string, clb: (error:Error) => void) that the extension -// host can call to run the tests. The test runner is expected to use console.log -// to report the results back to the caller. When the tests are finished, return -// a possible error to the callback or null if none. - -import { Globals } from '../src/globals'; - -var testRunner = require('vscode/lib/testrunner'); - -Globals.isTesting = true; - -// You can directly control Mocha options by uncommenting the following lines -// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info -testRunner.configure({ - ui: 'tdd', - useColors: true, - timeout: 10000, -}); - -module.exports = testRunner; - - diff --git a/test/macro.test.ts b/test/macro.test.ts deleted file mode 100644 index c6917638d59..00000000000 --- a/test/macro.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -"use strict"; - -import { setupWorkspace, cleanUpWorkspace } from './testUtils'; -import { ModeHandler } from '../src/mode/modeHandler'; -import { getTestingFunctions } from './testSimplifier'; - -suite("Record and execute a macro", () => { - let { - newTest, - newTestOnly - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - }); - - teardown(cleanUpWorkspace); - - newTest({ - title: "Can record and execute", - start: ['|foo = 1', "bar = 'a'", "foobar = foo + bar"], - keysPressed: 'qaA;Ivar qj@a', - end: ['var foo = 1;', "var| bar = 'a';", "foobar = foo + bar"] - }); - - newTest({ - title: "Can repeat last invoked macro", - start: ['|foo = 1', "bar = 'a'", "foobar = foo + bar"], - keysPressed: 'qaA;Ivar qj@aj@@', - end: ['var foo = 1;', "var bar = 'a';", "var| foobar = foo + bar;"] - }); - - newTest({ - title: "Can play back with count", - start: ['|"("+a+","+b+","+c+","+d+","+e+")"'], - keysPressed: 'f+s + qq;.q8@q', - end: ['"(" + a + "," + b + "," + c + "," + d + "," + e +| ")"'] - }); - - newTest({ - title: "Can play back with count, abort when a motion fails", - start: ['|"("+a+","+b+","+c+","+d+","+e+")"'], - keysPressed: 'f+s + qq;.q22@q', - end: ['"(" + a + "," + b + "," + c + "," + d + "," + e +| ")"'] - }); - - newTest({ - title: "Repeat change on contiguous lines", - start: ['1. |one', '2. two', '3. three', '4. four'], - keysPressed: 'qa0f.r)w~jq3@a', - end: ['1) One', '2) Two', '3) Three', '4) F|our'] - }); - - newTest({ - title: "Append command to a macro", - start: ['1. |one', '2. two', '3. three', '4. four'], - keysPressed: 'qa0f.r)qqAw~jq3@a', - end: ['1) One', '2) Two', '3) Three', '4) F|our'] - }); - - newTest({ - title: "Can record Ctrl Keys and repeat", - start: ["1|."], - keysPressed: 'qayypq4@a', - end: ['1.', '2.', '3.', '4.', '5.', '|6.'] - }); - - newTest({ - title: "Can execute macros with dot commands properly", - start: ["|test", "test", "test", "test", "test", "test", "test"], - keysPressed: 'qadd.q@a@a', - end: ["|test"], - }); -}); \ No newline at end of file diff --git a/test/mode/modeHandler.test.ts b/test/mode/modeHandler.test.ts deleted file mode 100644 index 7cb50f7e89f..00000000000 --- a/test/mode/modeHandler.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import { setupWorkspace, cleanUpWorkspace } from './../testUtils'; -import { ModeName } from '../../src/mode/mode'; -import { ModeHandler } from '../../src/mode/modeHandler'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("Mode Handler", () => { - let modeHandler: ModeHandler; - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test("ctor", () => { - assert.equal(modeHandler.currentMode.name, ModeName.Normal); - assert.equal(modeHandler.currentMode.isActive, true); - }); - - test("can set current mode", async () => { - assert.equal(modeHandler.currentMode.name, ModeName.Normal); - - await modeHandler.handleKeyEvent("i"); - assert.equal(modeHandler.currentMode.name, ModeName.Insert); - }); -}); diff --git a/test/mode/modeInsert.test.ts b/test/mode/modeInsert.test.ts deleted file mode 100644 index 21f2780dee6..00000000000 --- a/test/mode/modeInsert.test.ts +++ /dev/null @@ -1,286 +0,0 @@ -"use strict"; - -import {setupWorkspace, cleanUpWorkspace, assertEqualLines, assertEqual} from './../testUtils'; -import {ModeName} from '../../src/mode/mode'; -import {TextEditor} from '../../src/textEditor'; -import {ModeHandler} from "../../src/mode/modeHandler"; -import { getTestingFunctions } from '../testSimplifier'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("Mode Insert", () => { - let modeHandler: ModeHandler; - - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test("can be activated", async () => { - let activationKeys = ['o', 'I', 'i', 'O', 'a', 'A']; - - for (let key of activationKeys) { - await modeHandler.handleKeyEvent(key); - assertEqual(modeHandler.currentMode.name, ModeName.Insert); - - await modeHandler.handleKeyEvent(''); - } - }); - - test("can handle key events", async () => { - await modeHandler.handleMultipleKeyEvents(['i', '!']); - - return assertEqualLines(["!"]); - }); - - test(" should change cursor position", async () => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 'h', 'e', 'l', 'l', 'o', - '' - ]); - - assertEqual(TextEditor.getSelection().start.character, 4, " moved cursor position."); - }); - - test(" can exit insert", async () => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 't', 'e', 'x', 't', - '', - 'o' - ]); - - return assertEqualLines(["text", ""]); - }); - - test(" can exit insert", async () => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 't', 'e', 'x', 't', - '', - 'o' - ]); - - return assertEqualLines(["text", ""]); - }); - - test("Stay in insert when entering characters", async () => { - await modeHandler.handleKeyEvent('i'); - for (var i = 0; i < 10; i++) { - await modeHandler.handleKeyEvent('1'); - assertEqual(modeHandler.currentMode.name === ModeName.Insert, true); - } - }); - - test("Can handle 'O'", async () => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 't', 'e', 'x', 't', - '', - 'O' - ]); - - return assertEqualLines(["", "text"]); - }); - - test("Can handle 'i'", async () => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 't', 'e', 'x', 't', 't', 'e', 'x', 't', // insert 'texttext' - '', - '^', 'l', 'l', 'l', 'l', // move to the 4th character - 'i', - '!' // insert a ! - ]); - - assertEqualLines(["text!text"]); - }); - - test("Can handle 'I'", async () => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 't', 'e', 'x', 't', - '', - '^', 'l', 'l', 'l', - 'I', - '!', - ]); - - assertEqualLines(["!text"]); - }); - - test("Can handle 'a'", async () => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 't', 'e', 'x', 't', 't', 'e', 'x', 't', // insert 'texttext' - '', - '^', 'l', 'l', 'l', 'l', // move to the 4th character - 'a', - '!' // append a ! - ]); - - assertEqualLines(["textt!ext"]); - }); - - test("Can handle 'A'", async () => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 't', 'e', 'x', 't', - '', - '^', - 'A', - '!', - ]); - - assertEqualLines(["text!"]); - }); - - test("Can handle ''", async () => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 't', 'e', 'x', 't', ' ', 't', 'e', 'x', 't', - '', - ]); - - assertEqualLines(["text "]); - }); - - newTest({ - title: "Can handle on leading whitespace", - start: ['foo', ' |bar'], - keysPressed: 'i', - end: ['foo', '|bar'] - }); - - newTest({ - title: "Can handle at beginning of line", - start: ['foo', '|bar'], - keysPressed: 'i', - end: ['foo|bar'] - }); - - test("Correctly places the cursor after deleting the previous line break", async() => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - 'o', 'n', 'e', '\n', 't', 'w', 'o', - '', '', '', - '' - ]); - - assertEqualLines(["onetwo"]); - - assertEqual(TextEditor.getSelection().start.character, 3, " moved cursor to correct position"); - }); - - test("will not remove leading spaces input by user", async() => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - ' ', ' ', - '' - ]); - - assertEqualLines([" "]); - }); - - test("will remove closing bracket", async() => { - await modeHandler.handleMultipleKeyEvents([ - 'i', - '(', - '' - ]); - - assertEqualLines(["()"]); - - await modeHandler.handleMultipleKeyEvents([ - 'a', - '', - '' - ]); - - assertEqualLines([""]); - }); - - newTest({ - title: "Backspace works on whitespace only lines", - start: ['abcd', ' | '], - keysPressed: 'a', - end: ['abcd', " | "], - }); - - newTest({ - title: "Backspace works on end of whitespace only lines", - start: ['abcd', ' | '], - keysPressed: 'a', - end: ['abcd', " | "], - }); - - newTest({ - title: "Backspace works at beginning of file", - start: ['|bcd'], - keysPressed: 'ia', - end: ['|abcd'], - }); - - newTest({ - title: "Can perform to exit and perform one command in normal", - start: ['testtest|'], - keysPressed: 'a123b123', - end: ['123|testtest123'] - }); - - newTest({ - title: "Can perform insert command prefixed with count", - start: ['tes|t'], - keysPressed: '2i_', - end: ['tes_|_t'] - }); - - newTest({ - title: "Can perform append command prefixed with count", - start: ['tes|t'], - keysPressed: '3a=', - end: ['test==|='] - }); - - newTest({ - title: "Can perform insert at start of line command prefixed with count", - start: ['tes|t'], - keysPressed: '2I_', - end: ['_|_test'] - }); - - newTest({ - title: "Can perform append to end of line command prefixed with count", - start: ['t|est'], - keysPressed: '3A=', - end: ['test==|='] - }); - - newTest({ - title: "Can perform change char (s) command prefixed with count", - start: ['tes|ttest'], - keysPressed: '3s=====', - end: ['tes====|=st'] - }); - - newTest({ - title: "Can handle 'o' with count", - start: ['|foobar'], - keysPressed: '5ofun', - end: ['foobar', 'fu|n', 'fun', 'fun', 'fun', 'fun'] - }); - - newTest({ - title: "Can handle 'O' with count", - start: ['|foobar'], - keysPressed: '5Ofun', - end: ['fun', 'fun', 'fun', 'fun', 'fu|n', 'foobar'] - }); -}); diff --git a/test/mode/modeNormal.test.ts b/test/mode/modeNormal.test.ts deleted file mode 100644 index c5faa5802ef..00000000000 --- a/test/mode/modeNormal.test.ts +++ /dev/null @@ -1,1791 +0,0 @@ -"use strict"; - -import { setupWorkspace, setTextEditorOptions, cleanUpWorkspace, assertEqual } from './../testUtils'; -import { ModeName } from '../../src/mode/mode'; -import { ModeHandler } from '../../src/mode/modeHandler'; -import { getTestingFunctions } from '../testSimplifier'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("Mode Normal", () => { - let modeHandler: ModeHandler; - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - setTextEditorOptions(4, false); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test("can be activated", async () => { - let activationKeys = ['', '']; - - for (let key of activationKeys) { - await modeHandler.handleKeyEvent('i'); - await modeHandler.handleKeyEvent(key!); - - assertEqual(modeHandler.currentMode.name, ModeName.Normal, `${key} doesn't work.`); - } - - await modeHandler.handleKeyEvent('v'); - await modeHandler.handleKeyEvent('v'); - - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - newTest({ - title: "Can handle %", - start: ['|((( )))'], - keysPressed: '%', - end: ["((( ))|)"], - }); - - newTest({ - title: "Can handle % before opening brace", - start: ['|one (two)'], - keysPressed: '%', - end: ["one (two|)"], - }); - - newTest({ - title: "Can handle % nested inside parens", - start: ['(|one { two })'], - keysPressed: '%', - end: ["(one { two |})"], - }); - - newTest({ - title: "Can handle dw", - start: ['one |two three'], - keysPressed: 'dw', - end: ["one |three"], - }); - - newTest({ - title: "Can handle dw", - start: ['one | '], - keysPressed: 'dw', - end: ["one| "], - }); - - newTest({ - title: "Can handle dw", - start: ['one |two'], - keysPressed: 'dw', - end: ["one| "], - }); - - newTest({ - title: "Can handle dw across lines (1)", - start: ['one |two', ' three'], - keysPressed: 'dw', - end: ["one| ", " three"] - }); - - newTest({ - title: "Can handle dw across lines (2)", - start: ['one |two', '', 'three'], - keysPressed: 'dw', - end: ["one| ", "", "three"] - }); - - newTest({ - title: "Can handle dd last line", - start: ['one', '|two'], - keysPressed: 'dd', - end: ["|one"], - }); - - newTest({ - title: "Can handle dd single line", - start: ['|one'], - keysPressed: 'dd', - end: ["|"], - }); - - newTest({ - title: "Can handle dd", - start: ['|one', 'two'], - keysPressed: 'dd', - end: ["|two"], - }); - - newTest({ - title: "Can handle 3dd", - start: ['|one', 'two', 'three', 'four', 'five'], - keysPressed: '3dd', - end: ["|four", "five"], - }); - - newTest({ - title: "Can handle 3dd off end of document", - start: ['one', 'two', 'three', '|four', 'five'], - keysPressed: '3dd', - end: ["one", "two", "|three"], - }); - - newTest({ - title: "Can handle d2d", - start: ['one', 'two', '|three', 'four', 'five'], - keysPressed: 'd2d', - end: ["one", "two", "|five"], - }); - - newTest({ - title: "Can handle dd empty line", - start: ['one', '|', 'two'], - keysPressed: 'dd', - end: ["one", "|two"], - }); - - newTest({ - title: "Can handle ddp", - start: ['|one', 'two'], - keysPressed: 'ddp', - end: ["two", "|one"], - }); - - newTest({ - title: "Can handle 'de'", - start: ['text tex|t'], - keysPressed: '^de', - end: ['| text'], - }); - - newTest({ - title: "Can handle 'de' then 'de' again", - start: ['text tex|t'], - keysPressed: '^dede', - end: ['|'], - }); - - newTest({ - title: "Can handle 'db'", - start: ['One tw|o'], - keysPressed: '$db', - end: ['One |o'], - }); - - newTest({ - title: "Can handle 'db then 'db' again", - start: ['One tw|o'], - keysPressed: '$dbdb', - end: ['|o'], - }); - - newTest({ - title: "Can handle 'dl' at end of line", - start: ['bla|h'], - keysPressed: '$dldldl', - end: ['|b'], - }); - - newTest({ - title: "Can handle 'dF'", - start: ['abcdefg|h'], - keysPressed: 'dFd', - end: ['abc|h'], - }); - - newTest({ - title: "Can handle 'dT'", - start: ['abcdefg|h'], - keysPressed: 'dTd', - end: ['abcd|h'], - }); - - newTest({ - title: "Can handle 'd3' then ", - start: ['|1', '2', '3', '4', '5', '6'], - keysPressed: 'd3\n', - end: ['|5', '6'], - }); - - newTest({ - title: "Can handle 'dj'", - start: ['|11', '22', '33', '44', '55', '66'], - keysPressed: 'dj', - end: ['|33', '44', '55', '66'], - }); - - newTest({ - title: "Can handle 'dk'", - start: ['11', '22', '33', '44', '55', '|66'], - keysPressed: 'dk', - end: ['11', '22', '33', '|44'], - }); - - newTest({ - title: "Can handle 'cw'", - start: ['text text tex|t'], - keysPressed: '^lllllllcw', - end: ['text te| text'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'cw' without deleting following white spaces", - start: ['|const a = 1;'], - keysPressed: 'cw', - end: ['| a = 1;'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'c2w'", - start: ['|const a = 1;'], - keysPressed: 'c2w', - end: ['| = 1;'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'cw' without removing EOL", - start: ['|text;', 'text'], - keysPressed: 'llllcw', - end: ['text|', 'text'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 's'", - start: ['tex|t'], - keysPressed: '^sk', - end: ['k|ext'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'yiw' with correct cursor ending position", - start: ['tes|t'], - keysPressed: 'yiwp', - end: ['ttes|test'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'ciw'", - start: ['text text tex|t'], - keysPressed: '^lllllllciw', - end: ['text | text'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ciw' on blanks", - start: ['text text tex|t'], - keysPressed: '^lllllciw', - end: ['text|text text'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'caw'", - start: ['text text tex|t'], - keysPressed: '^llllllcaw', - end: ['text |text'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'caw' on first letter", - start: ['text text tex|t'], - keysPressed: '^lllllcaw', - end: ['text |text'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'caw' on blanks", - start: ['text tex|t'], - keysPressed: '^lllllcaw', - end: ['text|'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'caw' on blanks", - start: ['text | text text'], - keysPressed: 'caw', - end: ['text| text'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci(' on first parentheses", - start: ['print(|"hello")'], - keysPressed: 'ci(', - end: ['print(|)'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci(' with nested parentheses", - start: ['call|(() => 5)'], - keysPressed: 'ci(', - end: ['call(|)'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci(' backwards through nested parens", - start: ['call(() => |5)'], - keysPressed: 'ci(', - end: ['call(|)'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'cib' on first parentheses", - start: ['print(|"hello")'], - keysPressed: 'cib', - end: ['print(|)'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci(' across multiple lines with last character at beginning", - start: ['(|a', 'b)'], - keysPressed: 'ci)', - end: ['(|)'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ca(' spanning multiple lines", - start: ['call(', ' |arg1)'], - keysPressed: 'ca(', - end: ['call|'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'cab' spanning multiple lines", - start: ['call(', ' |arg1)'], - keysPressed: 'cab', - end: ['call|'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci{' spanning multiple lines", - start: ['one {', '|', '}'], - keysPressed: 'ci{', - end: ['one {', '|', '}'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci{' spanning multiple lines and handle whitespaces correctly", - start: ['one { ', '|', '}'], - keysPressed: 'ci{', - end: ['one {|', '}'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci{' spanning multiple lines and handle whitespaces correctly", - start: ['one {', '|', ' }'], - keysPressed: 'ci{', - end: ['one {', '|', ' }'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci(' on the closing bracket", - start: ['(one|)'], - keysPressed: 'ci(', - end: ['(|)'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ciB' spanning multiple lines", - start: ['one {', '|', '}'], - keysPressed: 'ciB', - end: ['one {', '|', '}'], - endMode: ModeName.Insert - }); - - newTest({ - title: "will fail when ca( with no ()", - start: ['|blaaah'], - keysPressed: 'ca(', - end: ['|blaaah'], - endMode: ModeName.Normal - }); - - newTest({ - title: "will fail when ca{ with no {}", - start: ['|blaaah'], - keysPressed: 'ca{', - end: ['|blaaah'], - endMode: ModeName.Normal - }); - - newTest({ - title: "will fail when caB with no {}", - start: ['|blaaah'], - keysPressed: 'caB', - end: ['|blaaah'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'ci[' spanning multiple lines", - start: ['one [', '|', ']'], - keysPressed: 'ci[', - end: ['one [', '|', ']'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci]' on first bracket", - start: ['one[|"two"]'], - keysPressed: 'ci]', - end: ['one[|]'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ca[' on first bracket", - start: ['one[|"two"]'], - keysPressed: 'ca[', - end: ['one|'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ca]' on first bracket", - start: ['one[|"two"]'], - keysPressed: 'ca]', - end: ['one|'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\'' on first quote", - start: ["|'one'"], - keysPressed: "ci'", - end: ["'|'"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\'' inside quoted string", - start: ["'o|ne'"], - keysPressed: "ci'", - end: ["'|'"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\'' on closing quote", - start: ["'one|'"], - keysPressed: "ci'", - end: ["'|'"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\'' when string is ahead", - start: ["on|e 'two'"], - keysPressed: "ci'", - end: ["one '|'"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\"' on opening quote", - start: ['|"one"'], - keysPressed: 'ci"', - end: ['"|"'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\"' starting behind the quoted word", - start: ['|one "two"'], - keysPressed: 'ci"', - end: ['one "|"'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ca\"' starting behind the quoted word", - start: ['|one "two"'], - keysPressed: 'ca"', - end: ['one |'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ca\"' starting on the opening quote", - start: ['one |"two"'], - keysPressed: 'ca"', - end: ['one |'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\"' with escaped quotes", - start: ['"one \\"tw|o\\""'], - keysPressed: 'ci"', - end: ['"|"'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\"' with a single escaped quote", - start: ['|"one \\" two"'], - keysPressed: 'ci"', - end: ['"|"'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\"' with a single escaped quote behind", - start: ['one "two \\" |three"'], - keysPressed: 'ci"', - end: ['one "|"'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\"' with an escaped backslash", - start: ['one "tw|o \\\\three"'], - keysPressed: 'ci"', - end: ['one "|"'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\"' with an escaped backslash on closing quote", - start: ['"\\\\|"'], - keysPressed: 'ci"', - end: ['"|"'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ca\"' starting on the closing quote", - start: ['one "two|"'], - keysPressed: 'ca"', - end: ['one |'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'ci\"' with complex escape sequences", - start: ['"two|\\\\\\""'], - keysPressed: 'ci"', - end: ['"|"'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can pick the correct open quote between two strings for 'ci\"'", - start: ['"one" |"two"'], - keysPressed: 'ci"', - end: ['"one" "|"'], - endMode: ModeName.Insert - }); - - newTest({ - title: "will fail when ca\" ahead of quoted string", - start: ['"one" |two'], - keysPressed: 'ca"', - end: ['"one" |two'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'ca`' inside word", - start: ['one `t|wo`'], - keysPressed: 'ca`', - end: ['one |'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'daw' on word with cursor inside spaces", - start: ['one two | three, four '], - keysPressed: 'daw', - end: ['one two|, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daw' on word with trailing spaces", - start: ['one tw|o three, four '], - keysPressed: 'daw', - end: ['one |three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daw' on word with leading spaces", - start: ['one two th|ree, four '], - keysPressed: 'daw', - end: ['one two|, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daw' on word with numeric prefix", - start: ['on|e two three, four '], - keysPressed: 'd3aw', - end: ['|, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daw' on word with numeric prefix and across lines", - start: ['one two three, fo|ur ', 'five six'], - keysPressed: 'd2aw', - end: ['one two three, |six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daw' on word with numeric prefix and across lines", - start: ['one two fo|ur', 'five six'], - keysPressed: 'd2aw', - end: ['one two |six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daw' on word with numeric prefix and across lines, containing words end with `.`", - start: ['one two three, fo|ur ', 'five. six'], - keysPressed: 'd2aw', - end: ['one two three, |. six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daw' on end of word", - start: ['one two three fou|r'], - keysPressed: 'daw', - end: ['one two thre|e'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daw' on words at beginning of line with leading whitespace", - start: ['if (something){', - ' |this.method();'], - keysPressed: 'daw', - end: ['if (something){', - ' |.method();'] - }); - - newTest({ - title: "Can handle 'daw' on words at ends of lines in the middle of whitespace", - start: ['one two | ', - 'four'], - keysPressed: 'daw', - end: ['one tw|o'] - }); - - newTest({ - title: "Can handle 'daw' on word at beginning of file", - start: ['o|ne'], - keysPressed: 'daw', - end: ['|'] - }); - - newTest({ - title: "Can handle 'daw' on word at beginning of line", - start: ['one two', - 'th|ree'], - keysPressed: 'daw', - end: ['one two', - '|'] - }); - - newTest({ - title: "Can handle 'daw' on word at end of line with trailing whitespace", - start: ['one tw|o ', - 'three four'], - keysPressed: 'daw', - end: ['one| ', - 'three four'] - }); - - newTest({ - title: "Can handle 'daw' around word at end of line", - start: ['one t|wo', - ' three'], - keysPressed: 'daw', - end: ['on|e', - ' three'] - }); - - newTest({ - title: "Can handle 'daW' on big word with cursor inside spaces", - start: ['one two | three, four '], - keysPressed: 'daW', - end: ['one two| four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daW' on word with trailing spaces", - start: ['one tw|o three, four '], - keysPressed: 'daW', - end: ['one |three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daW' on word with leading spaces", - start: ['one two th|ree, four '], - keysPressed: 'daW', - end: ['one two |four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daW' on word with numeric prefix", - start: ['on|e two three, four '], - keysPressed: 'd3aW', - end: ['|four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daW' on word with numeric prefix and across lines", - start: ['one two three, fo|ur ', 'five. six'], - keysPressed: 'd2aW', - end: ['one two three, |six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daW' on beginning of word", - start: ['one |two three'], - keysPressed: 'daW', - end: ['one |three'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'daW' on end of one line", - start: ['one |two'], - keysPressed: 'daW', - end: ['on|e'], - endMode: ModeName.Normal - }); - newTest({ - title: "Can handle 'daW' around word at end of line", - start: ['one t|wo', - ' three'], - keysPressed: 'daW', - end: ['on|e', - ' three'] - }); - - newTest({ - title: "Can handle 'diw' on word with cursor inside spaces", - start: ['one two | three, four '], - keysPressed: 'diw', - end: ['one two|three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diw' on word", - start: ['one tw|o three, four '], - keysPressed: 'diw', - end: ['one | three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diw' on word with numeric prefix", - start: ['on|e two three, four '], - keysPressed: 'd3iw', - end: ['| three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diw' on trailing spaces at the end of line", - start: ['one two three | ', 'five six'], - keysPressed: 'diw', - end: ['one two thre|e', 'five six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diw' on word with numeric prefix and across lines", - start: ['one two three, fo|ur ', 'five six'], - keysPressed: 'd3iw', - end: ['one two three, | six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diw' on word with numeric prefix and across lines, containing words end with `.`", - start: ['one two three, fo|ur ', 'five. six'], - keysPressed: 'd3iw', - end: ['one two three, |. six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diW' on big word with cursor inside spaces", - start: ['one two | three, four '], - keysPressed: 'diW', - end: ['one two|three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diW' on word with trailing spaces", - start: ['one tw|o, three, four '], - keysPressed: 'diW', - end: ['one | three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diW' on word with leading spaces", - start: ['one two th|ree, four '], - keysPressed: 'diW', - end: ['one two | four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diW' on word with numeric prefix", - start: ['on|e two three, four '], - keysPressed: 'd3iW', - end: ['| three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'diW' on word with numeric prefix and across lines", - start: ['one two three, fo|ur ', 'five. six'], - keysPressed: 'd3iW', - end: ['one two three, | six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle d}", - start: ['|foo', 'bar', '', 'fun'], - keysPressed: 'd}', - end: ['|', 'fun'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle y} at beginning of line", - start: ['|foo', 'bar', '', 'fun'], - keysPressed: 'y}p', - end: ['foo', '|foo', 'bar', 'bar', '', 'fun'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Select sentence with trailing spaces", - start: ["That's my sec|ret, Captain. I'm always angry."], - keysPressed: 'das', - end: ["|I'm always angry."], - endMode: ModeName.Normal - }); - - newTest({ - title: "Select sentence with leading spaces", - start: ["That's my secret, Captain. I'm a|lways angry."], - keysPressed: 'das', - end: ["That's my secret, Captain|."], - endMode: ModeName.Normal - }); - - newTest({ - title: "Select inner sentence with trailing spaces", - start: ["That's my sec|ret, Captain. I'm always angry."], - keysPressed: 'dis', - end: ["| I'm always angry."], - endMode: ModeName.Normal - }); - - newTest({ - title: "Select inner sentence with leading spaces", - start: ["That's my secret, Captain. I'm a|lways angry."], - keysPressed: 'dis', - end: ["That's my secret, Captain.| "], - endMode: ModeName.Normal - }); - - newTest({ - title: "Select spaces between sentences", - start: ["That's my secret, Captain. | I'm always angry."], - keysPressed: 'visd', - end: ["That's my secret, Captain.|I'm always angry."], - endMode: ModeName.Normal - }); - - - newTest({ - title: "Can handle 'df'", - start: ['aext tex|t'], - keysPressed: '^dft', - end: ['| text'] - }); - - newTest({ - title: "Can handle 'dt'", - start: ['aext tex|t'], - keysPressed: '^dtt', - end: ['|t text'] - }); - - newTest({ - title: "Can handle backspace", - start: ['text |text'], - keysPressed: '', - end: ['tex|t text'] - }); - - newTest({ - title: "Can handle backspace across lines", - start: ['one', '|two'], - keysPressed: '', - end: ['o|ne', 'two'] - }); - - newTest({ - title: "Can handle A and backspace", - start: ['|text text'], - keysPressed: 'A', - end: ['text te|x'] - }); - - newTest({ - title: "Can handle 'yy' without changing cursor position", - start: ['one', 'tw|o'], - keysPressed: 'yy', - end: ['one', 'tw|o'] - }); - - newTest({ - title: "Can handle 'P' after 'yy'", - start: ['one', 'tw|o'], - keysPressed: 'yyP', - end: ['one', '|two', 'two'] - }); - - newTest({ - title: "Can handle 'p' after 'yy'", - start: ['one', 'tw|o'], - keysPressed: 'yyp', - end: ['one', 'two', '|two'] - }); - - newTest({ - title: "Can handle 'P' after 'Nyy'", - start: ['on|e', 'two', 'three'], - keysPressed: '3yyP', - end: ['|one', 'two', 'three', 'one', 'two', 'three'] - }); - - newTest({ - title: "Can handle 'p' after 'Nyy'", - start: ['on|e', 'two', 'three'], - keysPressed: '3yyp', - end: ['one', '|one', 'two', 'three', 'two', 'three'] - }); - - newTest({ - title: "Can handle 'p' after 'yy' with correct cursor position", - start: ['| one', 'two'], - keysPressed: 'yyjp', - end: [' one', 'two', ' |one'] - }); - - newTest({ - title: "Can handle 'gp' after 'yy'", - start: ['one', 'tw|o', 'three'], - keysPressed: 'yygp', - end: ['one', 'two', 'two', '|three'] - }); - - newTest({ - title: "Can handle 'gp' after 'Nyy'", - start: ['on|e', 'two', 'three'], - keysPressed: '2yyjgp', - end: ['one', 'two', 'one', 'two', '|three'] - }); - - newTest({ - title: "Can handle 'gp' after 'Nyy' if cursor is on the last line", - start: ['on|e', 'two', 'three'], - keysPressed: '2yyjjgp', - end: ['one', 'two', 'three', 'one', '|two'] - }); - - newTest({ - title: "Can handle 'gP' after 'yy'", - start: ['one', 'tw|o', 'three'], - keysPressed: 'yygP', - end: ['one', 'two', '|two', 'three'] - }); - - newTest({ - title: "Can handle 'gP' after 'Nyy'", - start: ['on|e', 'two', 'three'], - keysPressed: '2yygP', - end: ['one', 'two', '|one', 'two', 'three'] - }); - - newTest({ - title: "Can handle ']p' after yy", - start: [' |one', ' two'], - keysPressed: 'yyj]p', - end: [' one', ' two', ' |one'] - }); - - newTest({ - title: "Can handle ']p' after 'Nyy'", - start: [' |one', ' two', ' three'], - keysPressed: '2yyjj]p', - end: [' one', ' two', ' three', ' |one', ' two'] - }); - - newTest({ - title: "Can handle ']p' after 'Nyy' and indent with tabs first", - start: [' |one', ' two', ' three'], - keysPressed: '2yyjj]p', - end: [' one', ' two', ' three', ' |one', '\ttwo'] - }); - - newTest({ - title: "Can handle ']p' after 'Nyy' and decrease indents if possible", - start: [' |one', ' two', ' three'], - keysPressed: '2yyjj]p', - end: [' one', ' two', ' three', ' |one', 'two'] - }); - - newTest({ - title: "Can handle '[p' after yy", - start: [' two', ' |one'], - keysPressed: 'yyk[p', - end: [' |one', ' two', ' one'] - }); - - newTest({ - title: "Can handle '[p' after 'Nyy'", - start: [' three', '|one', ' two'], - keysPressed: '2yyk[p', - end: [' |one', ' two', ' three', 'one', ' two'] - }); - - newTest({ - title: "Can handle '[p' after 'Nyy' and indent with tabs first", - start: [' three', '| one', ' two'], - keysPressed: '2yyk[p', - end: [' |one', '\ttwo', ' three', ' one', ' two'] - }); - - newTest({ - title: "Can handle '[p' after 'Nyy' and decrease indents if possible", - start: [' three', ' |one', ' two'], - keysPressed: '2yyk[p', - end: [' |one', 'two', ' three', ' one', ' two'] - }); - - newTest({ - title: "Can handle 'p' after y'a", - start: ['|one', 'two', 'three'], - keysPressed: "majjy'ap", - end: ['one', 'two', 'three', '|one', 'two', 'three'] - }); - - newTest({ - title: "Can handle pasting in visual mode over selection", - start: ['|foo', 'bar', 'fun'], - keysPressed: 'Yjvll"ayjV"app', - end: ['foo', 'bar', 'bar', '|fun'] - }); - - newTest({ - title: "Can repeat w", - start: ['|one two three four'], - keysPressed: '2w', - end: ['one two |three four'] - }); - - newTest({ - title: "Can repeat p", - start: ['|one'], - keysPressed: 'yy2p', - end: ['one', '|one', 'one'] - }); - - newTest({ - title: "I works correctly", - start: ['| one'], - keysPressed: 'Itest ', - end: [' test| one'] - }); - - newTest({ - title: "gI works correctly", - start: ['| one'], - keysPressed: 'gItest', - end: ['tes|t one'] - }); - - newTest({ - title: "g_ works correctly", - start: ['te|sttest'], - keysPressed: 'g_', - end: ['testtes|t'] - }); - - newTest({ - title: "3g_ works correctly", - start: ['tes|ttest', 'testtest', 'testtest'], - keysPressed: '3g_', - end: ['testtest', 'testtest', 'testtes|t'] - }); - -// These tests run poorly on Travis for w.e. reason - // newTest({ - // title: "gq handles spaces after single line comments correctly", - // start: ['// We choose to write a vim extension, not because it is easy, but because it is hard|.'], - // keysPressed: 'Vgq', - // end: [ '// We choose to write a vim extension, not because it is easy, but because it is', - // '|// hard.'], - // }); - - // newTest({ - // title: "gq handles spaces before single line comments correctly", - // start: [' // We choose to write a vim extension, not because it is easy, but because it is hard|.'], - // keysPressed: 'Vgq', - // end: [ ' // We choose to write a vim extension, not because it is easy, but because', - // '| // it is hard.'] - // }); - - - newTest({ - title: "Can handle space", - start: ['|abc', 'def'], - keysPressed: ' ', - end: ['ab|c', 'def'] - }); - - newTest({ - title: "Can handle space", - start: ['|abc', 'def'], - keysPressed: ' ', - end: ['abc', 'd|ef'] - }); - - newTest({ - title: "Undo 1", - start: ['|'], - keysPressed: 'iabcadefuu', - end: ['|'] - }); - - newTest({ - title: "Undo 2", - start: ['|'], - keysPressed: 'iabcadefu', - end: ['ab|c'] - }); - - newTest({ - title: "Undo cursor", - start: ['|'], - keysPressed: 'IabcIdefIghiuuu', - end: ['|'] - }); - - newTest({ - title: "Undo cursor 2", - start: ['|'], - keysPressed: 'IabcIdefIghiuu', - end: ['|abc'] - }); - - newTest({ - title: "Undo cursor 3", - start: ['|'], - keysPressed: 'IabcIdefIghiu', - end: ['|defabc'] - }); - - newTest({ - title: "Undo with movement first", - start: ['|'], - keysPressed: 'iabcadefhlhlu', - end: ['ab|c'] - }); - - newTest({ - title: "Redo", - start: ['|'], - keysPressed: 'iabcadefuu', - end: ['|abc'] - }); - - newTest({ - title: "Redo", - start: ['|'], - keysPressed: 'iabcadefuu', - end: ['abc|def'] - }); - - newTest({ - title: "Redo", - start: ['|'], - keysPressed: 'iabcadefuuhlhl', - end: ['abc|def'] - }); - - newTest({ - title: "Can handle u", - start: ['|ABC DEF'], - keysPressed: 'vwu', - end: ['|abc dEF'] - }); - - newTest({ - title: "Can handle guw", - start: ['|ABC DEF'], - keysPressed: 'guw', - end: ['|abc DEF'] - }); - - newTest({ - title: "Can handle gUw", - start: ['|abc def'], - keysPressed: 'gUw', - end: ['|ABC def'] - }); - - newTest({ - title: "Can handle u over line breaks", - start: ['|ABC', 'DEF'], - keysPressed: 'vG$u', - end: ['|abc', 'def'] - }); - - newTest({ - title: "can handle s in visual mode", - start: ["|abc def ghi"], - keysPressed: "vwshi ", - end: ["hi| ef ghi"] - }); - - newTest({ - title: "can handle p with selection", - start: ["|abc def ghi"], - keysPressed: "vwywvwp", - end: ["abc abc |dhi"] - }); - - // test works when run manually - // newTest({ - // title: "can handle p with selection", - // start: ["one", "two", "|three"], - // keysPressed: "yykVp", - // end: ["|three", "three"] - // }); - - newTest({ - title: "can handle P with selection", - start: ["|abc def ghi"], - keysPressed: "vwywvwP", - end: ["abc abc |dhi"] - }); - - newTest({ - title: "can handle p in visual to end of line", - start: ["1234 |5678", "test test"], - keysPressed: "vllllyjvllllp", - end: ["1234 5678", "test |5678", ""] - }); - - newTest({ - title: "can repeat backspace twice", - start: ["|11223344"], - keysPressed: "A0.", - end: ["112|2"] - }); - - newTest({ - title: "can delete linewise with d2G", - start: ["|one", "two" , "three"], - keysPressed: "d2G", - end: ["|three"] - }); - - newTest({ - title: "can dE correctly", - start: ["|one two three"], - keysPressed: "dE", - end: ["| two three"] - }); - - newTest({ - title: "can dE correctly", - start: ["|one((( two three"], - keysPressed: "dE", - end: ["| two three"] - }); - - newTest({ - title: "can dE correctly", - start: ["one two |three"], - keysPressed: "dE", - end: ["one two| "] - }); - - newTest({ - title: "can ctrl-a correctly behind a word", - start: ["|one 9"], - keysPressed: "", - end: ["one 1|0"] - }); - - newTest({ - title: "can ctrl-a the right word (always the one AFTER the cursor)", - start: ["1 |one 2"], - keysPressed: "", - end: ["1 one |3"] - }); - - newTest({ - title: "can ctrl-a on word", - start: ["one -|11"], - keysPressed: "", - end: ["one -1|0"] - }); - - newTest({ - title: "can ctrl-a on a hex number", - start: ["|0xf"], - keysPressed: "", - end: ["0x1|0"] - }); - - newTest({ - title: "can ctrl-a on decimal", - start: ["1|1.123"], - keysPressed: "", - end: ["1|2.123"] - }); - - newTest({ - title: "can ctrl-a with numeric prefix", - start: ["|-10"], - keysPressed: "15", - end: ["|5"] - }); - - newTest({ - title: "can ctrl-a on a decimal", - start: ["-10.|1"], - keysPressed: "10", - end: ["-10.1|1"] - }); - - newTest({ - title: "can ctrl-a on an octal ", - start: ["07|"], - keysPressed: "", - end: ["01|0"] - }); - - newTest({ - title: "Correctly increments in the middle of a number", - start: ["10|1"], - keysPressed: "", - end: ["10|2"] - }); - - newTest({ - title: "can ctrl-x correctly behind a word", - start: ["|one 10"], - keysPressed: "", - end: ["one |9"] - }); - - newTest({ - title: "can ctrl-a on an number with word before ", - start: ["|test3"], - keysPressed: "", - end: ["test|4"] - }); - - newTest({ - title: "can ctrl-a on an number with word before and after ", - start: ["|test3abc"], - keysPressed: "", - end: ["test|4abc"] - }); - - newTest({ - title: "can ctrl-x on a negative number with word before and after ", - start: ["|test-2abc"], - keysPressed: "", - end: ["test|1abc"] - }); - - newTest({ - title: "can ctrl-a properly on multiple lines", - start: ["id: 1|,", "someOtherId: 1"], - keysPressed: "", - end: ["id: 1|,", "someOtherId: 1"] - }); - - newTest({ - title: "can on word with multiple numbers (incrementing first number)", - start: ["f|oo1bar2"], - keysPressed: "", - end: ["foo|2bar2"] - }); - - newTest({ - title: "can on word with multiple numbers (incrementing second number)", - start: ["foo1|bar2"], - keysPressed: "", - end: ["foo1bar|3"] - }); - - newTest({ - title: "can do Y", - start: ["|blah blah"], - keysPressed: "Yp", - end: ["blah blah", "|blah blah"] - }); - - newTest({ - title: "Can do S", - start: [" one", " tw|o", " three"], - keysPressed: "2S", - end: [" one", " |"] - }); - - newTest({ - title: "/ does not affect mark", - start: ["|one", "twooo", "thurr"], - keysPressed: "ma/two\n'a", - end: ["|one", "twooo", "thurr"] - }); - - newTest({ - title: "/ can search with regex", - start: ["|", "one two2o"], - keysPressed: "/o\\do\n", - end: ["", "one tw|o2o"] - }); - - newTest({ - title: "/ can search with newline", - start: ["|asdf", "__asdf", "asdf"], - keysPressed: "/\\nasdf\n", - end: ["asdf", "__asd|f", "asdf"], - }); - - newTest({ - title: "/ can search through multiple newlines", - start: ["|asdf", "__asdf", "asdf", "abc", " abc"], - keysPressed: "/\asdf\\nasdf\\nabc\n", - end: ["asdf", "__|asdf", "asdf", "abc", " abc"], - }); - - newTest({ - title: "Can do C", - start: ["export const options = {", "|", "};"], - keysPressed: "C", - end: ["export const options = {", "|", "};"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do cit on a matching tag", - start: ["he|llo"], - keysPressed: "cit", - end: ["|"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Ignores cit on a non-matching tag", - start: ["he|llo"], - keysPressed: "cit", - end: ["he|llo"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Ignores cit on a nested tag", - start: ["he|llo"], - keysPressed: "cit", - end: ["|"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do cit on a tag with an attribute tag", - start: ["hello"], - keysPressed: "cit", - end: ["|"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do cat on a matching tag", - start: ["one he|llo two"], - keysPressed: "cat", - end: ["one | two"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do cit on a multiline tag", - start: [" \nhe|llo\ntext"], - keysPressed: "cit", - end: [" |"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do cit on a multiline tag with nested tags", - start: [" \n

hello

\nh
e|llo\ntext
"], - keysPressed: "cit", - end: [" |"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do cit inside of a tag with another non closing tag inside tags", - start: ["hello
wo|rld
"], - keysPressed: "cit", - end: ["|"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do cit inside of a tag with another empty closing tag inside tags", - start: ["hel|loworld"], - keysPressed: "cit", - end: ["|"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do dit on empty tag block, cursor moves to inside", - start: ["
"], - keysPressed: "dit", - end: ["|"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do cit on empty tag block, cursor moves to inside", - start: ["
"], - keysPressed: "cit", - end: ["|"], - endMode: ModeName.Insert - }); - - newTest({ - title: "can do cit with self closing tags", - start: ["
{{c|ursor here}}
"], - keysPressed: "cit", - end: ["
|
"], - endMode: ModeName.Insert - }); - - newTest({ - title: "Respects indentation with cc", - start: ["{", " int| a;"], - keysPressed: "cc", - end: ["{", " |"], - endMode: ModeName.Insert - }); - - newTest({ - title: "can handle 'cc' on empty line", - start: ['foo', '|', 'bar'], - keysPressed: 'cc', - end: ['foo', '|', 'bar'], - endMode: ModeName.Insert - }); - - newTest({ - title: "cc copies linewise", - start: ['foo', '|fun', 'bar'], - keysPressed: 'ccjp', - end: ['foo', '', 'bar', '|fun'] - }); - - newTest({ - title: "Indent current line with correct Vim Mode", - start: ["|one", "two"], - keysPressed: ">>", - end: ["\t|one", "two"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle and do nothing", - start: ['te|st'], - keysPressed: '', - end: ['te|st'], - }); - - newTest({ - title: "Can handle # on consecutive words", - start: ['test test test test |test'], - keysPressed: '#', - end: ['test test test |test test'], - }); - - newTest({ - title: "Can handle # on skipped words", - start: ['test aaa test aaa test aaa test aaa |test'], - keysPressed: '#', - end: ['test aaa test aaa test aaa |test aaa test'], - }); - - newTest({ - title: "Can 'D'elete the characters under the cursor until the end of the line", - start: ['test aaa test aaa test aaa test |aaa test'], - keysPressed: 'D', - end: ['test aaa test aaa test aaa test| '] - }); - -/* -Disabling test until upstream VSCode issue is resolved: https://github.com/Microsoft/vscode/issues/26274 - newTest({ - title: "Can 'D'elete the characters under multiple cursors until the end of the line", - start: [ - 'test aaa test aaa test aaa test |aaa test', - 'test aaa test aaa test aaa test aaa test' - ], - keysPressed: 'D', - end: [ - 'test aaa test aaa test aaa tes|t ', - 'test aaa test aaa test aaa test ' - ] - }); -*/ - - newTest({ - title: "cc on whitespace-only line clears line", - start: ["| "], - keysPressed: 'cc', - end: ["|"], - }); - - newTest({ - title: "Can do cai", - start: [ - 'if foo > 3:', - ' log("foo is big")|', - ' foo = 3', - 'do_something_else()', - ], - keysPressed: "cai", - end: [ - '|', - 'do_something_else()', - ], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do cii", - start: [ - 'if foo > 3:', - '\tlog("foo is big")', - '\tfoo = 3', - '|', - 'do_something_else()', - ], - keysPressed: "cii", - end: [ - 'if foo > 3:', - '\t|', - 'do_something_else()', - ], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do caI", - start: [ - 'if foo > 3:', - ' log("foo is big")|', - ' foo = 3', - 'do_something_else()', - ], - keysPressed: "caI", - end: [ - '|', - ], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can do dai", - start: [ - 'if foo > 3:', - ' log("foo is big")|', - ' foo = 3', - 'do_something_else()', - ], - keysPressed: "dai", - end: [ - '|do_something_else()', - ], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do dii", - start: [ - 'if foo > 3:', - ' log("foo is big")', - ' foo = 3', - '|', - 'do_something_else()', - ], - keysPressed: "dii", - end: [ - 'if foo > 3:', - '|do_something_else()', - ], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do daI", - start: [ - 'if foo > 3:', - ' log("foo is big")|', - ' foo = 3', - 'do_something_else()', - ], - keysPressed: "daI", - end: [ - '|', - ], - endMode: ModeName.Normal - }); -}); diff --git a/test/mode/modeReplace.test.ts b/test/mode/modeReplace.test.ts deleted file mode 100644 index 0ae221d2ffc..00000000000 --- a/test/mode/modeReplace.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -"use strict"; - -import { setupWorkspace, cleanUpWorkspace} from './../testUtils'; -import { ModeName } from '../../src/mode/mode'; -import { ModeHandler } from '../../src/mode/modeHandler'; -import { getTestingFunctions } from '../testSimplifier'; - -suite("Mode Replace", () => { - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - }); - - teardown(cleanUpWorkspace); - - newTest({ - title: "Can handle R", - start: ['123|456'], - keysPressed: 'Rab', - end: ["123ab|6"] - }); - - newTest({ - title: "Can handle R", - start: ['123|456'], - keysPressed: 'Rabcd', - end: ["123abcd|"] - }); - - newTest({ - title: "Can handle R and quit Replace Mode", - start: ['|123456'], - keysPressed: 'Rabc', - end: ["ab|c456"] - }); - - newTest({ - title: "Can handle R across lines", - start: ['123|456', '789'], - keysPressed: 'Rabcd\nefg', - end: ["123abcd", "efg|", "789"] - }); - - newTest({ - title: "Can handle R across lines and quit Replace Mode", - start: ['123|456', '789'], - keysPressed: 'Rabcd\nefg', - end: ["123abcd", "ef|g", "789"] - }); - - newTest({ - title: "Can handle R with {count}", - start: ['123|456', '789'], - keysPressed: '3Rabc\ndef', - end: ['123abc', 'defabc', 'defabc', 'de|f', '789'] - }); - - newTest({ - title: "Can handle backspace", - start: ['123|456'], - keysPressed: 'Rabc', - end: ["123|456"] - }); - - newTest({ - title: "Can handle backspace", - start: ['123|456'], - keysPressed: 'Rabcd', - end: ["12|3456"] - }); - - newTest({ - title: "Can handle backspace across lines", - start: ['123|456'], - keysPressed: 'Rabcd\nef', - end: ["123ab|6"] - }); - - newTest({ - title: "Can handle arrows", - start: ['123|456'], - keysPressed: 'Rabc', - end: ["123|abc"] - }); - - newTest({ - title: "Can handle .", - start: ['123|456', '123456'], - keysPressed: 'Rabcj0.', - end: ["123abc", "ab|c456"] - }); - - newTest({ - title: "Can handle . across lines", - start: ['123|456', '123456'], - keysPressed: 'Rabc\ndefj0.', - end: ["123abc", "def", "abc", "de|f"] - }); -}); \ No newline at end of file diff --git a/test/mode/modeVisual.test.ts b/test/mode/modeVisual.test.ts deleted file mode 100644 index 31c70bc7f34..00000000000 --- a/test/mode/modeVisual.test.ts +++ /dev/null @@ -1,947 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import { ModeHandler } from '../../src/mode/modeHandler'; -import { setupWorkspace, cleanUpWorkspace, assertEqualLines, assertEqual } from './../testUtils'; -import { ModeName } from '../../src/mode/mode'; -import { TextEditor } from '../../src/textEditor'; -import { getTestingFunctions } from '../testSimplifier'; -import { Configuration } from "../../src/configuration/configuration"; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("Mode Visual", () => { - let modeHandler: ModeHandler; - - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test("can be activated", async () => { - await modeHandler.handleKeyEvent('v'); - assertEqual(modeHandler.currentMode.name, ModeName.Visual); - - await modeHandler.handleKeyEvent('v'); - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - test("Can handle w", async () => { - await modeHandler.handleMultipleKeyEvents("itest test test\ntest\n".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', 'g', 'g', - 'v', 'w' - ]); - - const sel = TextEditor.getSelection(); - - assert.equal(sel.start.character, 0); - assert.equal(sel.start.line, 0); - - // The input cursor comes BEFORE the block cursor. Try it out, this - // is how Vim works. - assert.equal(sel.end.character, 6); - assert.equal(sel.end.line, 0); - }); - - test("Can handle wd", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'w', 'd' - ]); - - assertEqualLines(["wo three"]); - }); - - test("Can handle x", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'x' - ]); - - assertEqualLines(["ne two three"]); - - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - test("Can handle x across a selection", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'w', 'x' - ]); - - assertEqualLines(["wo three"]); - - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - test("Can do vwd in middle of sentence", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three foar".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'l', 'l', 'l', 'l', - 'v', 'w', 'd' - ]); - - assertEqualLines(["one hree foar"]); - }); - - test("Can do vwd in middle of sentence", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'l', 'l', 'l', 'l', - 'v', 'w', 'd' - ]); - - assertEqualLines(["one hree"]); - }); - - test("Can do vwd multiple times", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three four".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'w', 'd', - 'v', 'w', 'd', - 'v', 'w', 'd' - ]); - - assertEqualLines(["our"]); - }); - - test("handles case where we go from selecting on right side to selecting on left side", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'l', 'l', 'l', 'l', - 'v', 'w', 'b', 'b', 'd' - ]); - - assertEqualLines(["wo three"]); - }); - - newTest({ - title: "Can handle H key", - start: ['1', '2', '|3', '4', '5'], - keysPressed: 'vH', - end: ['|1', '2', '3', '4', '5'] - }); - - test("handles case where we delete over a newline", async () => { - await modeHandler.handleMultipleKeyEvents("ione two\n\nthree four".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '0', 'k', 'k', - 'v', '}', 'd' - ]); - - assertEqualLines(["three four"]); - }); - - test("handles change operator", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'w', 'c' - ]); - - assertEqualLines(["wo three"]); - assertEqual(modeHandler.currentMode.name, ModeName.Insert); - }); - - suite("Vim's EOL handling is weird", () => { - - test("delete through eol", async () => { - await modeHandler.handleMultipleKeyEvents( - 'ione\ntwo'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - '^', 'g', 'g', - 'v', 'l', 'l', 'l', - 'd' - ]); - - assertEqualLines(["two"]); - }); - - test("join 2 lines by deleting through eol", async () => { - await modeHandler.handleMultipleKeyEvents( - 'ione\ntwo'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'l', 'v', 'l', 'l', - 'd' - ]); - - assertEqualLines(["otwo"]); - }); - - test("d$ doesn't delete whole line", async () => { - await modeHandler.handleMultipleKeyEvents( - 'ione\ntwo'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'd', '$' - ]); - - assertEqualLines(["", "two"]); - }); - - test("vd$ does delete whole line", async () => { - await modeHandler.handleMultipleKeyEvents( - 'ione\ntwo'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'v', '$', 'd' - ]); - - assertEqualLines(["two"]); - }); - - newTest({ - title: "Paste over selection copies the selection", - start: ["|from to"], - keysPressed: "dewvep0P", - end: ["t|o from"] - }); - - newTest({ - title: "Paste over selection copies the selection linewise", - start: ["foo", "bar", "|fun"], - keysPressed: "viwykVkpp", - end: ["fun", "|foo", "bar", "fun"] - }); - - }); - - suite("Arrow keys work perfectly in Visual Mode", () => { - newTest({ - title: "Can handle key", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: 'vx', - end: ['blah', '|ur', 'hur'] - }); - - newTest({ - title: "Can handle key", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: 'vx', - end: ['blah', 'duh', '|ur'] - }); - - newTest({ - title: "Can handle key", - start: ['blah', 'duh', 'd|ur', 'hur'], - keysPressed: 'vx', - end: ['blah', 'duh', '|r', 'hur'] - }); - - newTest({ - title: "Can handle key", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: 'vx', - end: ['blah', 'duh', '|r', 'hur'] - }); - }); - - suite("handles aw in visual mode", () => { - newTest({ - title: "Can handle 'vawd' on word with cursor inside spaces", - start: ['one two | three, four '], - keysPressed: 'vawd', - end: ['one two|, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with trailing spaces", - start: ['one tw|o three, four '], - keysPressed: 'vawd', - end: ['one |three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with leading spaces", - start: ['one two th|ree, four '], - keysPressed: 'vawd', - end: ['one two|, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with numeric prefix", - start: ['on|e two three, four '], - keysPressed: 'v3awd', - end: ['|, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with numeric prefix and across lines", - start: ['one two three, fo|ur ', 'five six'], - keysPressed: 'v2awd', - end: ['one two three, |six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with numeric prefix and across lines, containing words end with `.`", - start: ['one two three, fo|ur ', 'five. six'], - keysPressed: 'v2awd', - end: ['one two three, |. six'], - endMode: ModeName.Normal - }); - }); - - suite("handles aW in visual mode", () => { - newTest({ - title: "Can handle 'vaWd' on big word with cursor inside spaces", - start: ['one two | three, four '], - keysPressed: 'vaWd', - end: ['one two| four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with trailing spaces", - start: ['one tw|o three, four '], - keysPressed: 'vaWd', - end: ['one |three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with leading spaces", - start: ['one two th|ree, four '], - keysPressed: 'vaWd', - end: ['one two |four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with numeric prefix", - start: ['on|e two three, four '], - keysPressed: 'v3aWd', - end: ['|four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with numeric prefix and across lines", - start: ['one two three, fo|ur ', 'five. six'], - keysPressed: 'v2aWd', - end: ['one two three, |six'], - endMode: ModeName.Normal - }); - }); - - suite("handles aW in visual mode", () => { - newTest({ - title: "Can handle 'vaWd' on big word with cursor inside spaces", - start: ['one two | three, four '], - keysPressed: 'vaWd', - end: ['one two| four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with trailing spaces", - start: ['one tw|o three, four '], - keysPressed: 'vaWd', - end: ['one |three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with leading spaces", - start: ['one two th|ree, four '], - keysPressed: 'vaWd', - end: ['one two |four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with numeric prefix", - start: ['on|e two three, four '], - keysPressed: 'v3aWd', - end: ['|four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with numeric prefix and across lines", - start: ['one two three, fo|ur ', 'five. six'], - keysPressed: 'v2aWd', - end: ['one two three, |six'], - endMode: ModeName.Normal - }); - }); - - suite("handles aw in visual mode", () => { - newTest({ - title: "Can handle 'vawd' on word with cursor inside spaces", - start: ['one two | three, four '], - keysPressed: 'vawd', - end: ['one two|, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with trailing spaces", - start: ['one tw|o three, four '], - keysPressed: 'vawd', - end: ['one |three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with leading spaces", - start: ['one two th|ree, four '], - keysPressed: 'vawd', - end: ['one two|, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with numeric prefix", - start: ['on|e two three, four '], - keysPressed: 'v3awd', - end: ['|, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with numeric prefix and across lines", - start: ['one two three, fo|ur ', 'five six'], - keysPressed: 'v2awd', - end: ['one two three, |six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vawd' on word with numeric prefix and across lines, containing words end with `.`", - start: ['one two three, fo|ur ', 'five. six'], - keysPressed: 'v2awd', - end: ['one two three, |. six'], - endMode: ModeName.Normal - }); - }); - - suite("handles aW in visual mode", () => { - newTest({ - title: "Can handle 'vaWd' on big word with cursor inside spaces", - start: ['one two | three, four '], - keysPressed: 'vaWd', - end: ['one two| four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with trailing spaces", - start: ['one tw|o three, four '], - keysPressed: 'vaWd', - end: ['one |three, four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with leading spaces", - start: ['one two th|ree, four '], - keysPressed: 'vaWd', - end: ['one two |four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with numeric prefix", - start: ['on|e two three, four '], - keysPressed: 'v3aWd', - end: ['|four '], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'vaWd' on word with numeric prefix and across lines", - start: ['one two three, fo|ur ', 'five. six'], - keysPressed: 'v2aWd', - end: ['one two three, |six'], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'Y' in visual mode", - start: ['one', '|two'], - keysPressed: 'vwYP', - end: ['one', '|two', 'two'], - endMode: ModeName.Normal - }); - }); - - suite("handles as in visual mode", () => { - newTest({ - title: "Select sentence with trailing spaces in visual mode", - start: ["That's my sec|ret, Captain. I'm always angry."], - keysPressed: 'vlasd', - end: ["That's my sec|I'm always angry."], - endMode: ModeName.Normal - }); - - newTest({ - title: "Select sentence with leading spaces in visual mode", - start: ["That's my secret, Captain. I'm a|lways angry."], - keysPressed: 'vhasd', - end: ["That's my secret, Captain.|ways angry."], - endMode: ModeName.Normal - }); - - newTest({ - title: "Select multiple sentences in visual mode", - start: ["That's my secret, Captain. I|'m always angry."], - keysPressed: 'vhhasd', - end: ["|m always angry."], - endMode: ModeName.Normal - }); - }); - - suite("handles is in visual mode", () => { - newTest({ - title: "Select inner sentence with trailing spaces in visual mode", - start: ["That's my sec|ret, Captain. I'm always angry."], - keysPressed: 'vlisd', - end: ["That's my sec| I'm always angry."], - endMode: ModeName.Normal - }); - - newTest({ - title: "Select inner sentence with leading spaces in visual mode", - start: ["That's my secret, Captain. I'm a|lways angry."], - keysPressed: 'vhisd', - end: ["That's my secret, Captain. |ways angry."], - endMode: ModeName.Normal - }); - - newTest({ - title: "Select spaces between sentences in visual mode", - start: ["That's my secret, Captain. | I'm always angry."], - keysPressed: 'vhisd', - end: ["That's my secret, Captain.| I'm always angry."], - endMode: ModeName.Normal - }); - }); - - suite("handles tag blocks in visual mode", () => { - newTest({ - title: "Can do vit on a matching tag", - start: ["one he|llo two"], - keysPressed: "vitd", - end: ["one | two"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do vat on a matching tag", - start: ["one he|llo two"], - keysPressed: "vatd", - end: ["one | two"], - endMode: ModeName.Normal - }); - }); - - newTest({ - title: "Can do vi) on a matching parenthesis", - start: ["test(te|st)"], - keysPressed: "vi)d", - end: ["test(|)"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do va) on a matching parenthesis", - start: ["test(te|st);"], - keysPressed: "va)d", - end: ["test|;"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do va} on a matching bracket as first character", - start: ["1|{", "test", "}1"], - keysPressed: "va}d", - end: ["1|1"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do vi( on a matching bracket near first character", - start: ["test(()=>{", "|", "});"], - keysPressed: "vi(d", - end: ["test(|);"], - endMode: ModeName.Normal - }); - - suite("handles replace in visual mode", () => { - newTest({ - title: "Can do a single line replace", - start: ["one |two three four five"], - keysPressed: "vwwer1", - end: ["one |11111111111111 five"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do a multi line replace", - start: ["one |two three four five", "one two three four five"], - keysPressed: "vjer1", - end: ["one |1111111111111111111", "1111111 three four five"], - endMode: ModeName.Normal - }); - }); - - newTest({ - title: "Can do v_x to delete to first char", - start: ["", "test te|st test", ""], - keysPressed: "v_x", - end: ["", "|t test", ""], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do vg_x to delete to last char with no EOL", - start: ["", "test te|st test", ""], - keysPressed: "vg_x", - end: ["", "test t|e", ""], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do v3g_x to delete to last char with no EOL with count", - start: ["te|st", "test", "test", "test"], - keysPressed: "v3g_x", - end: ["t|e", "test"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do v$x to delete to last char including EOL", - start: ["", "test te|st test", ""], - keysPressed: "v$x", - end: ["", "test t|e"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do gv to reselect previous selection", - start: ["tes|ttest"], - keysPressed: "vlllgvd", - end: ["tes|est"], - endMode: ModeName.Normal - }); - - suite("D command will remove all selected lines", () => { - newTest({ - title: "D deletes all selected lines", - start: ["first line", "test| line1", "test line2", "second line"], - keysPressed: "vjD", - end: ["first line", "|second line"], - endMode: ModeName.Normal - }); - - newTest({ - title: "D deletes the current line", - start: ["first line", "test| line1", "second line"], - keysPressed: "vlllD", - end: ["first line", "|second line"], - endMode: ModeName.Normal - }); - }); - - suite("handles indent blocks in visual mode", () => { - newTest({ - title: "Can do vai", - start: [ - 'if foo > 3:', - ' log("foo is big")|', - ' foo = 3', - 'do_something_else()', - ], - keysPressed: "vaid", - end: [ - '|do_something_else()', - ], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do vii", - start: [ - 'if foo > 3:', - ' bar|', - ' if baz:', - ' foo = 3', - 'do_something_else()', - ], - keysPressed: "viid", - end: [ - 'if foo > 3:', - '|do_something_else()', - ], - endMode: ModeName.Normal - }); - - newTest({ - title: "Doesn't naively select the next line", - start: [ - 'if foo > 3:', - ' bar|', - 'if foo > 3:', - ' bar', - ], - keysPressed: "viid", - end: [ - 'if foo > 3:', - '|if foo > 3:', - ' bar', - ], - endMode: ModeName.Normal - }); - - newTest({ - title: "Searches backwards if cursor line is empty", - start: [ - 'if foo > 3:', - ' log("foo is big")', - '|', - ' foo = 3', - 'do_something_else()', - ], - keysPressed: "viid", - end: [ - 'if foo > 3:', - '|do_something_else()', - ], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do vaI", - start: [ - 'if foo > 3:', - ' log("foo is big")|', - ' foo = 3', - 'do_something_else()', - ], - keysPressed: "vaId", - end: [ - '|', - ], - endMode: ModeName.Normal - }); - }); - - suite("visualstar", () => { - let originalVisualstarValue = false; - - setup(() => { - originalVisualstarValue = Configuration.visualstar; - Configuration.visualstar = true; - }); - - teardown(() => { - Configuration.visualstar = originalVisualstarValue; - }); - - newTest({ - title: "Works with *", - start: [ - '|public modes = [ModeName.Visual', - 'public modes = [ModeName.VisualBlock', - 'public modes = [ModeName.VisualLine', - ], - // This is doing a few things: - // - select to the end of "Visual" - // - press "*", the cursor will go to the next line since it matches - // - press "n", the cursor will go to the last line since it matches - keysPressed: "2vfl*n", - end: [ - 'public modes = [ModeName.Visual', - 'public modes = [ModeName.VisualBlock', - '|public modes = [ModeName.VisualLine', - ], - endMode: ModeName.Normal - }); - - newTest({ - title: "Works with #", - start: [ - 'public modes = [ModeName.Visual', - 'public modes = [ModeName.VisualBlock', - '|public modes = [ModeName.VisualLine', - ], - // This is doing a few things: - // - select to the end of "Visual" - // - press "#", the cursor will go to the previous line since it matches - // - press "n", the cursor will go to the first line since it matches - keysPressed: "2vfl#n", - end: [ - '|public modes = [ModeName.Visual', - 'public modes = [ModeName.VisualBlock', - 'public modes = [ModeName.VisualLine', - ], - endMode: ModeName.Normal - }); - - }); - - suite("search works in visual mode", () => { - newTest({ - title: "Works with /", - start: ["f|oo", - "bar", - "fun", - "baz"], - keysPressed: "v/baz\nx", - end: ["f|az"] - }); - - newTest({ - title: "Works with ?", - start: ["foo", - "bar", - "fun", - "b|az"], - keysPressed: "v?foo\nx", - end: ["|z"] - }); - }); - - suite("X will delete linewise", () => { - newTest({ - title: "normal selection", - start: ["this is", - "the| best", - "test i have seen in", - "the world"], - keysPressed: "vjX", - end: ["this is", "|the world"] - }); - - newTest({ - title: "normal selection", - start: ["this is", - "the| best", - "test i have seen in", - "the world"], - keysPressed: "vj$X", - end: ["this is", "|the world"] - }); - }); - - suite("C will delete linewise", () => { - newTest({ - title: "normal selection", - start: ["this is", - "the| best", - "test i have seen in", - "the world"], - keysPressed: "vjC", - end: ["this is", "|", "the world"] - }); - - newTest({ - title: "normal selection", - start: ["this is", - "the| best", - "test i have seen in", - "the world"], - keysPressed: "vj$C", - end: ["this is", "|", "the world"] - }); - }); - - suite("R will delete linewise", () => { - newTest({ - title: "normal selection", - start: ["this is", - "the| best", - "test i have seen in", - "the world"], - keysPressed: "vjR", - end: ["this is", "|", "the world"] - }); - - newTest({ - title: "normal selection", - start: ["this is", - "the| best", - "test i have seen in", - "the world"], - keysPressed: "vj$R", - end: ["this is", "|", "the world"] - }); - }); - - suite("Linewise Registers will be inserted properly", () => { - newTest({ - title: "downward selection", - start: ["i ya|nked", - "this line", - "", - "1.line", - "a123456", - "b123456", - "2.line"], - keysPressed: "vjY4j3lvjllp", - end: ["i yanked", - "this line", - "", - "1.line", - "a12", - "|i yanked", - "this line", - "6", - "2.line"], - }); - - newTest({ - title: "upward selection", - start: ["i yanked", - "this| line", - "", - "1.line", - "a123456", - "b123456", - "2.line"], - keysPressed: "vkY4j3lvjllp", - end: ["i yanked", - "this line", - "", - "1.line", - "a12", - "|i yanked", - "this line", - "6", - "2.line"], - }); - }); -}); - diff --git a/test/mode/modeVisualBlock.test.ts b/test/mode/modeVisualBlock.test.ts deleted file mode 100644 index cba3f4da033..00000000000 --- a/test/mode/modeVisualBlock.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -"use strict"; - -import { setupWorkspace, cleanUpWorkspace, assertEqual } from './../testUtils'; -import { ModeName } from '../../src/mode/mode'; -import { ModeHandler } from '../../src/mode/modeHandler'; -import { getTestingFunctions } from '../testSimplifier'; - -import * as vscode from 'vscode'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("Mode Visual Block", () => { - let modeHandler: ModeHandler; - - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test("can be activated", async () => { - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - - await modeHandler.handleKeyEvent(''); - assertEqual(modeHandler.currentMode.name, ModeName.VisualBlock); - - await modeHandler.handleKeyEvent(''); - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - newTest({ - title: "Can handle A forward select", - start: ['|test', 'test'], - keysPressed: 'lljA123', - end: ['tes123|t', 'tes123t'], - }); - - newTest({ - title: "Can handle A backwards select", - start: ['tes|t', 'test'], - keysPressed: 'hhjA123', - end: ['tes123|t', 'tes123t'], - }); - - newTest({ - title: "Can handle I forward select", - start: ['|test', 'test'], - keysPressed: 'lljI123', - end: ['t123|est', 't123est'], - }); - - newTest({ - title: "Can handle I backwards select", - start: ['tes|t', 'test'], - keysPressed: 'hhjI123', - end: ['t123|est', 't123est'], - }); - - newTest({ - title: "Can handle I with empty lines on first character (inserts on empty line)", - start: ['|test', '', 'test'], - keysPressed: 'lljjI123', - end: ['123|test', '123', '123test'], - }); - - newTest({ - title: "Can handle I with empty lines on non-first character (does not insert on empty line)", - start: ['t|est', '', 'test'], - keysPressed: 'lljjI123', - end: ['t123|est', '', 't123est'], - }); - - newTest({ - title: "Can handle c forward select", - start: ['|test', 'test'], - keysPressed: 'lljc123', - end: ['t123|t', 't123t'], - }); - - newTest({ - title: "Can handle c backwards select", - start: ['tes|t', 'test'], - keysPressed: 'hhjc123', - end: ['t123|t', 't123t'], - }); - - newTest({ - title: "Can handle C", - start: ['tes|t', 'test'], - keysPressed: 'hhjC123', - end: ['t123|', 't123'], - }); - - newTest({ - title: "Can do a multi line replace", - start: ["one |two three four five", "one two three four five"], - keysPressed: "jeer1", - end: ["one |111111111 four five", "one 111111111 four five"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can handle 'D'", - start: ['tes|t', 'test'], - keysPressed: 'hjD', - end: ['t|e', 'te'], - }); - -}); diff --git a/test/mode/modeVisualLine.test.ts b/test/mode/modeVisualLine.test.ts deleted file mode 100644 index 3f92589ef7d..00000000000 --- a/test/mode/modeVisualLine.test.ts +++ /dev/null @@ -1,352 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import { ModeHandler } from '../../src/mode/modeHandler'; -import { setupWorkspace, cleanUpWorkspace, assertEqualLines, assertEqual } from './../testUtils'; -import { ModeName } from '../../src/mode/mode'; -import { TextEditor } from '../../src/textEditor'; -import { getTestingFunctions } from '../testSimplifier'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("Mode Visual", () => { - let modeHandler: ModeHandler; - - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test("can be activated", async () => { - await modeHandler.handleKeyEvent('v'); - assertEqual(modeHandler.currentMode.name, ModeName.Visual); - - await modeHandler.handleKeyEvent('v'); - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - test("Can handle w", async () => { - await modeHandler.handleMultipleKeyEvents("itest test test\ntest\n".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', 'g', 'g', - 'v', 'w' - ]); - - const sel = TextEditor.getSelection(); - - assert.equal(sel.start.character, 0); - assert.equal(sel.start.line, 0); - - // The input cursor comes BEFORE the block cursor. Try it out, this - // is how Vim works. - assert.equal(sel.end.character, 6); - assert.equal(sel.end.line, 0); - }); - - test("Can handle wd", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'w', 'd' - ]); - - assertEqualLines(["wo three"]); - }); - - test("Can handle x", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'x' - ]); - - assertEqualLines(["ne two three"]); - - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - test("Can handle U", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'U' - ]); - - assertEqualLines(["One two three"]); - - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - test("Can handle x across a selection", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'w', 'x' - ]); - - assertEqualLines(["wo three"]); - - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - test("Can do vwd in middle of sentence", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three foar".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'l', 'l', 'l', 'l', - 'v', 'w', 'd' - ]); - - assertEqualLines(["one hree foar"]); - }); - - test("Can do vwd in middle of sentence", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'l', 'l', 'l', 'l', - 'v', 'w', 'd' - ]); - - assertEqualLines(["one hree"]); - }); - - test("Can do vwd multiple times", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three four".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'w', 'd', - 'v', 'w', 'd', - 'v', 'w', 'd' - ]); - - assertEqualLines(["our"]); - }); - - test("Can handle U across a selection", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'l', 'l', 'l', 'l', 'U' - ]); - - assertEqualLines(["ONE Two three"]); - - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - test("Can handle U across a selection in reverse order", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'w', 'v', 'h', 'h', 'U' - ]); - - assertEqualLines(["onE Two three"]); - - assertEqual(modeHandler.currentMode.name, ModeName.Normal); - }); - - test("handles case where we go from selecting on right side to selecting on left side", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'l', 'l', 'l', 'l', - 'v', 'w', 'b', 'b', 'd' - ]); - - assertEqualLines(["wo three"]); - }); - - test("handles case where we delete over a newline", async () => { - await modeHandler.handleMultipleKeyEvents("ione two\n\nthree four".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '0', 'k', 'k', - 'v', '}', 'd' - ]); - - assertEqualLines(["three four"]); - }); - - test("handles change operator", async () => { - await modeHandler.handleMultipleKeyEvents("ione two three".split("")); - await modeHandler.handleMultipleKeyEvents([ - '', '^', - 'v', 'w', 'c' - ]); - - assertEqualLines(["wo three"]); - assertEqual(modeHandler.currentMode.name, ModeName.Insert); - }); - - suite("Vim's EOL handling is weird", () => { - - test("delete through eol", async () => { - await modeHandler.handleMultipleKeyEvents( - 'ione\ntwo'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - '^', 'g', 'g', - 'v', 'l', 'l', 'l', - 'd' - ]); - - assertEqualLines(["two"]); - }); - - test("join 2 lines by deleting through eol", async () => { - await modeHandler.handleMultipleKeyEvents( - 'ione\ntwo'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'l', 'v', 'l', 'l', - 'd' - ]); - - assertEqualLines(["otwo"]); - }); - - test("d$ doesn't delete whole line", async () => { - await modeHandler.handleMultipleKeyEvents( - 'ione\ntwo'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'd', '$' - ]); - - assertEqualLines(["", "two"]); - }); - - test("vd$ does delete whole line", async () => { - await modeHandler.handleMultipleKeyEvents( - 'ione\ntwo'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'v', '$', 'd' - ]); - - assertEqualLines(["two"]); - }); - - }); - - suite("Arrow keys work perfectly in Visual Line Mode", () => { - newTest({ - title: "Can handle key", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: 'Vx', - end: ['blah', '|hur'] - }); - - newTest({ - title: "Can handle key", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: 'Vx', - end: ['blah', '|duh'] - }); - }); - - suite("Can handle d/c correctly in Visual Line Mode", () => { - newTest({ - title: "Can handle d key", - start: ['|{', ' a = 1;', '}'], - keysPressed: 'VGdp', - end: ['', '|{', ' a = 1;', '}'] - }); - - newTest({ - title: "Can handle d key", - start: ['|{', ' a = 1;', '}'], - keysPressed: 'VGdP', - end: ['|{', ' a = 1;', '}', ''] - }); - - newTest({ - title: "Can handle d key", - start: ['1', '2', '|{', ' a = 1;', '}'], - keysPressed: 'VGdp', - end: ['1', '2', '|{', ' a = 1;', '}'] - }); - - newTest({ - title: "Can handle d key", - start: ['1', '2', '|{', ' a = 1;', '}'], - keysPressed: 'VGdP', - end: ['1', '|{', ' a = 1;', '}', '2'] - }); - - newTest({ - title: "can handle 'c'", - start: ['foo', 'b|ar', 'fun'], - keysPressed: 'Vc', - end: ['foo', '|', 'fun'], - endMode: ModeName.Insert - }); - }); - - suite("handles replace in visual line mode", () => { - newTest({ - title: "Can do a single line replace", - start: ["one |two three four five", "one two three four five"], - keysPressed: "Vr1", - end: ["|11111111111111111111111", "one two three four five"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do a multi visual line replace", - start: ["one |two three four five", "one two three four five"], - keysPressed: "Vjr1", - end: ["|11111111111111111111111", "11111111111111111111111"], - endMode: ModeName.Normal - }); - - newTest({ - title: "Can do a multi visual line replace from the bottom up", - start: ["test", "test", "test", "|test", "test"], - keysPressed: "Vkkr1", - end: ["test", "|1111", "1111", "1111", "test"], - endMode: ModeName.Normal - }); - }); - - suite("search works in visual line mode", () => { - newTest({ - title: "Works with /", - start: ["f|oo", - "bar", - "fun", - "baz"], - keysPressed: "V/fun\nx", - end: ["|baz"] - }); - - newTest({ - title: "Works with ?", - start: ["foo", - "bar", - "fun", - "b|az"], - keysPressed: "V?bar\nx", - end: ["|foo"] - }); - }); - -}); diff --git a/test/mode/normalModeTests/commands.test.ts b/test/mode/normalModeTests/commands.test.ts deleted file mode 100644 index cd694e96772..00000000000 --- a/test/mode/normalModeTests/commands.test.ts +++ /dev/null @@ -1,276 +0,0 @@ -"use strict"; - -import { setupWorkspace, cleanUpWorkspace } from './../../testUtils'; -import { ModeName } from '../../../src/mode/mode'; -import { ModeHandler } from '../../../src/mode/modeHandler'; -import { getTestingFunctions } from '../../testSimplifier'; - -suite("Mode Normal", () => { - let { - newTest, - newTestOnly - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - }); - - teardown(cleanUpWorkspace); - - newTest({ - title: "Can handle 'x'", - start: ['te|xt'], - keysPressed: 'x', - end: ["te|t"], - }); - - newTest({ - title: "Can handle 'Nx'", - start: ['te|xt'], - keysPressed: '2x', - end: ["t|e"], - }); - - newTest({ - title: "Can handle 'x' at end of line", - start: ['one tw|o'], - keysPressed: '^llxxxxxxxxx', - end: ['|'], - }); - - newTest({ - title: "Can handle 'Ns'", - start: ['|text'], - keysPressed: '3s', - end: ['|t'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'Ns' at end of line", - start: ['te|xt'], - keysPressed: '3s', - end: ['te|'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle ''", - start: ['te|xt'], - keysPressed: '', - end: ["te|t"], - }); - - newTest({ - title: "Can handle '' with counts, which removes the last character of the count", - start: ['|text'], - keysPressed: '10x', - end: ["|ext"], - }); - - newTest({ - title: "Can handle '' at end of line", - start: ['one tw|o'], - keysPressed: '^ll', - end: ['|'], - }); - - newTest({ - title: "Can handle 'cc'", - start: ['one', '|one two', 'three'], - keysPressed: 'cca', - end: ["one", "|a", "three"], - }); - - newTest({ - title: "Can handle 'Ncc'", - start: ['one', '|one two', 'three four', 'five'], - keysPressed: '2cca', - end: ["one", "|a", "five"] - }); - - newTest({ - title: "Can handle 'yy'", - start: ['|one'], - keysPressed: 'yyOp', - end: ["", "|one", "one"], - }); - - newTest({ - title: "Can handle 'D'", - start: ['tex|t'], - keysPressed: '^llD', - end: ['t|e'], - }); - - newTest({ - title: "Can handle 'D' on empty lines", - start: ['text', '|', 'text'], - keysPressed: 'D', - end: ['text', '|', 'text'] - }); - - newTest({ - title: "Can handle 'DD'", - start: ['tex|t'], - keysPressed: '^llDD', - end: ['|t'], - }); - - newTest({ - title: "Can handle 'C'", - start: ['tex|t'], - keysPressed: '^llC', - end: ['te|'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'NC'", - start: ['tex|t', 'one', 'two'], - keysPressed: '^ll2C', - end: ['te|', 'two'], - endMode: ModeName.Insert - }); - - newTest({ - title: "Can handle 'r'", - start: ['tex|t'], - keysPressed: 'hrs', - end: ['te|st'] - }); - - newTest({ - title: "Can handle 'r'", - start: ['123|456', '789'], - keysPressed: '2ra', - end: ['123a|a6', '789'] - }); - - newTest({ - title: "Can handle 'r'", - start: ['123|456', '789'], - keysPressed: '4ra', - end: ['123|456', '789'] - }); - - newTest({ - title: "Can handle 'r' after 'dd'", - start: ['one', 'two', 'thre|e'], - keysPressed: 'kddrT', - end: ['one', '|Three'] - }); - - newTest({ - title: "Can handle 'J' once", - start: ['one', 'tw|o'], - keysPressed: 'kJ', - end: ['one| two'] - }); - - newTest({ - title: "Can handle 'J' twice", - start: ['one', 'two', 'thre|e'], - keysPressed: 'kkJJ', - end: ['one two| three'] - }); - - newTest({ - title: "Can handle 'J' with empty last line", - start: ['one', 'two', '|'], - keysPressed: 'kJ', - end: ['one', 'two| '] - }); - - newTest({ - title: "Can handle 'J's with multiple empty last lines", - start: ['one', 'two', '', '', '', '|'], - keysPressed: 'kkkkkJJJJJ', - end: ['one two| '] - }); - - newTest({ - title: "Can handle 'J' with leading white space on next line", - start: ['on|e', ' two'], - keysPressed: 'kJ', - end: ['one| two'] - }); - - newTest({ - title: "Can handle 'J' with only white space on next line", - start: ['on|e', ' '], - keysPressed: 'J', - end: ['one| '] - }); - - newTest({ - title: "Can handle 'J' with TWO indented lines", - start: [' on|e', ' two'], - keysPressed: 'kJ', - end: [' one| two'] - }); - - newTest({ - title: "Can handle 'J' with ')' first character on next line", - start: ['one(', ')tw|o'], - keysPressed: 'kJ', - end: ['one(|)two'] - }); - - newTest({ - title: "Can handle 'J' with a following delete", - start: ['on|e', 'two'], - keysPressed: 'Jx', - end: ['one|two'] - }); - - newTest({ - title: "Can handle 'J' in Visual Line mode", - start: ['on|e', 'two'], - keysPressed: 'VJ', - end: ['one| two'] - }); - - newTest({ - title: "Can handle 'gJ' once", - start: ['|one', 'two'], - keysPressed: 'kgJ', - end: ['one|two'] - }); - - newTest({ - title: "Can handle 'gJ' once and ALL WHITESPACE IS ELIMINATED", - start: ['|one', ' two'], - keysPressed: 'kgJ', - end: ['one|two'] - }); - - newTest({ - title: "Can handle '~'", - start: ['|text'], - keysPressed: '~', - end: ['T|ext'] - }); - - newTest({ - title: "Can handle 'g~{motion}'", - start: ['|one two'], - keysPressed: 'g~w', - end: ['|ONE two'] - }); - - newTest({ - title: "Can handle '' in insert mode", - start: ['one', '|'], - keysPressed: 'i', - end: ['on|e'] - }); - - newTest({ - title: "Can handle undo with P", - start: ['one', '|two', 'three'], - keysPressed: 'ddkPjddu', - end: ['two', '|one', 'three'], - }); - -}); diff --git a/test/mode/normalModeTests/dot.test.ts b/test/mode/normalModeTests/dot.test.ts deleted file mode 100644 index cf7af56655b..00000000000 --- a/test/mode/normalModeTests/dot.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -"use strict"; - -import * as vscode from "vscode"; -import { setupWorkspace, cleanUpWorkspace, setTextEditorOptions, assertEqualLines } from './../../testUtils'; -import { ModeHandler } from '../../../src/mode/modeHandler'; -import { waitForTabChange } from '../../../src/util'; -import * as assert from 'assert'; -import { getTestingFunctions } from '../../testSimplifier'; -import { getAndUpdateModeHandler } from "../../../extension"; - -suite("Dot Operator", () => { - let modeHandler: ModeHandler; - - let { - newTest, - newTestOnly - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - setTextEditorOptions(4, false); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test('repeats actions across editors ', async () => { - // setting the content of the first 2 tabs - const firstTabContent = 'some\ntest\nabc\nend'; - const secondTabContent = 'another\ntest\ndef\nend'; - const firstTabKeys = ['', 'a'].concat(firstTabContent.split('')); - const secondTabKeys = ['', 'a'].concat(secondTabContent.split('')); - await setupWorkspace(); - setTextEditorOptions(5, false); - - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - - await modeHandler.handleMultipleKeyEvents(firstTabKeys.concat([''])); - - await modeHandler.handleMultipleKeyEvents(['', 'g', 'T']); - await waitForTabChange(); - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - await modeHandler.handleMultipleKeyEvents(secondTabKeys.concat([''])); - - // running an action in second tab and repeating in first tab - await modeHandler.handleMultipleKeyEvents(['g', 'g', 'd' , 'd']); - await assertEqualLines(['test', 'def', 'end']); - await modeHandler.handleMultipleKeyEvents(['g', 't']); - await waitForTabChange(); - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - await modeHandler.handleMultipleKeyEvents(['', 'g', 'g', '.']); - await assertEqualLines(['test', 'abc', 'end']); - }); - - newTest({ - title: "Can repeat '~' with ", - start: ['|teXt'], - keysPressed: '4~', - end: ['TEx|T'] - }); - - newTest({ - title: "Can repeat '~' with dot", - start: ['|teXt'], - keysPressed: '~...', - end: ['TEx|T'] - }); - - newTest({ - title: "Can repeat 'x'", - start: ['|text'], - keysPressed: 'x.', - end: ['|xt'] - }); - - newTest({ - title: "Can repeat 'J'", - start: ['|one', 'two', 'three'], - keysPressed: 'J.', - end: ['one two| three'] - }); - - newTest({ - title: "Can handle dot with A", - start: ['|one', 'two', 'three'], - keysPressed: 'A!j.j.', - end: ['one!', 'two!', 'three|!'] - }); - - newTest({ - title: "Can handle dot with I", - start: ['on|e', 'two', 'three'], - keysPressed: 'I!j.j.', - end: ['!one', '!two', '|!three'] - }); - - newTest({ - title: "Can repeat actions that require selections", - start: ['on|e', 'two'], - keysPressed: 'Vj>.', - end: ['\t\t|one', '\t\ttwo'] - }); -}); - -suite("Repeat content change", () => { - let { - newTest, - newTestOnly - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - setTextEditorOptions(4, false); - }); - - teardown(cleanUpWorkspace); - - newTest({ - title: "Can repeat ''", - start: ['on|e', 'two'], - keysPressed: 'aj.', - end: ['\tone', '\ttw|o'] - }); - - newTest({ - title: "Can repeat insert change and ''", - start: ['on|e', 'two'], - keysPressed: 'abj.', - end: ['\toneb', '\ttwo|b'] - }); - - newTest({ - title: "Can repeat change by ``", - start: ['on|e', 'two'], - keysPressed: 'abja', - end: ['\toneb', '\ttwo|b'] - }); - - newTest({ - title: "Only one arrow key can be repeated in Insert Mode", - start: ['on|e', 'two'], - keysPressed: 'abj$.', - end: ['obne', 'tw|bo'] - }); - - newTest({ - title: "Cached content change will be cleared by arrow keys", - start: ['on|e', 'two'], - keysPressed: 'abcj.', - end: ['\tonecb', 'tw|co'] - }); -}); \ No newline at end of file diff --git a/test/mode/normalModeTests/motions.test.ts b/test/mode/normalModeTests/motions.test.ts deleted file mode 100644 index b9b7a449060..00000000000 --- a/test/mode/normalModeTests/motions.test.ts +++ /dev/null @@ -1,679 +0,0 @@ -"use strict"; - -import { setupWorkspace, cleanUpWorkspace } from './../../testUtils'; -import { ModeHandler } from '../../../src/mode/modeHandler'; -import { getTestingFunctions, testIt } from '../../testSimplifier'; -import { waitForTabChange } from '../../../src/util'; -import { getAndUpdateModeHandler } from "../../../extension"; - -suite("Motions in Normal Mode", () => { - let modeHandler: ModeHandler; - - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - newTest({ - title: "Can handle %", - start: ['|((( )))'], - keysPressed: '%', - end: ["((( ))|)"], - }); - - newTest({ - title: "Can handle %", - start: ['((( ))|)'], - keysPressed: '%', - end: ["|((( )))"], - }); - - newTest({ - title: "Can handle %", - start: ['|[(( ))]'], - keysPressed: '%', - end: ["[(( ))|]"], - }); - - newTest({ - title: "Can handle %", - start: ['|[(( }}} ))]'], - keysPressed: '%', - end: ["[(( }}} ))|]"], - }); - - newTest({ - title: "Can handle %", - start: ['|[(( }}} ))]'], - keysPressed: '%', - end: ["[(( }}} ))|]"], - }); - - newTest({ - title: "Can handle %", - start: ['[(( }}} ))|]'], - keysPressed: '%', - end: ["|[(( }}} ))]"], - }); - - newTest({ - title: "Can handle [(", - start: ['({|})'], - keysPressed: '[(', - end: ['|({})'] - }); - - newTest({ - title: "Can handle nested [(", - start: ['(({|})'], - keysPressed: '[(', - end: ['(|({})'] - }); - - newTest({ - title: "Can handle [(", - start: ['(({|})'], - keysPressed: '2[(', - end: ['|(({})'] - }); - - newTest({ - title: "Can handle [( and character under cursor exclusive", - start: ['(|({})'], - keysPressed: '[(', - end: ['|(({})'] - }); - - newTest({ - title: "Can handle ])", - start: ['({|})'], - keysPressed: '])', - end: ['({}|)'] - }); - - newTest({ - title: "Can handle nested ])", - start: ['(({|}))'], - keysPressed: '])', - end: ['(({}|))'] - }); - - newTest({ - title: "Can handle ])", - start: ['(({|}))'], - keysPressed: '2])', - end: ['(({})|)'] - }); - - newTest({ - title: "Can handle ]) and character under cursor exclusive", - start: ['(({}|))'], - keysPressed: '])', - end: ['(({})|)'] - }); - - newTest({ - title: "Can handle [{", - start: ['{(|)}'], - keysPressed: '[{', - end: ['|{()}'] - }); - - newTest({ - title: "Can handle nested [{", - start: ['{{(|)}'], - keysPressed: '[{', - end: ['{|{()}'] - }); - - newTest({ - title: "Can handle [{", - start: ['{{(|)}'], - keysPressed: '2[{', - end: ['|{{()}'] - }); - - newTest({ - title: "Can handle [{ and character under cursor exclusive", - start: ['{|{()}'], - keysPressed: '[{', - end: ['|{{()}'] - }); - - newTest({ - title: "Can handle ]}", - start: ['{(|)}'], - keysPressed: ']}', - end: ['{()|}'] - }); - - newTest({ - title: "Can handle nested ]}", - start: ['{{(|)}}'], - keysPressed: ']}', - end: ['{{()|}}'] - }); - - newTest({ - title: "Can handle ]}", - start: ['{{(|)}}'], - keysPressed: '2]}', - end: ['{{()}|}'] - }); - - newTest({ - title: "Can handle ]} and character under cursor exclusive", - start: ['{{()|}}'], - keysPressed: ']}', - end: ['{{()}|}'] - }); - - newTest({ - title: "Can handle 'ge'", - start: ['text tex|t'], - keysPressed: '$ge', - end: ['tex|t text'], - }); - - newTest({ - title: "Can handle 'gg'", - start: ['text', 'text', 'tex|t'], - keysPressed: '$jkjgg', - end: ['|text', 'text', 'text'], - }); - - newTest({ - title: "Can handle 'gg' to first non blank char on random line", - start: [' te|xt', ' text', ' text', 'test'], - keysPressed: '3gg', - end: [' text', ' text', ' |text', 'test'], - }); - - newTest({ - title: "Can handle 'gg' to first non blank char on first line", - start: [' text', 'text', 'tex|t'], - keysPressed: 'gg', - end: [' |text', 'text', 'text'], - }); - - newTest({ - title: "Retain same column when moving up/down", - start: ['text text', 'text', 'text tex|t'], - keysPressed: 'kk', - end: ['text tex|t', 'text', 'text text'], - }); - - newTest({ - title: "Can handle ", - start: ['text te|xt', 'text'], - keysPressed: '\n', - end: ['text text', '|text'] - }); - - newTest({ - title: "$ always keeps cursor on EOL", - start: ['text text', 'text', 'text tex|t'], - keysPressed: 'gg$jj', - end: ['text text', 'text', 'text tex|t'], - }); - - newTest({ - title: "Can handle 'f'", - start: ['text tex|t'], - keysPressed: '^ft', - end: ['tex|t text'] - }); - - newTest({ - title: "Can handle 'f' twice", - start: ['text tex|t'], - keysPressed: '^ftft', - end: ['text |text'] - }); - - newTest({ - title: "Can handle 'F'", - start: ['text tex|t'], - keysPressed: '$Ft', - end: ['text |text'] - }); - - newTest({ - title: "Can handle 'F' twice", - start: ['text tex|t'], - keysPressed: '$FtFt', - end: ['tex|t text'] - }); - - newTest({ - title: "Can handle 't'", - start: ['text tex|t'], - keysPressed: '^tt', - end: ['te|xt text'] - }); - - newTest({ - title: "Can handle 't' twice", - start: ['text tex|t'], - keysPressed: '^tttt', - end: ['te|xt text'] - }); - - newTest({ - title: "Can handle 'T'", - start: ['text tex|t'], - keysPressed: '$Tt', - end: ['text t|ext'] - }); - - newTest({ - title: "Can handle 'T' twice", - start: ['text tex|t'], - keysPressed: '$TtTt', - end: ['text t|ext'] - }); - - newTest({ - title: "Can run a forward search", - start: ['|one two three'], - keysPressed: '/thr\n', - end: ['one two |three'], - }); - - newTest({ - title: "Can run a forward and find next search", - start: ['|one two two two'], - keysPressed: '/two\nn', - end: ['one two |two two'], - }); - - test('Remembers a forward search from another editor', async function() { - // adding another editor - await setupWorkspace(); - - await testIt(modeHandler, { - title: "", - start: ['|one two two two'], - keysPressed: '/two\n', - end: ['one |two two two'], - }); - - await modeHandler.handleMultipleKeyEvents(['g', 'T', '']); - - await waitForTabChange(); - - await testIt(modeHandler, { - title: "", - start: ['|three four two one'], - keysPressed: 'n', - end: ['three four |two one'], - }); - }); - - test('Shares forward search history from another editor', async () => { - // adding another editor - await setupWorkspace(); - - await testIt(modeHandler, { - title: "", - start: ['|one two two two'], - keysPressed: '/two\n', - end: ['one |two two two'], - }); - - await modeHandler.handleMultipleKeyEvents(['g', 'T', '']); - - await waitForTabChange(); - - await testIt(modeHandler, { - title: "", - start: ['|three four two one'], - keysPressed: '/\n', - end: ['three four |two one'], - }); - }); - - newTest({ - title: "Can run a reverse search", - start: ['one two thre|e'], - keysPressed: '?two\n', - end: ['one |two three'], - }); - - newTest({ - title: "Can run a reverse and find next search", - start: ['one two two thre|e'], - keysPressed: '?two\nn', - end: ['one |two two three'], - }); - - test('Remembers a reverse search from another editor', async () => { - // adding another editor - await setupWorkspace(); - - await testIt(modeHandler, { - title: "", - start: ['one two two two|'], - keysPressed: '?two\n', - end: ['one two two |two'], - }); - - await modeHandler.handleMultipleKeyEvents(['g', 'T', '']); - - await waitForTabChange(); - - await testIt(modeHandler, { - title: "", - start: ['three four two one|'], - keysPressed: 'n', - end: ['three four |two one'], - }); - }); - - test('Shares reverse search history from another editor', async () => { - // adding another editor - await setupWorkspace(); - - await testIt(modeHandler, { - title: "", - start: ['one two two two|'], - keysPressed: '?two\n', - end: ['one two two |two'], - }); - - await modeHandler.handleMultipleKeyEvents(['g', 'T', '']); - - await waitForTabChange(); - - await testIt(modeHandler, { - title: "", - start: ['three four two one|'], - keysPressed: '?\n', - end: ['three four |two one'], - }); - }); - - - newTest({ - title: "maintains column position correctly", - start: ['|one one one', 'two', 'three'], - keysPressed: 'lllljj', - end: ['one one one', 'two', 'thre|e'], - }); - - newTest({ - title: "maintains column position correctly with $", - start: ['|one one one', 'two', 'three'], - keysPressed: '$jj', - end: ['one one one', 'two', 'thre|e'], - }); - - newTest({ - title: "Can handle G ", - start: ['|one', 'two', 'three'], - keysPressed: 'G', - end: ['one', 'two', '|three'] - }); - - newTest({ - title: "Can handle G with number prefix", - start: ['|one', 'two', 'three'], - keysPressed: '2G', - end: ['one', '|two', 'three'] - }); - - newTest({ - title: "Can handle G with number prefix", - start: ['|one', 'two', 'three'], - keysPressed: '5G', - end: ['one', 'two', '|three'] - }); - - newTest({ - title: "Can handle gg", - start: ['one', '|two', 'three'], - keysPressed: 'gg', - end: ['|one', 'two', 'three'] - }); - - newTest({ - title: "Can handle gg with number prefix", - start: ['|one', 'two', 'three'], - keysPressed: '2gg', - end: ['one', '|two', 'three'] - }); - - newTest({ - title: "Can handle dot with A", - start: ['|one', 'two', 'three'], - keysPressed: 'A!j.j.', - end: ['one!', 'two!', 'three|!'] - }); - - newTest({ - title: "Can handle dot with I", - start: ['on|e', 'two', 'three'], - keysPressed: 'I!j.j.', - end: ['!one', '!two', '|!three'] - }); - - // This is still currently not possible. - // newTest({ - // title: "Can handle dot with I with tab", - // start: ['on|e', 'two', 'three'], - // keysPressed: 'Ij.j.', - // end: [' one', ' two', ' | three'] - // }); - - newTest({ - title: "Can handle 0", - start: ['blah blah bla|h'], - keysPressed: '0', - end: ['|blah blah blah'] - }); - - newTest({ - title: "Can handle 0 as part of a repeat", - start: ['|blah blah blah'], - keysPressed: '10l', - end: ['blah blah |blah'] - }); - - newTest({ - title: "Can handle g*", - start: ['|blah duh blahblah duh blah'], - keysPressed: 'g*', - end: ['blah duh |blahblah duh blah'] - }); - - newTest({ - title: "Can handle g*n", - start: ['|blah duh blahblah duh blah'], - keysPressed: 'g*n', - end: ['blah duh blah|blah duh blah'] - }); - - - newTest({ - title: "Can handle *", - start: ['|blah blahblah duh blah blah'], - keysPressed: '*', - end: ['blah blahblah duh |blah blah'] - }); - - newTest({ - title: "Can handle **", - start: ['|blah duh blah duh blah'], - keysPressed: '**', - end: ['blah duh blah duh |blah'] - }); - - newTest({ - title: "Can handle # on whitespace", - start: ['abc abcdef| abc'], - keysPressed: '#', - end: ['|abc abcdef abc'], - }); - - newTest({ - title: "Can handle # on EOL", - start: ['abc abcdef abc| '], - keysPressed: '#', - end: ['abc abcdef abc| '], - }); - - newTest({ - title: "Can handle g#", - start: ['blah duh blahblah duh |blah'], - keysPressed: 'g#', - end: ['blah duh blah|blah duh blah'] - }); - - newTest({ - title: "Can handle g#n", - start: ['blah duh blahblah duh |blah'], - keysPressed: 'g#n', - end: ['blah duh |blahblah duh blah'] - }); - - newTest({ - title: "Can handle #", - start: ['blah blah blahblah duh |blah'], - keysPressed: '#', - end: ['blah |blah blahblah duh blah'] - }); - - newTest({ - title: "Can handle # already on the word", - start: ['one o|ne'], - keysPressed: '#', - end: ['|one one'] - }); - - newTest({ - title: "Can handle ##", - start: ['blah duh blah duh |blah'], - keysPressed: '##', - end: ['|blah duh blah duh blah'] - }); - - newTest({ - title: "Can handle |", - start: ['blah duh blah duh |blah'], - keysPressed: '|', - end: ['|blah duh blah duh blah'] - }); - - newTest({ - title: "Can handle |", - start: ['blah duh blah duh |blah'], - keysPressed: '3|', - end: ['bl|ah duh blah duh blah'] - }); - - newTest({ - title: "Can handle +", - start: ['|blah', 'duh'], - keysPressed: '+', - end: ['blah', '|duh'] - }); - - newTest({ - title: "Can handle + indent", - start: ['|blah', ' duh'], - keysPressed: '+', - end: ['blah', ' |duh'] - }); - - newTest({ - title: "Can handle + with count prefix", - start: ['|blah', 'duh', 'dur', 'hur'], - keysPressed: '2+', - end: ['blah', 'duh', '|dur', 'hur'] - }); - - - newTest({ - title: "Can handle -", - start: ['blah', '|duh'], - keysPressed: '-', - end: ['|blah', 'duh'] - }); - - newTest({ - title: "Can handle - indent", - start: [' blah', '|duh'], - keysPressed: '-', - end: [' |blah', 'duh'] - }); - - newTest({ - title: "Can handle - with count prefix", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: '2-', - end: ['|blah', 'duh', 'dur', 'hur'] - }); - - newTest({ - title: "Can handle _", - start: ['blah', '|duh'], - keysPressed: '_', - end: ['blah', '|duh'] - }); - - newTest({ - title: "Can handle _ with count prefix", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: '2_', - end: ['blah', 'duh', 'dur', '|hur'] - }); - - newTest({ - title: "Can handle g_", - start: ['blah', '|duh'], - keysPressed: 'g_', - end: ['blah', 'du|h'] - }); - - newTest({ - title: "Can handle g_ with count prefix", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: '2g_', - end: ['blah', 'duh', 'dur', 'hu|r'] - }); - - newTest({ - title: "Can handle key", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: '', - end: ['blah', '|duh', 'dur', 'hur'] - }); - - newTest({ - title: "Can handle key", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: '', - end: ['blah', 'duh', 'dur', '|hur'] - }); - - newTest({ - title: "Can handle key", - start: ['blah', 'duh', 'd|ur', 'hur'], - keysPressed: '', - end: ['blah', 'duh', '|dur', 'hur'] - }); - - newTest({ - title: "Can handle key", - start: ['blah', 'duh', '|dur', 'hur'], - keysPressed: '', - end: ['blah', 'duh', 'd|ur', 'hur'] - }); -}); diff --git a/test/motion.test.ts b/test/motion.test.ts deleted file mode 100644 index a9ddc8612eb..00000000000 --- a/test/motion.test.ts +++ /dev/null @@ -1,512 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import { TextEditor } from './../src/textEditor'; -import { Position } from './../src/common/motion/position'; -import { setupWorkspace, cleanUpWorkspace } from './testUtils'; -import { ModeHandler } from './../src/mode/modeHandler'; -import { getTestingFunctions } from './testSimplifier'; - -suite("old motion tests", () => { - let text: string[] = [ - "mary had", - "a", - "little lamb", - " whose fleece was " - ]; - - suiteSetup(async () => { - await setupWorkspace(); - await TextEditor.insert(text.join('\n')); - }); - - suiteTeardown(cleanUpWorkspace); - - test("char right: should move one column right", () => { - let position = new Position(0, 0); - assert.equal(position.line, 0); - assert.equal(position.character, 0); - - let next = position.getRight(); - assert.equal(next.line, 0); - assert.equal(next.character, 1); - }); - - test("char right", () => { - let motion = new Position(0, 9); - motion = motion.getRight(); - - assert.equal(motion.line, 0); - assert.equal(motion.character, 9); - }); - - test("char left: should move cursor one column left", () => { - let position = new Position(0, 5); - assert.equal(position.line, 0); - assert.equal(position.character, 5); - - position = position.getLeft(); - assert.equal(position.line, 0); - assert.equal(position.character, 4); - }); - - test("char left: left-most column should stay at the same location", () => { - let motion = new Position(0, 0); - assert.equal(motion.line, 0); - assert.equal(motion.character, 0); - - motion = motion.getLeft(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 0); - }); - - test("line down: should move cursor one line down", () => { - let motion = new Position(1, 0); - assert.equal(motion.line, 1); - assert.equal(motion.character, 0); - - motion = motion.getDown(0); - assert.equal(motion.line, 2); - assert.equal(motion.character, 0); - }); - - test("line down: bottom-most line should stay at the same location", () => { - let motion = new Position(3, 0); - assert.equal(motion.line, 3); - assert.equal(motion.character, 0); - - motion = motion.getDown(3); - assert.equal(motion.line, 3); - assert.equal(motion.character, 0); - }); - - suite("line up", () => { - test("should move cursor one line up", () => { - let position = new Position(1, 0); - assert.equal(position.line, 1); - assert.equal(position.character, 0); - - position = position.getUp(0); - assert.equal(position.line, 0); - assert.equal(position.character, 0); - }); - - test("top-most line should stay at the same location", () => { - let motion = new Position(0, 1); - assert.equal(motion.line, 0); - assert.equal(motion.character, 1); - - motion = motion.getUp(0); - assert.equal(motion.line, 0); - assert.equal(motion.character, 1); - }); - }); - - test("line begin", () => { - let motion = new Position(0, 3).getLineBegin(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 0); - }); - - test("line end", () => { - let motion = new Position(0, 0).getLineEnd(); - assert.equal(motion.line, 0); - assert.equal(motion.character, text[0].length); - - motion = new Position(2, 0).getLineEnd(); - assert.equal(motion.line, 2); - assert.equal(motion.character, text[2].length); - }); - - test("document begin", () => { - let motion = new Position(1, 0).getDocumentBegin(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 0); - }); - - test("document end", () => { - let motion = new Position(0, 0).getDocumentEnd(); - assert.equal(motion.line, text.length - 1); - assert.equal(motion.character, text[text.length - 1].length); - }); - - test("line begin cursor on first non-blank character", () => { - let motion = new Position(0, 3).getFirstLineNonBlankChar(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 0); - }); - - test("last line begin cursor on first non-blank character", () => { - let motion = new Position(3, 0).getFirstLineNonBlankChar(); - assert.equal(motion.line, 3); - assert.equal(motion.character, 1); - }); -}); - -suite("word motion", () => { - let text: string[] = [ - "if (true) {", - " return true;", - "} else {", - "", - " return false;", - " ", - "} // endif" - ]; - - suiteSetup(() => { - return setupWorkspace().then(() => { - return TextEditor.insert(text.join('\n')); - }); - }); - - suiteTeardown(cleanUpWorkspace); - - suite("word right", () => { - test("move to word right", () => { - let motion = new Position(0, 3).getWordRight(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 4); - }); - - test("last word should move to next line", () => { - let motion = new Position(0, 10).getWordRight(); - assert.equal(motion.line, 1); - assert.equal(motion.character, 2); - }); - - test("last word should move to next line stops on empty line", () => { - let motion = new Position(2, 7).getWordRight(); - assert.equal(motion.line, 3); - assert.equal(motion.character, 0); - }); - - test("last word should move to next line skips whitespace only line", () => { - let motion = new Position(4, 14).getWordRight(); - assert.equal(motion.line, 6); - assert.equal(motion.character, 0); - }); - - test("last word on last line should go to end of document (special case!)", () => { - let motion = new Position(6, 6).getWordRight(); - assert.equal(motion.line, 6); - assert.equal(motion.character, 10); - }); - - }); - - suite("word left", () => { - test("move cursor word left across spaces", () => { - let motion = new Position(0, 3).getWordLeft(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 0); - }); - - test("move cursor word left within word", () => { - let motion = new Position(0, 5).getWordLeft(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 4); - }); - - test("first word should move to previous line, beginning of last word", () => { - let motion = new Position(1, 2).getWordLeft(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 10); - }); - - test("first word should move to previous line, stops on empty line", () => { - let motion = new Position(4, 2).getWordLeft(); - assert.equal(motion.line, 3); - assert.equal(motion.character, 0); - }); - - test("first word should move to previous line, skips whitespace only line", () => { - let motion = new Position(6, 0).getWordLeft(); - assert.equal(motion.line, 4); - assert.equal(motion.character, 14); - }); - }); - - suite("WORD right", () => { - test("move to WORD right", () => { - let motion = new Position(0, 3).getBigWordRight(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 10); - }); - - test("last WORD should move to next line", () => { - let motion = new Position(1, 10).getBigWordRight(); - assert.equal(motion.line, 2); - assert.equal(motion.character, 0); - }); - - test("last WORD should move to next line stops on empty line", () => { - let motion = new Position(2, 7).getBigWordRight(); - assert.equal(motion.line, 3); - assert.equal(motion.character, 0); - }); - - test("last WORD should move to next line skips whitespace only line", () => { - let motion = new Position(4, 12).getBigWordRight(); - assert.equal(motion.line, 6); - assert.equal(motion.character, 0); - }); - }); - - suite("WORD left", () => { - test("move cursor WORD left across spaces", () => { - let motion = new Position(0, 3).getBigWordLeft(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 0); - }); - - test("move cursor WORD left within WORD", () => { - let motion = new Position(0, 5).getBigWordLeft(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 3); - }); - - test("first WORD should move to previous line, beginning of last WORD", () => { - let motion = new Position(2, 0).getBigWordLeft(); - assert.equal(motion.line, 1); - assert.equal(motion.character, 9); - }); - - test("first WORD should move to previous line, stops on empty line", () => { - let motion = new Position(4, 2).getBigWordLeft(); - assert.equal(motion.line, 3); - assert.equal(motion.character, 0); - }); - - test("first WORD should move to previous line, skips whitespace only line", () => { - let motion = new Position(6, 0).getBigWordLeft(); - assert.equal(motion.line, 4); - assert.equal(motion.character, 9); - }); - }); - - suite("end of word right", () => { - test("move to end of current word right", () => { - let motion = new Position(0, 4).getCurrentWordEnd(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 7); - }); - - test("move to end of next word right", () => { - let motion = new Position(0, 7).getCurrentWordEnd(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 8); - }); - - test("end of last word should move to next line", () => { - let motion = new Position(0, 10).getCurrentWordEnd(); - assert.equal(motion.line, 1); - assert.equal(motion.character, 7); - }); - - test("end of last word should move to next line skips empty line", () => { - let motion = new Position(2, 7).getCurrentWordEnd(); - assert.equal(motion.line, 4); - assert.equal(motion.character, 7); - }); - - test("end of last word should move to next line skips whitespace only line", () => { - let motion = new Position(4, 14).getCurrentWordEnd(); - assert.equal(motion.line, 6); - assert.equal(motion.character, 0); - }); - }); - - suite("end of WORD right", () => { - test("move to end of current WORD right", () => { - let motion = new Position(0, 4).getCurrentBigWordEnd(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 8); - }); - - test("move to end of next WORD right", () => { - let motion = new Position(0, 8).getCurrentBigWordEnd(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 10); - }); - - test("end of last WORD should move to next line", () => { - let motion = new Position(0, 10).getCurrentBigWordEnd(); - assert.equal(motion.line, 1); - assert.equal(motion.character, 7); - }); - - test("end of last WORD should move to next line skips empty line", () => { - let motion = new Position(2, 7).getCurrentBigWordEnd(); - assert.equal(motion.line, 4); - assert.equal(motion.character, 7); - }); - - test("end of last WORD should move to next line skips whitespace only line", () => { - let motion = new Position(4, 14).getCurrentBigWordEnd(); - assert.equal(motion.line, 6); - assert.equal(motion.character, 0); - }); - }); - - test("line begin cursor on first non-blank character", () => { - let motion = new Position(4, 3).getFirstLineNonBlankChar(); - assert.equal(motion.line, 4); - assert.equal(motion.character, 2); - }); - - test("last line begin cursor on first non-blank character", () => { - let motion = new Position(6, 0).getFirstLineNonBlankChar(); - assert.equal(motion.line, 6); - assert.equal(motion.character, 0); - }); -}); - -suite("sentence motion", () => { - let text: Array = [ - "This text has many sections in it. What do you think?", - "", - "A paragraph boundary is also a sentence boundry, see", - "", - "Weird things happen when there is no appropriate sentence ending", - "", - "Next line is just whitespace", - " ", - "Wow!", - "Another sentence inside one paragraph." - ]; - - suiteSetup(() => { - return setupWorkspace().then(() => { - return TextEditor.insert(text.join('\n')); - }); - }); - - suiteTeardown(cleanUpWorkspace); - - suite("sentence forward", () => { - test("next concrete sentence", () => { - let motion = new Position(0, 0).getSentenceBegin({forward: true}); - assert.equal(motion.line, 0); - assert.equal(motion.character, 35); - }); - - test("next sentence that ends with paragraph ending", () => { - let motion = new Position(2, 50).getNextLineBegin(); - assert.equal(motion.line, 3); - assert.equal(motion.character, 0); - }); - - test("next sentence when cursor is at the end of previous paragraph", () => { - let motion = new Position(3, 0).getSentenceBegin({forward: true}); - assert.equal(motion.line, 4); - assert.equal(motion.character, 0); - }); - - test("next sentence when paragraph contains a line of whilte spaces", () => { - let motion = new Position(6, 2).getSentenceBegin({forward: true}); - assert.equal(motion.line, 9); - assert.equal(motion.character, 0); - }); - }); - - suite("sentence backward", () => { - test("current sentence begin", () => { - let motion = new Position(0, 37).getSentenceBegin({forward: false}); - assert.equal(motion.line, 0); - assert.equal(motion.character, 35); - }); - - test("sentence forward when cursor is at the beginning of the second sentence", () => { - let motion = new Position(0, 35).getSentenceBegin({forward: false}); - assert.equal(motion.line, 0); - assert.equal(motion.character, 0); - }); - - test("current sentence begin with no concrete sentense inside", () => { - let motion = new Position(3, 0).getSentenceBegin({forward: false}); - assert.equal(motion.line, 2); - assert.equal(motion.character, 0); - }); - - test("current sentence begin when it's not the same as current paragraph begin", () => { - let motion = new Position(2, 0).getSentenceBegin({forward: false}); - assert.equal(motion.line, 1); - assert.equal(motion.character, 0); - }); - - test("current sentence begin when previous line ends with a concrete sentence", () => { - let motion = new Position(9, 5).getSentenceBegin({forward: false}); - assert.equal(motion.line, 9); - assert.equal(motion.character, 0); - }); - }); -}); - -suite("paragraph motion", () => { - let text: Array = [ - "this text has", // 0 - "", // 1 - "many", // 2 - "paragraphs", // 3 - "", // 4 - "", // 5 - "in it.", // 6 - "", // 7 - "WOW" // 8 - ]; - - suiteSetup(() => { - return setupWorkspace().then(() => { - return TextEditor.insert(text.join('\n')); - }); - }); - - suiteTeardown(cleanUpWorkspace); - - suite("paragraph down", () => { - test("move down normally", () => { - let motion = new Position(0, 0).getCurrentParagraphEnd(); - assert.equal(motion.line, 1); - assert.equal(motion.character, 0); - }); - - test("move down longer paragraph", () => { - let motion = new Position(2, 0).getCurrentParagraphEnd(); - assert.equal(motion.line, 4); - assert.equal(motion.character, 0); - }); - - test("move down starting inside empty line", () => { - let motion = new Position(4, 0).getCurrentParagraphEnd(); - assert.equal(motion.line, 7); - assert.equal(motion.character, 0); - }); - - test("paragraph at end of document", () => { - let motion = new Position(7, 0).getCurrentParagraphEnd(); - assert.equal(motion.line, 8); - assert.equal(motion.character, 3); - }); - }); - - suite("paragraph up", () => { - test("move up short paragraph", () => { - let motion = new Position(1, 0).getCurrentParagraphBeginning(); - assert.equal(motion.line, 0); - assert.equal(motion.character, 0); - }); - - test("move up longer paragraph", () => { - let motion = new Position(3, 0).getCurrentParagraphBeginning(); - assert.equal(motion.line, 1); - assert.equal(motion.character, 0); - }); - - test("move up starting inside empty line", () => { - let motion = new Position(5, 0).getCurrentParagraphBeginning(); - assert.equal(motion.line, 1); - assert.equal(motion.character, 0); - }); - }); -}); diff --git a/test/notation.test.ts b/test/notation.test.ts deleted file mode 100644 index ec631ac5530..00000000000 --- a/test/notation.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import { AngleBracketNotation } from '../src/notation'; - -suite("Notation", () => { - test("Normalize", () => { - let testCases = { - '': '', - 'cTrL+x': '', - 'CtRl+y': '', - 'c-z': '', - }; - - for (const test in testCases) { - if (testCases.hasOwnProperty(test)) { - let expected = testCases[test]; - - let actual = AngleBracketNotation.Normalize(test); - assert.equal(actual, expected); - } - } - }); -}); \ No newline at end of file diff --git a/test/number/numericString.test.ts b/test/number/numericString.test.ts deleted file mode 100644 index 8befaa4dbd4..00000000000 --- a/test/number/numericString.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import { NumericString } from '../../src/common/number/numericString'; - -suite("numeric string", () => { - test("fails on non-string", () => { - assert.equal(null, NumericString.parse("hi")); - }); - - test("handles hex round trip", () => { - const input = "0xa1"; - assert.equal(input, NumericString.parse(input)!.toString()); - }); - - test("handles decimal round trip", () => { - const input = "9"; - assert.equal(input, NumericString.parse(input)!.toString()); - }); - - test("handles octal trip", () => { - const input = "07"; - assert.equal(input, NumericString.parse(input)!.toString()); - }); -}); \ No newline at end of file diff --git a/test/operator/comment.test.ts b/test/operator/comment.test.ts deleted file mode 100644 index 931d8a813ad..00000000000 --- a/test/operator/comment.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -"use strict"; - -import { setupWorkspace, cleanUpWorkspace } from './../testUtils'; -import { ModeName } from '../../src/mode/mode'; -import { ModeHandler } from '../../src/mode/modeHandler'; -import { getTestingFunctions } from '../testSimplifier'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("comment operator", () => { - let modeHandler: ModeHandler; - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(".js"); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - newTest({ - title: "gcc comments out current line", - start: [ - "first line", - "|second line" - ], - keysPressed: 'gcc', - end: [ - "first line", - "|// second line", - ], - }); - - newTest({ - title: "gcj comments in current and next line", - start: [ - "// first| line", - "// second line", - "third line" - ], - keysPressed: 'gcj', - end: [ - "|first line", - "second line", - "third line" - ], - }); - - newTest({ - title: "block comment with motion", - start: [ - "function test(arg|1, arg2, arg3) {" - ], - keysPressed: 'gCi)', - end: [ - "function test(|/*arg1, arg2, arg3*/) {" - ] - }); - - newTest({ - title: "block comment in Visual Mode", - start: [ - "blah |blah blah" - ], - keysPressed: 'vllllgC', - end: [ - "blah |/*blah*/ blah" - ], - endMode: ModeName.Normal - }); - -}); \ No newline at end of file diff --git a/test/operator/put.test.ts b/test/operator/put.test.ts deleted file mode 100644 index 29966e7959c..00000000000 --- a/test/operator/put.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -"use strict"; - -import { ModeHandler } from "../../src/mode/modeHandler"; -import { setupWorkspace, cleanUpWorkspace, assertEqualLines } from '../testUtils'; -import { getTestingFunctions } from '../testSimplifier'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("put operator", () => { - - let modeHandler: ModeHandler; - - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - test("basic put test", async () => { - await modeHandler.handleMultipleKeyEvents( - 'iblah blah'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - '^', 'D', 'p', 'p' - ]); - - await assertEqualLines(["blah blahblah blah"]); - }); - - test("test yy end of line", async () => { - await modeHandler.handleMultipleKeyEvents( - 'iblah blah\nblah'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - '^', 'y', 'y', 'p' - ]); - - await assertEqualLines(["blah blah", "blah", "blah"]); - }); - - test("test yy first line", async () => { - await modeHandler.handleMultipleKeyEvents( - 'iblah blah\nblah'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', 'y', 'y', 'p' - ]); - - await assertEqualLines(["blah blah", "blah blah", "blah"]); - }); - - test("test yy middle line", async () => { - await modeHandler.handleMultipleKeyEvents( - 'i1\n2\n3'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'k', 'y', 'y', 'p' - ]); - - await assertEqualLines(["1", "2", "2", "3"]); - }); - - newTest({ - title: "test yy with correct positon movement", - start: ["o|ne", "two", "three", "four"], - keysPressed: '2yyjjpk', - end: ["one", "two", "|three", "one", "two", "four"] - }); -}); \ No newline at end of file diff --git a/test/plugins/surround.test.ts b/test/plugins/surround.test.ts deleted file mode 100644 index 50117f691c4..00000000000 --- a/test/plugins/surround.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -"use strict"; - -import { setupWorkspace, cleanUpWorkspace } from './../testUtils'; -import { ModeName } from '../../src/mode/mode'; -import { ModeHandler } from '../../src/mode/modeHandler'; -import { getTestingFunctions } from '../testSimplifier'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("surround plugin", () => { - let modeHandler: ModeHandler; - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(".js"); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - newTest({ - title: "ysiw) surrounds word", - start: ["first li|ne test"], - keysPressed: 'ysiw)', - end: ["first (|line) test"], - }); - - newTest({ - title: "ysiw< surrounds word with tags", - start: ["first li|ne test"], - keysPressed: 'ysiw<123>', - end: ["first <123>|line test"], - }); - - newTest({ - title: "ysiw< surrounds word with tags and attributes", - start: ["first li|ne test"], - keysPressed: 'ysiw', - end: ['first |line test'], - }); - - newTest({ - title: "yss) surrounds entire line respecting whitespace", - start: ["foo", " foob|ar "], - keysPressed: 'yss)', - end: ["foo", " (|foobar) "] - }); - - newTest({ - title: "change surround", - start: ["first 'li|ne' test"], - keysPressed: "cs')", - end: ["first (li|ne) test"], - }); - - newTest({ - title: "change surround to tags", - start: ["first [li|ne] test"], - keysPressed: "cs]tabc>", - end: ["first li|ne test"], - }); - - newTest({ - title: "delete surround", - start: ["first 'li|ne' test"], - keysPressed: "ds'", - end: ["first li|ne test"], - }); - - newTest({ - title: "delete surround with tags", - start: ["first li|ne test"], - keysPressed: "dst", - end: ["first li|ne test"], - }); -}); diff --git a/test/register/register.test.ts b/test/register/register.test.ts deleted file mode 100644 index ac232e58579..00000000000 --- a/test/register/register.test.ts +++ /dev/null @@ -1,189 +0,0 @@ -"use strict"; - -import * as vscode from 'vscode'; -import { ModeHandler } from "../../src/mode/modeHandler"; -import { setupWorkspace, cleanUpWorkspace, assertEqualLines, assertEqual} from '../testUtils'; -import { getTestingFunctions } from '../testSimplifier'; -import * as util from '../../src/util'; -import { getAndUpdateModeHandler } from "../../extension"; - -suite("register", () => { - let modeHandler: ModeHandler; - - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - modeHandler = await getAndUpdateModeHandler(); - }); - - teardown(cleanUpWorkspace); - - newTest({ - title: "Can copy to a register", - start: ['|one', 'two'], - keysPressed: '"add"ap', - end: ["two", "|one"], - }); - - util.clipboardCopy("12345"); - - newTest({ - title: "Can access '*' (clipboard) register", - start: ['|one'], - keysPressed: '"*P', - end: ["1234|5one"], - }); - - newTest({ - title: "Can access '+' (clipboard) register", - start: ['|one'], - keysPressed: '"+P', - end: ["1234|5one"], - }); - - newTest({ - title: "Can use two registers together", - start: ['|one', "two"], - keysPressed: '"ayyj"byy"ap"bp', - end: ["one", "two", "one", "|two"], - }); - - newTest({ - title: "Can use black hole register", - start: ['|asdf', "qwer"], - keysPressed: 'yyj"_ddkp', - end: ["asdf", "|asdf"], - }); - - test("System clipboard works with chinese characters", async () => { - const testString = '你好'; - util.clipboardCopy(testString); - assertEqual(testString, util.clipboardPaste()); - - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - - // Paste from our paste handler - await modeHandler.handleMultipleKeyEvents([ - '', - '"', '*', 'P', - 'a' - ]); - assertEqualLines([testString]); - - // Now try the built in vscode paste - await vscode.commands.executeCommand("editor.action.clipboardPasteAction"); - - assertEqualLines([testString + testString]); - }); - - test("Yank stores text in Register '0'", async () => { - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - - await modeHandler.handleMultipleKeyEvents( - 'itest1\ntest2\ntest3'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'y', 'y', - 'j', - 'y', 'y', - 'g', 'g', - 'd', 'd', - '"', '0', - 'P' - ]); - - assertEqualLines([ - 'test2', - 'test2', - 'test3' - ]); - }); - - test("Register '1'-'9' stores delete content", async () => { - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - - await modeHandler.handleMultipleKeyEvents( - 'itest1\ntest2\ntest3\n'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'd', 'd', - 'd', 'd', - 'd', 'd', - '"', '1', 'p', - '"', '2', 'p', - '"', '3', 'p' - ]); - - assertEqualLines([ - '', - 'test3', - 'test2', - 'test1' - ]); - }); - - test("\"A appends linewise text to \"a", async() => { - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - - await modeHandler.handleMultipleKeyEvents( - 'itest1\ntest2\ntest3'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'v', 'l', 'l', - '"', 'a', 'y', - 'j', - 'V', - '"', 'A', 'y', - 'j', - '"', 'a', 'p' - ]); - - assertEqualLines([ - 'test1', - 'test2', - 'test3', - 'tes', - 'test2' - ]); - }); - - test("\"A appends character wise text to \"a", async() => { - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - - await modeHandler.handleMultipleKeyEvents( - 'itest1\ntest2\n'.split('') - ); - - await modeHandler.handleMultipleKeyEvents([ - '', - 'g', 'g', - 'v', 'l', 'l', 'l', 'l', - '"', 'a', 'y', - 'j', - 'v', 'l', 'l', 'l', 'l', - '"', 'A', 'y', - 'j', - '"', 'a', 'p' - ]); - - assertEqualLines([ - 'test1', - 'test2', - 'test1test2', - ]); - }); - -}); \ No newline at end of file diff --git a/test/register/repeatableMovement.test.ts b/test/register/repeatableMovement.test.ts deleted file mode 100644 index 54d5d4fab8f..00000000000 --- a/test/register/repeatableMovement.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; - -import { ModeHandler } from "../../src/mode/modeHandler"; -import { setupWorkspace, cleanUpWorkspace, assertEqualLines } from '../testUtils'; -import { getTestingFunctions } from '../testSimplifier'; - -suite("register", () => { - let { - newTest, - newTestOnly, - } = getTestingFunctions(); - - setup(async () => { - await setupWorkspace(); - }); - - suiteTeardown(cleanUpWorkspace); - - newTest({ - title: "Can repeat f", - start: ['|abc abc abc'], - keysPressed: 'fa;', - end: ['abc abc |abc'], - }); - - newTest({ - title: "Can repeat reversed F", - start: ['|abc abc abc'], - keysPressed: 'fa$,', - end: ['abc abc |abc'], - }); - - newTest({ - title: "Can repeat t", - start: ['|abc abc abc'], - keysPressed: 'tc;', - end: ['abc a|bc abc'], - }); - - newTest({ - title: "Can repeat N times reversed t", - start: ['|abc abc abc abc'], - keysPressed: 'tc$3,', - end: ['abc| abc abc abc'], - }); - -}); \ No newline at end of file diff --git a/test/testSimplifier.ts b/test/testSimplifier.ts deleted file mode 100644 index f97286559ad..00000000000 --- a/test/testSimplifier.ts +++ /dev/null @@ -1,239 +0,0 @@ -import * as assert from 'assert'; -import * as vscode from 'vscode'; -import { ModeName } from '../src/mode/mode'; -import { HistoryTracker } from '../src/history/historyTracker'; -import { Position } from '../src/common/motion/position'; -import { ModeHandler } from '../src/mode/modeHandler'; -import { TextEditor } from '../src/textEditor'; -import { assertEqualLines } from './testUtils'; -import { waitForCursorUpdatesToHappen } from '../src/util'; -import { Globals } from '../src/globals'; -import { getAndUpdateModeHandler } from "../extension"; - -export function getTestingFunctions() { - const newTest = (testObj: ITestObject): void => { - const stack = (new Error()).stack; - let niceStack = stack ? stack.split('\n').splice(2, 1).join('\n') : "no stack available :("; - - test(testObj.title, async () => testIt.bind(null, await getAndUpdateModeHandler())(testObj) - .catch((reason: Error) => { - reason.stack = niceStack; - throw reason; - }) - ); - }; - - const newTestOnly = (testObj: ITestObject): void => { - console.log("!!! Running single test !!!"); - const stack = (new Error()).stack; - let niceStack = stack ? stack.split('\n').splice(2, 1).join('\n') : "no stack available :("; - - test.only(testObj.title, async () => testIt.bind(null, await getAndUpdateModeHandler())(testObj) - .catch((reason: Error) => { - reason.stack = niceStack; - throw reason; - }) - ); - }; - - return { - newTest, - newTestOnly, - }; -} - -interface ITestObject { - title: string; - start: string[]; - keysPressed: string; - end: string[]; - endMode?: ModeName; -} - -class TestObjectHelper { - /** - * Position that the test says that the cursor starts at. - */ - startPosition = new Position(0, 0); - - /** - * Position that the test says that the cursor ends at. - */ - endPosition = new Position(0, 0); - - private _isValid = false; - private _testObject: ITestObject; - - constructor(_testObject: ITestObject) { - this._testObject = _testObject; - - this._parse(_testObject); - } - - public get isValid(): boolean { - return this._isValid; - } - - private _setStartCursorPosition(lines: string[]): boolean { - let result = this._getCursorPosition(lines); - this.startPosition = result.position; - return result.success; - } - - private _setEndCursorPosition(lines: string[]): boolean { - let result = this._getCursorPosition(lines); - this.endPosition = result.position; - return result.success; - } - - private _getCursorPosition(lines: string[]): { success: boolean; position: Position} { - let ret = { success: false, position: new Position(0, 0) }; - for (let i = 0; i < lines.length; i++) { - let columnIdx = lines[i].indexOf('|'); - if (columnIdx >= 0) { - ret.position = ret.position.setLocation(i, columnIdx); - ret.success = true; - } - } - - return ret; - } - - private _parse(t: ITestObject): void { - this._isValid = true; - if (!this._setStartCursorPosition(t.start)) { - this._isValid = false; - } - if (!this._setEndCursorPosition(t.end)) { - this._isValid = false; - } - } - - public asVimInputText(): string[] { - let ret = 'i' + this._testObject.start.join('\n').replace('|', ''); - return ret.split(''); - } - - public asVimOutputText(): string[] { - let ret = this._testObject.end.slice(0); - ret[this.endPosition.line] = ret[this.endPosition.line].replace('|', ''); - return ret; - } - - /** - * Returns a sequence of Vim movement characters 'hjkl' as a string array - * which will move the cursor to the start position given in the test. - */ - public getKeyPressesToMoveToStartPosition(): string[] { - let ret = ''; - let linesToMove = this.startPosition.line; - - let cursorPosAfterEsc = - this._testObject.start[this._testObject.start.length - 1].replace('|', '').length - 1; - let numCharsInCursorStartLine = - this._testObject.start[this.startPosition.line].replace('|', '').length - 1; - let charactersToMove = this.startPosition.character; - - if (linesToMove > 0) { - ret += Array(linesToMove + 1).join('j'); - } else if (linesToMove < 0) { - ret += Array(Math.abs(linesToMove) + 1).join('k'); - } - - if (charactersToMove > 0) { - ret += Array(charactersToMove + 1).join('l'); - } else if (charactersToMove < 0) { - ret += Array(Math.abs(charactersToMove) + 1).join('h'); - } - - return ret.split(''); - } -} - -/** - * Tokenize a string like "abcd" into ["a", "b", "c", "", "d", ""] - */ -function tokenizeKeySequence(sequence: string): string[] { - let isBracketedKey = false; - let key = ""; - let result: string[] = []; - - for (const char of sequence) { - key += char; - - if (char === '<') { - isBracketedKey = true; - } - - if (char === '>') { - isBracketedKey = false; - } - - if (isBracketedKey) { - continue; - } - - result.push(key); - key = ""; - } - - return result; -} - -async function testIt(modeHandler: ModeHandler, testObj: ITestObject): Promise { - modeHandler.vimState.editor = vscode.window.activeTextEditor!; - - let helper = new TestObjectHelper(testObj); - - // Don't try this at home, kids. - (modeHandler as any)._vimState.cursorPosition = new Position(0, 0); - - await modeHandler.handleKeyEvent(''); - - // Insert all the text as a single action. - await modeHandler.vimState.editor.edit(builder => { - builder.insert(new Position(0, 0), testObj.start.join("\n").replace("|", "")); - }); - - await modeHandler.handleMultipleKeyEvents(['', 'g', 'g']); - - await waitForCursorUpdatesToHappen(); - - // Since we bypassed VSCodeVim to add text, - // we need to tell the history tracker that we added it. - modeHandler.vimState.historyTracker.addChange(); - modeHandler.vimState.historyTracker.finishCurrentStep(); - - // move cursor to start position using 'hjkl' - await modeHandler.handleMultipleKeyEvents(helper.getKeyPressesToMoveToStartPosition()); - - await waitForCursorUpdatesToHappen(); - - Globals.modeHandlerForTesting = modeHandler; - - // assumes key presses are single characters for now - await modeHandler.handleMultipleKeyEvents(tokenizeKeySequence(testObj.keysPressed)); - - // Check valid test object input - assert(helper.isValid, "Missing '|' in test object."); - - // Check final cursor position - // - let actualPosition = Position.FromVSCodePosition(TextEditor.getSelection().start); - let expectedPosition = helper.endPosition; - assert.equal(actualPosition.line, expectedPosition.line, "Cursor LINE position is wrong."); - assert.equal(actualPosition.character, expectedPosition.character, "Cursor CHARACTER position is wrong."); - - // end: check given end output is correct - // - assertEqualLines(helper.asVimOutputText()); - - // endMode: check end mode is correct if given - if (typeof testObj.endMode !== 'undefined') { - let actualMode = ModeName[modeHandler.currentMode.name].toUpperCase(); - let expectedMode = ModeName[testObj.endMode].toUpperCase(); - assert.equal(actualMode, expectedMode, "Didn't enter correct mode."); - } -} - -export { ITestObject, testIt }; \ No newline at end of file diff --git a/test/testUtils.ts b/test/testUtils.ts deleted file mode 100644 index 0bb7dc86784..00000000000 --- a/test/testUtils.ts +++ /dev/null @@ -1,94 +0,0 @@ -"use strict"; - -import {TextEditor} from '../src/textEditor'; -import * as vscode from "vscode"; -import * as assert from 'assert'; -import {join} from 'path'; -import * as os from 'os'; -import * as fs from 'fs'; -import { Configuration } from '../src/configuration/configuration'; - -function rndName() { - return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); -} - -async function createRandomFile(contents: string, fileExtension: string): Promise { - const tmpFile = join(os.tmpdir(), rndName() + fileExtension); - - try { - fs.writeFileSync(tmpFile, contents); - return vscode.Uri.file(tmpFile); - } catch (error) { - throw error; - } -} - -export function assertEqualLines(expectedLines: string[]) { - for (let i = 0; i < expectedLines.length; i++) { - let expected = expectedLines[i]; - let actual = TextEditor.readLineAt(i); - assert.equal(actual, expected, `Content does not match; Expected=${expected}. Actual=${actual}`); - } - - assert.equal(TextEditor.getLineCount(), expectedLines.length, "Line count does not match."); -} - -/** - * Assert that the first two arguments are equal, and fail a test otherwise. - * - * The only difference between this and assert.equal is that here we - * check to ensure the types of the variables are correct. - */ -export function assertEqual(one: T, two: T, message: string = ""): void { - assert.equal(one, two, message); -} - -export async function setupWorkspace(fileExtension: string = ""): Promise { - const file = await createRandomFile("", fileExtension); - const doc = await vscode.workspace.openTextDocument(file); - - await vscode.window.showTextDocument(doc); - setTextEditorOptions(2, true); - - assert.ok(vscode.window.activeTextEditor); -} - -export async function cleanUpWorkspace(): Promise { - // https://github.com/Microsoft/vscode/blob/master/extensions/vscode-api-tests/src/utils.ts - return new Promise((c, e) => { - if (vscode.window.visibleTextEditors.length === 0) { - return c(); - } - - // TODO: the visibleTextEditors variable doesn't seem to be - // up to date after a onDidChangeActiveTextEditor event, not - // even using a setTimeout 0... so we MUST poll :( - let interval = setInterval(() => { - if (vscode.window.visibleTextEditors.length > 0) { - return; - } - - clearInterval(interval); - c(); - }, 10); - - vscode.commands.executeCommand('workbench.action.closeAllEditors') - .then(() => null, (err: any) => { - clearInterval(interval); - e(err); - }); - }).then(() => { - assert.equal(vscode.window.visibleTextEditors.length, 0); - assert(!vscode.window.activeTextEditor); - }); -} - -export function setTextEditorOptions(tabSize: number, insertSpaces: boolean): void { - Configuration.enableNeovim = true; - Configuration.tabstop = tabSize; - Configuration.expandtab = insertSpaces; - let options = vscode.window.activeTextEditor!.options; - options.tabSize = tabSize; - options.insertSpaces = insertSpaces; - vscode.window.activeTextEditor!.options = options; -} \ No newline at end of file diff --git a/test/textEditor.test.ts b/test/textEditor.test.ts deleted file mode 100644 index c5ab1c87d48..00000000000 --- a/test/textEditor.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -"use strict"; - -import * as assert from 'assert'; -import * as vscode from 'vscode'; -import { TextEditor } from './../src/textEditor'; -import { setupWorkspace, cleanUpWorkspace } from './testUtils'; - -suite("text editor", () => { - suiteSetup(async () => { - await setupWorkspace(); - }); - - suiteTeardown(cleanUpWorkspace); - - test("insert 'Hello World'", async () => { - let expectedText = "Hello World"; - - await TextEditor.insert(expectedText); - - assert.equal(vscode.window.activeTextEditor!.document.lineCount, 1); - let actualText = TextEditor.readLineAt(0); - assert.equal(actualText, expectedText); - }); - - test("replace 'World' with 'Foo Bar'", async () => { - let newText = "Foo Bar"; - let start = new vscode.Position(0, 6); - let end = new vscode.Position(0, 11); - let range : vscode.Range = new vscode.Range(start, end); - - await TextEditor.replace(range, newText); - assert.equal(vscode.window.activeTextEditor!.document.lineCount, 1); - - let actualText = TextEditor.readLineAt(0); - assert.equal(actualText, "Hello Foo Bar"); - }); - - test("delete `Hello`", async () => { - assert.equal(vscode.window.activeTextEditor!.document.lineCount, 1); - - let end = new vscode.Position(0, 5); - let range = new vscode.Range(new vscode.Position(0, 0), end); - - await TextEditor.delete(range); - let actualText = TextEditor.readLineAt(0); - assert.equal(actualText, " Foo Bar"); - }); - - test("delete the whole line", async () => { - assert.equal(vscode.window.activeTextEditor!.document.lineCount, 1); - - let range = vscode.window.activeTextEditor!.document.lineAt(0).range; - - await TextEditor.delete(range); - let actualText = TextEditor.readLineAt(0); - assert.equal(actualText, ""); - }); - - test("try to read lines that don't exist", () => { - assert.equal(vscode.window.activeTextEditor!.document.lineCount, 1); - assert.throws(() => TextEditor.readLineAt(1), RangeError); - assert.throws(() => TextEditor.readLineAt(2), RangeError); - }); -}); diff --git a/test/tslint.json b/test/tslint.json deleted file mode 100644 index 75efc86e4f7..00000000000 --- a/test/tslint.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../tslint.json", - "rules": { - "no-unused-variable": false - } -} diff --git a/tsconfig.json b/tsconfig.json index 38935262b5b..adecf9bbf26 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,8 @@ "noImplicitReturns": true, "suppressImplicitAnyIndexErrors": true, "lib": [ - "es6" + "es6", + "dom" ], "sourceMap": true, "strictNullChecks": true, @@ -18,4 +19,4 @@ "node_modules", ".vscode-test" ] -} +} \ No newline at end of file