aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Buckley-Houston2017-05-30 09:24:00 +0000
committerThomas Buckley-Houston2017-05-30 09:24:00 +0000
commit79bd6b4273325f891d2fcdb2a79ede59e313e414 (patch)
tree48028cfaf00bdd6ef82b83afb638317db4a9a53d
parent37905d1bad1dd19cca13fd36022b0cffb811b699 (diff)
Formalise plugin: functions, autoload, tests
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml8
-rw-r--r--Gemfile5
-rw-r--r--Gemfile.lock30
-rw-r--r--LICENSE19
-rw-r--r--README.md121
-rw-r--r--autoload/novim_mode.vim287
-rw-r--r--doc/novim-mode.txt12
-rw-r--r--doc/novim_mode.txt170
-rw-r--r--doc/tags2
-rw-r--r--plugin/novim-mode.vim92
-rw-r--r--plugin/novim_mode.vim38
-rw-r--r--spec/novim_mode_spec.rb105
-rw-r--r--spec/spec_helper.rb21
14 files changed, 774 insertions, 137 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6e92f57
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+tags
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..31917a6
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+addons:
+ apt:
+ packages:
+ - vim-gtk
+
+before_script:
+ - "export DISPLAY=:99.0"
+ - "sh -e /etc/init.d/xvfb start"
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..08e709d
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,5 @@
+source 'https://rubygems.org'
+gem 'vimrunner'
+gem 'rake'
+gem 'rspec'
+
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..726404b
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,30 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ diff-lcs (1.3)
+ rake (12.0.0)
+ rspec (3.6.0)
+ rspec-core (~> 3.6.0)
+ rspec-expectations (~> 3.6.0)
+ rspec-mocks (~> 3.6.0)
+ rspec-core (3.6.0)
+ rspec-support (~> 3.6.0)
+ rspec-expectations (3.6.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.6.0)
+ rspec-mocks (3.6.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.6.0)
+ rspec-support (3.6.0)
+ vimrunner (0.3.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ rake
+ rspec
+ vimrunner
+
+BUNDLED WITH
+ 1.11.2
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9cf1062
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index abd2430..7083a00 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,12 @@
-# No vim keybindings mode
+# No Vim Keybindings Mode
-_Work In Progress_
+Some, indeed many, may say this is counter-productive or even sacrilegious. But Vim is a lot more than just a keybinding paradigm; firstly it has one of the richest plugin ecosystems of any editor, but also it is a --if not *the* most-- ubiquitous text editor that's been battle tested for over 25 years. There are more reasons to use it than merely its famous shortcut vocabulary.
-Some, indeed many, may say this is counter-productive or even sacrilegious. But Vim is a lot more than just a keybinding paradigm; firstly it has one of the richest plugin ecosystems of any editor, but also it is a --if not *the* most-- ubiquitous text editor and has been battle tested for over 25 years. There are more reasons to use it than merely its famous shortcut vocabulary. This plugin is an attempt to expose everything else about Vim without the overhead of learning to use Normal Mode The Right Way. This is not a rebellion, it is merely a manifestation of the distinction between Vim the editor and Vim the keybinding paradigm. Please do not dismiss Normal Mode just because this plugin exists, give `vimtutor` a go: modal editing is popular for a reason.
+This plugin is an attempt to expose everything else about Vim without the overhead of cultivating Normal Mode fluency. This is not a rebellion, it is merely a manifestation of the distinction between Vim the editor and Vim the keybinding paradigm. Please do not dismiss Normal Mode just because this plugin exists, give `vimtutor` a try, modal editing is popular for a reason.
-Vim itself already has support for something similar in its optional [`mswin.vim`](https://github.com/vim/vim/blob/master/runtime/mswin.vim) config file. However it still assumes the relevance of Normal Mode and `SHIFT+INSERT` combinations. Whereas this plugin attempts to avoid Normal Mode unless absolutely necessary, say for interacting with the NERDTree buffer, wherein Insert mode has no purpose.
+Vim itself already has support for something similar in its optional [`mswin.vim`](https://github.com/vim/vim/blob/master/runtime/mswin.vim) config file. However it still assumes the necessity of Normal Mode and such heritage as `SHIFT+INSERT`-style combinations. This plugin however, attempts to avoid Normal Mode unless absolutely necessary, say for interacting with the NERDTree buffer, wherein Insert Mode has no purpose.
+
+The name `novim-mode` is a nod to the prevalence of 'vim-mode' plugins and extensions available in environments outside the editor, such as web browsers. In the same way that the love of Vim has led to efforts to export it elsewhere, 'novim-mode' is the love of 'conventional' editing imported into Vim.
## Installation
@@ -14,44 +16,101 @@ Use your favourite plugin manager, eg, for vim-plug;
## Usage
-Most keybindings should work as you might expect from, say Atom or Sublime Text; `CTRL+S` to save, etc. However there are some differences;
- * `CTRL+Z` does not undo, as that is already a well-established *nix shortcut for backgrounding a program.
- * CTRL/HOME/END/PAGEUP/PAGEDOWN keys don't work during selection mode. Though large areas of text can be quickly selected with the mouse. It may end up being easier to simulate selection mode with Vim's default Visual mode.
- * There seems to be a bug where only `SHIFT+TAB` and not `TAB` works for indenting during selection mode. Again this may be fixed by simulating selection mode with Visual mode in the future.
+Most keybindings should work as you might expect from, say Atom or Sublime Text; `SHIFT+ARROW` to select and `CTRL+C/V` to copy/paste. But don't expect Vim to completely bend to your will, it is still useful to familiarise yourself with some of Vim's basic concepts. For instance you may on occasion find yourself stuck in a particular Vim mode, like when pasting text without 'Paste Mode' then inserted text can trigger random mappings. In such case `CTRL+Q` may not kill Vim and you'll need to find a way of getting to Normal Mode and typing `:q` then `<RETURN>`. Such is life with Vim, this plugin is highly unlikely to ever change that. (BTW conventional pasting is on by default, but to exit an errant 'Paste Mode' use `:set nopaste`.)
+
+If you are new to Vim, then perhaps the only remaining confusion after installing
+this plugin will be about where files go when you open new ones. This question
+will be answered by Vim's concept of 'buffers'. You may wish to install something
+like [vim-buftabline](https://github.com/ap/vim-buftabline) to give a familiar
+list of open files along the top of the editor.
### Keybindings
- * `CTRL+S`: Saves the current file
- * `SHIFT+ARROW`: Enter selection mode
- * `CTRL+C`: Copy selection
- * `CTRL+X`: Cut selection
- * `CTRL+V`: Paste current selection
- * `CTRL+A`: Select all
- * `TAB`: Indent selected text [currently broken]
- * `SHIFT+TAB`: Unindent selected text
- * `CTRL+F`: Find text. `F3` and `SHIFT+F3` find next/previous
- * `CTRL+U/R`: Undo/Redo
- * `CTRL+K`: Delete current line
- * `CTRL+D`: Duplicate current line
- * `ALT+UP/DOWN`: Move current line up/down
- * `CTRL+Q`: Exit window/tab/split/quickfix/etc
+Many terminals default to intercepting `CTRL+S` to suspend output, if so you will
+need to disable this behaviour. Most terminals will also bind the `CTRL+Q` to undo
+the suspend, useful, but you will also need to unmap that, or change the mapping
+in this plugin for quitting Vim.
+
+`CTRL`-based shortcuts are paired with uppercase letters here because Vim
+does not recognise the difference between cases when using `CTRL` combinations and
+documenting in uppercase implies something of this distinction.
+
+#### General editor shortcuts
+ * `CTRL+N`: Open a new file.
+ * `CTRL+O`: Open an existing file.
+ * `CTRL+S`: Saves the current file.
+ * `CTRL+G`: Goto line.
+ * `ALT+;`: Vim command prompt.
+ * `ALT+o`: Replaces native `CTRL+O` to give one-off Normal Mode commands.
+
+#### Pane controls
+ * `ALT+ARROW`: Change pane/buffer focus.
+ * `CTRL+W`: Closes current pane-like thing. Also closes associated quickfix and location panes.
+
+#### Selecting, copy and paste
+ * `SHIFT+ARROW`: Select text
+ * `CTRL+C`: Copy selection or copy line if no selection.
+ * `CTRL+X`: Cut selection or cut line if no selection.
+ * `CTRL+V`: Paste current selection.
+ * `CTRL+A`: Select all.
+ * `CTRL+D`: Select word under cursor. Use something like [vim-multicursors](https://github.com/terryma/vim-multiple-cursors) for multi cursor support.
+ * `CTRL+L`: Select line under cursor, repetition selects more lines.
+
+#### Indenting
+ * `TAB` or `ALT+]`: Indent current line or selected text. _[`TAB` currently broken]_
+ * `SHIFT+TAB` or `ALT+[`: Unindent current line or selected text.
+
+#### Finding, replacing
+ * `CTRL+F`: Find text. When text is selected that selection is searched for.
+ * `F3` and `SHIFT+F3`: Find next and previous occurences.
+ * `CTRL+H`: Find and replace. `[FIND]` and `[REPLACE]` are prepopulated.
+
+#### Undoing
+ * `CTRL+Z` or `CTRL+U`: Undo.
+ * `CTRL+Y`: Redo.
+
+#### Other text manipulation tricks
+ * `CTRL+LEFT/RIGHT`: Move cursor per word (works in selection as well).
+ * `CTRL+ALT+k`: Delete current line.
+ * `CTRL+ALT+k`: Duplicate current line.
+ * `CTRL+UP/DOWN`: Move current line or selected text up/down.
### Interoperability
-
-You may wish to adjust the order in which this plugin is loaded as it overrides mappings.
-
-When adding a new binding that needs Normal mode, you should use `<C-O>` before the targeted command, for example;
+When adding a new binding of your own that needs Normal mode, you should use `<C-O>` before the targeted command, for example;
```vim
" Ensure CtrlP doesn't get overridden by autocomplete in insertmode
inoremap <C-P> <C-O>:CtrlP<CR>
```
-For easily navigating splits/windows/etc you will want to create your own mappings. Personally, I'm currently using [vim-tmux-navigator](https://github.com/christoomey/vim-tmux-navigator) with my own custom mappings to `CTRL+ARROW`.
+Overriding or disabling shortcuts in this plugin can be done in several ways. The simplest way is to use:
+```
+let g:novim_mode_use_shortcuts = 0
+inoremap ... custom mapping ...
+call novim_mode#StartNoVimMode()
+```
-To access the Vim command prompt, I currently have this mapping;
+Shorcuts are also grouped roughly under the headings described above, so you may be able to disable
+one of the following:
```vim
-inoremap <C-L> <C-O>:
+let g:novim_mode_use_general_app_shortcuts = 1
+let g:novim_mode_use_pane_controls = 1
+let g:novim_mode_use_copypasting = 1
+let g:novim_mode_use_indenting = 1
+let g:novim_mode_use_finding = 1
+let g:novim_mode_use_undoing = 1
+let g:novim_mode_use_text_tricks = 1
+
+" Small fixes to HOME and PAGEUP behaviour
+let g:novim_mode_use_editor_fixes = 1
+" Allows scrolling through wrapped lines one visual line at a time
+let g:novim_mode_use_better_wrap_navigation = 1
```
-It is of course useful for many things. For example search and replace.
-Perhaps these mappings could be added as configurable optionals one day.
+Lastly you can unmap a mapping using commands such as `nunmap`, `iunmap`, `sunmap`, etc.
+
+### Known issues
+ * There seems to be a bug where only `SHIFT+TAB` and not `TAB` works for indenting during selection mode. Again this may be fixed by simulating selection mode with Visual mode in the future.
+ * When using `novim_mode_use_better_wrap_navigation`, then END key does not go the end of a visual line, but to the end of the physically represented line.
+ * Mapping `<CTRL+m>` internally means mapping `<RETURN>`. This is a throwback to Vim's days as a pure terminal application.
+ * `CTRL+BACKSPACE` internally represents <CTRL+H>, which can be annoying. Again this is a throwback to Vim's days as a pure terminal application.
+
diff --git a/autoload/novim_mode.vim b/autoload/novim_mode.vim
new file mode 100644
index 0000000..f7408d3
--- /dev/null
+++ b/autoload/novim_mode.vim
@@ -0,0 +1,287 @@
+" A fundamental question for this plugin is whether insertmode
+" is always relevant. This is where we try to get an answer.
+function! s:IsEditableBuffer()
+ if &buftype ==# 'nofile'
+ \|| !&modifiable
+ \|| &readonly
+ \|| bufname('%') =~# 'NERD_tree_'
+ return 0
+ else
+ return 1
+ endif
+endfunction
+
+" Make sure insert mode is the default mode only when opening/switching
+" to files that you want to edit.
+function! s:InsertMode()
+ if s:IsEditableBuffer() == 1
+ exe "set insertmode"
+ else
+ exe "set noinsertmode"
+ endif
+endfunction
+
+" Count number of open buffers. They don't have to be visible.
+function! s:CountListedBuffers()
+ let bfr_count = 0
+ for bfr in range(1, bufnr("$"))
+ if buflisted(bfr)
+ \&& ! empty(bufname(bfr))
+ \|| getbufvar(bfr, '&buftype') ==# 'help'
+ let bfr_count += 1
+ endif
+ endfor
+ return bfr_count
+endfunction
+
+" The number of visible lines in the current buffer.
+" TODO: Include wrapped lines in the total, so that we can start scrolling
+" through *visible* lines rather than file lines.
+function! s:BufferLines()
+ return line('w$') - line('w0')
+endfunction
+
+function! s:InsertAndSelectionBehaviour()
+ " Intelligently set/unset insertmode
+ augroup start_insertmode
+ autocmd!
+ autocmd BufEnter * call s:InsertMode()
+ augroup END
+
+ " Mostly changes the way selection works.
+ " See: http://vimdoc.sourceforge.net/htmldoc/gui.html#:behave
+ " An extract from the docs about the difference between `behave mswin`
+ " and `behave xterm`:
+ " mswin xterm
+ " 'selectmode' 'mouse,key' ''
+ " 'mousemodel' 'popup' 'extend'
+ " 'keymodel' 'startsel,stopsel' ''
+ " 'selection' 'exclusive' 'inclusive'
+ behave mswin
+
+ " Make 'v' commands default to Visual mode.
+ " Not sure how useful this is because the mappings that use 'v'-based
+ " commands don't seem to follow this option. Thus why you will see <C-G>
+ " after some selection-based commands to switch from Visual to Selection
+ " Mode. So might be better to give experienced users who are pressing
+ " 'v' in normal mode the expected behaviour.
+ set selectmode+=cmd
+endfunction
+
+" All shortcuts in one function so they can be more easily controlled.
+function! g:SetNoVimModeShortcuts()
+ " Basic interactions with the editor
+ if g:novim_mode_use_general_app_shortcuts == 1
+ " CTRL+q to completely exit vim
+ inoremap <silent> <C-Q> <C-O>:call novim_mode#ExitVim()<CR>
+ snoremap <silent> <C-Q> <C-O>:call novim_mode#ExitVim()<CR>
+ nnoremap <silent> <C-Q> :call novim_mode#ExitVim()<CR>
+
+ " CTRL+n for new file
+ inoremap <C-N> <C-O>:edit<Space>
+ " CTRL+o to open file
+ " TODO: hook into netrw or NERDTree
+ inoremap <C-O> <C-O>:edit<Space>
+ " CTRL+s saves
+ inoremap <silent> <C-S> <C-O>:update<CR>
+
+ " Goto line number
+ inoremap <C-G> <C-O>:call novim_mode#GotoLine()<CR>
+
+ " ALT+; for command prompt
+ inoremap <M-;> <C-O>:
+
+ " <ALT+o> replaces native <C-O> for one-time normal mode commands.
+ inoremap <M-o> <C-O>
+ snoremap <M-o> <C-O>
+ endif
+
+ " General fixes to editor behaviour
+ if g:novim_mode_use_editor_fixes == 1
+ " Fix HOME to go back to the first non-whitespace character of the line
+ inoremap <silent> <Home> <C-O>^
+
+ " Tweaks PageUp behaviour to get cursor to first line on top page
+ inoremap <silent> <PageUp> <C-O>:call novim_mode#PageUp()<CR>
+ endif
+
+ " Move between splits, panes, windows, etc and close them
+ if g:novim_mode_use_pane_controls == 1
+ inoremap <silent> <M-Left> <C-O><C-W><Left>
+ nnoremap <silent> <M-Left> <C-W><Left>
+ inoremap <silent> <M-Down> <C-O><C-W><Down>
+ nnoremap <silent> <M-Down> <C-W><Down>
+ inoremap <silent> <M-Up> <C-O><C-W><Up>
+ nnoremap <silent> <M-Up> <C-W><Up>
+ inoremap <silent> <M-Right> <C-O><C-W><Right>
+ nnoremap <silent> <M-Right> <C-W><Right>
+
+ " This allows unsaved buffers to be kept in the background.
+ set hidden
+
+ " CTRL+w to delete current pane-like things.
+ inoremap <silent> <C-W> <C-O>:call novim_mode#ClosePane()<CR>
+ snoremap <silent> <C-W> <C-O>:call novim_mode#ClosePane()<CR>
+ nnoremap <silent> <C-W> :call novim_mode#ClosePane()<CR>
+ end
+
+ " Selecting, copy, paste, etc
+ if g:novim_mode_use_copypasting == 1
+ " One of those curious features of Vim: without `onemore` when pasting
+ " at the end of a line, pasted text gets put *before* the cursor.
+ set virtualedit=onemore
+ " NB. All these use the named 'p' register.
+ inoremap <C-V> <C-O>"pP
+ snoremap <C-C> <C-O>"pyi
+ inoremap <C-C> <C-O>"pY
+ snoremap <C-X> <C-O>"pxi
+ inoremap <C-X> <C-O>"pdd
+ " Select word under cursor
+ inoremap <C-D> <C-O>viw<C-G>
+ " Select current line
+ inoremap <C-L> <C-O>V<C-G>
+ " Append next line to selection
+ snoremap <C-L> <C-O>gj
+
+ " CTRL-A for selecting all text
+ inoremap <C-A> <C-O>gg<C-O>gH<C-O>G
+ snoremap <C-A> <C-O><C-C>gggH<C-O>G
+ endif
+
+ " Indenting
+ if g:novim_mode_use_indenting == 1
+ " TODO: TAB doesn't work in mswin selection mode, but SHIFT+TAB does??
+ snoremap <Tab> <C-O>>gv
+ inoremap <M-]> <C-T>
+ snoremap <M-]> <C-O>>gv
+ " Unindenting
+ snoremap <S-Tab> <C-O><gv
+ inoremap <M-[> <C-D>
+ snoremap <M-[> <C-O><gv
+ endif
+
+ if g:novim_mode_use_finding == 1
+ " Find
+ inoremap <C-F> <C-O>/
+ " Find selected word under cursor
+ snoremap <C-F> <C-O>y/<C-R>"<CR>
+ " Find next
+ inoremap <F3> <C-O>n
+ " Find previous
+ inoremap <S-F3> <C-O>N
+ " Find and replace
+ inoremap <C-H> <C-O>:%s/[FIND]/[REPLACE]/g
+ " Clears highlighting.
+ " NB. Overriding ESC makes it harder to get into NORMAL mode.
+ inoremap <silent> <Esc> <C-O>:noh<CR>
+ endif
+
+ " Undo/redo
+ if g:novim_mode_use_undoing == 1
+ " Use <M-o><C-Z> for native terminal backgrounding.
+ " The <Esc>s used in the `snoremap` commands seem to prevent the selection
+ " process itself being put in the undo history - so now the undo actually undoes
+ " the last *text* activity.
+ inoremap <silent> <C-Z> <C-O>u
+ snoremap <silent> <C-Z> <Esc><C-O>u
+ " Map CTRL+u as well for now just because by default it deletes the line above
+ " the cursor.
+ inoremap <silent> <C-U> <C-O>u
+ snoremap <silent> <C-U> <Esc><C-O>u
+ " Redo
+ inoremap <silent> <C-Y> <C-O><C-R>
+ snoremap <silent> <C-Y> <Esc><C-O><C-R>
+ endif
+
+ " Useful, but not necessarily core or conventional, shortcuts for manipulating
+ " text.
+ if g:novim_mode_use_text_tricks == 1
+ " CTRL+ALTt+k deletes the current line under the cursor
+ inoremap <silent> <C-M-K> <C-O>"_dd
+
+ " CTRL+ALT+d duplicates current line.
+ " NB. Uses the named 'd' register.
+ inoremap <silent> <C-M-D> <C-O>"dyy<C-O>"dp
+
+ " CTRL+DOWN/UP moves the current/selected line(s) up and down
+ inoremap <silent> <C-Up> <C-O>:m -2<CR>
+ snoremap <silent> <C-Up> <C-O>:m '<-2<CR>gv=gv<C-G>
+ inoremap <silent> <C-Down> <C-O>:m +1<CR>
+ snoremap <silent> <C-Down> <C-O>:m '>+1<CR>gv=gv<C-G>
+ endif
+endfunction
+
+" By default Vim treats wrapped text as a single line even though it may
+" appear as many lines on screen. So here we try to make wrapped text behave
+" more conventionally.
+function! s:SetWrappedTextNavigation()
+ " Allow text to wrap in text files
+ au BufNewFile,BufRead *.txt,*.md,*.markdown setlocal linebreak spell
+
+ " Make arrow keys move through wrapped lines
+ " TODO:
+ " * Make END key move to end of current wrapped piece of line.
+ " * Scroll window 1 wrapped soft line at a time rather an entire block of wrapped
+ " lines.
+ au BufNewFile,BufRead *.txt,*.md,*.markdown inoremap <buffer> <Up> <C-O>gk
+ au BufNewFile,BufRead *.txt,*.md,*.markdown inoremap <buffer> <Down> <C-O>gj
+endfunction
+
+" Try to intuitively and intelligently close things like buffers, splits,
+" panes, quicklist, etc, basically anything that looks like a pane.
+function! novim_mode#ClosePane()
+ if s:IsEditableBuffer() == 1
+ " Close any location lists on screen.
+ exe "lclose"
+ " Close any quickfix lists on screen.
+ exe "cclose"
+
+ if s:CountListedBuffers() > 1
+ " By default if the buffer is the only one on screen, closing it closes the
+ " tab/window. So this little trick does a switch to the next buffer,
+ " then closes the previous buffer.
+ exe "bp\|bd #"
+ else
+ quit
+ endif
+ else
+ quit
+ endif
+endfunction
+
+" TODO: Mention any unsaved buffers
+function! novim_mode#ExitVim()
+ let l:confirmed = confirm('Do you really want to quit Vim?', "&Yes\n&No", 2)
+ if l:confirmed == 1
+ quitall!
+ endif
+endfunction
+
+function! novim_mode#GotoLine()
+ let l:line_number = input('Goto line: ')
+ execute line_number
+endfunction
+
+" Just to get PAGEUP to move to the first line when on the first page.
+function! novim_mode#PageUp()
+ " If current line is higher than the size of the buffer
+ if line(".") > s:BufferLines()
+ " Normal PageUp
+ execute "normal! \<C-b>"
+ else
+ " Goto first line
+ execute "normal! gg"
+ endif
+endfunction
+
+function! g:novim_mode#StartNoVimMode()
+ call s:InsertAndSelectionBehaviour()
+
+ if g:novim_mode_use_better_wrap_navigation == 1
+ call s:SetWrappedTextNavigation()
+ endif
+
+ if g:novim_mode_use_shortcuts == 1
+ call g:SetNoVimModeShortcuts()
+ endif
+endfunction
diff --git a/doc/novim-mode.txt b/doc/novim-mode.txt
deleted file mode 100644
index ffff024..0000000
--- a/doc/novim-mode.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-novim-mode.txt
-
-==============================================================================
-CONTENTS |novim-contents|
-
- Introduction ....................................... |novim-introduction|
-
-==============================================================================
-Introduction *novim* *novim-introduction*
-
-Novim-mode provides defaults to Insert Mode whenever appropriate and provides
-a set of keybindings to make Vim behave more like a 'normal' editor.
diff --git a/doc/novim_mode.txt b/doc/novim_mode.txt
new file mode 100644
index 0000000..3758f49
--- /dev/null
+++ b/doc/novim_mode.txt
@@ -0,0 +1,170 @@
+*novim_mode.txt* Vim keybindings for 'conventional' editor behaviour
+
+ NOVIM MODE DOCUMENTATION
+
+==============================================================================
+CONTENTS |novim-mode-contents|
+
+ Introduction ................................... |novim-mode-introduction|
+ Installation ................................... |novim-mode-installation|
+ Usage ................................................. |novim-mode-usage|
+ Keybindings ..................................... |novim-mode-keybindings|
+ Interoperability ........................... |novim-mode-interoperability|
+ Known Issues ................................... |novim-mode-known-issues|
+ References ................................. |novim-mode-known-references|
+
+==============================================================================
+1. Introduction *novim-mode* *novim-mode-introduction*
+
+Novim-mode makes Vim behave like a 'conventional', non-modal editor. Where key
+presses default to inserting text, `CTRL+S` saves the file and so on. In the
+same way that 'vim-mode' plugins are available for other editors to enable
+Vim-style keybindings, so too is 'novim-mode' available in Vim to enable
+'traditional'-style keybindings.
+
+Vim is one of the most battle-tested, ubiquitous and extensible editors
+available. If your muscle memory is already fluent with non-vim keybindings
+then you will be able to instantly benefit from all that is valuable about
+Vim without the learning curve of Normal Mode.
+
+==============================================================================
+2. Installation *novim-mode-installation*
+
+Use your favourite plugin manager, eg, for vim-plug; >
+
+ Plug 'tombh/novim-mode'`
+<
+
+==============================================================================
+3. Usage *novim-mode-usage*
+
+Most keybindings should work as you might expect from, say Atom or Sublime
+Text; `SHIFT+ARROW` to select and `CTRL+C/V` to copy/paste. But don't expect
+Vim to completely bend to your will, it is still useful to familiarise yourself
+with some of Vim's basic concepts. For instance you may on occasion find
+yourself stuck in a particular Vim mode, like when pasting text without 'Paste
+Mode' then inserted text can trigger random mappings. In such case `CTRL+Q`
+may not kill Vim and you'll need to find a way of getting to Normal Mode and
+typing `:q` then `<RETURN>`. Such is life with Vim, this plugin is highly
+unlikely to ever change that. (BTW conventional pasting is on by default,
+but to exit an errant 'Paste Mode' use `:set nopaste`.)
+
+If you are new to Vim, then perhaps the only remaining confusion after installing
+this plugin will be about where files go when you open new ones. This question
+will be answered by Vim's concept of 'buffers'. You may wish to install something
+like [vim-buftabline](https://github.com/ap/vim-buftabline) to give a familiar
+list of open files along the top of the editor.
+
+==============================================================================
+4. Keybindings *novim-mode-keybindings*
+
+Many terminals default to intercepting `CTRL+S` to suspend output, if so you
+will need to disable this behaviour. Most terminals will also bind `CTRL+Q`
+to undo the suspend, useful, but you will also need to unmap that, or change
+the mapping in this plugin for quitting Vim.
+
+`CTRL`-based shortcuts are paired with uppercase letters here because Vim
+does not recognise the difference between cases when using `CTRL` combinations
+and documenting in uppercase implies something of this distinction.
+
+General editor shortcuts
+ * `CTRL+N`: Open a new file.
+ * `CTRL+O`: Open an existing file.
+ * `CTRL+S`: Saves the current file.
+ * `CTRL+G`: Goto line.
+ * `ALT+;`: Vim command prompt.
+ * `ALT+o`: Replaces native `CTRL+O` to give one-off Normal Mode commands.
+
+Pane controls
+ * `ALT+ARROW`: Change pane/buffer focus.
+ * `CTRL+W`: Closes current pane-like thing. Also closes associated
+ quickfix and location panes.
+
+Selecting, copy and paste
+ * `SHIFT+ARROW`: Select text
+ * `CTRL+C`: Copy selection or copy line if no selection.
+ * `CTRL+X`: Cut selection or cut line if no selection.
+ * `CTRL+V`: Paste current selection.
+ * `CTRL+A`: Select all.
+ * `CTRL+D`: Select word under cursor. Use something like vim-multicursors
+ [1] for multi cursor support.
+ * `CTRL+L`: Select line under cursor, repetition selects more lines.
+
+Indenting
+ * `TAB` or `ALT+]`: Indent current line or selected text.
+ _[`TAB` currently broken]_
+ * `SHIFT+TAB` or `ALT+[`: Unindent current line or selected text.
+
+Finding, replacing
+ * `CTRL+F`: Find text. When text is selected that selection is searched
+ for.
+ * `F3` and `SHIFT+F3`: Find next and previous occurences.
+ * `CTRL+H`: Find and replace. `[FIND]` and `[REPLACE]` are prepopulated.
+
+Undoing
+ * `CTRL+Z` or `CTRL+U`: Undo.
+ * `CTRL+Y`: Redo.
+
+Other text manipulation tricks
+ * `CTRL+LEFT/RIGHT`: Move cursor per word (works in selection as well).
+ * `CTRL+ALT+k`: Delete current line.
+ * `CTRL+ALT+k`: Duplicate current line.
+ * `CTRL+UP/DOWN`: Move current line or selected text up/down.
+
+==============================================================================
+5. Interoperability *novim-mode-interoperability*
+
+When adding a new binding of your own that needs Normal mode, you should use
+`<C-O>` before the targeted command, for example; >
+
+ " Ensure CtrlP doesn't get overridden by autocomplete in insertmode
+ inoremap <C-P> <C-O>:CtrlP<CR>
+<
+
+Overriding or disabling shortcuts in this plugin can be done in several
+ways. The simplest way is to use: >
+
+ let g:novim_mode_use_shortcuts = 0
+ inoremap ... custom mapping ...
+ call novim_mode#StartNoVimMode()
+<
+
+Alternatively you can unmap a mapping using commands such as `nunmap`,
+`iunmap`, `sunmap`, etc.
+
+Lastly shorcuts are also grouped roughly under the headings described above,
+so you may be able to disable one of the
+following: >
+
+ let g:novim_mode_use_general_app_shortcuts = 1
+ let g:novim_mode_use_pane_controls = 1
+ let g:novim_mode_use_copypasting = 1
+ let g:novim_mode_use_indenting = 1
+ let g:novim_mode_use_finding = 1
+ let g:novim_mode_use_undoing = 1
+ let g:novim_mode_use_text_tricks = 1
+
+ " Small fixes to HOME and PAGEUP behaviour
+ let g:novim_mode_use_editor_fixes = 1
+ " Allows scrolling through wrapped lines one visual line at a time
+ let g:novim_mode_use_better_wrap_navigation = 1
+<
+==============================================================================
+6. Known Issues *novim-mode-known-issues*
+
+ * There seems to be a bug where only `SHIFT+TAB` and not `TAB` works for
+ indenting during selection mode. Again this may be fixed by simulating
+ selection mode with Visual mode in the future.
+ * When using `novim_mode_use_better_wrap_navigation`, then END key does not
+ go the end of a visual line, but to the end of the physically represented
+ line.
+ * Mapping `<CTRL+m>` internally means mapping `<RETURN>`. This is a
+ throwback to Vim's days as a pure terminal application.
+ * `CTRL+BACKSPACE` internally represents <CTRL+H>, which can be annoying.
+ Again this is a throwback to Vim's days as a pure terminal application.
+
+==============================================================================
+7. References *novim-mode-references*
+
+[1] https://github.com/terryma/vim-multiple-cursors
+
diff --git a/doc/tags b/doc/tags
deleted file mode 100644
index 64a1b6a..0000000
--- a/doc/tags
+++ /dev/null
@@ -1,2 +0,0 @@
-novim novim-mode.txt /*novim*
-novim-introduction novim-mode.txt /*novim-introduction*
diff --git a/plugin/novim-mode.vim b/plugin/novim-mode.vim
deleted file mode 100644
index 5df9bba..0000000
--- a/plugin/novim-mode.vim
+++ /dev/null
@@ -1,92 +0,0 @@
-" Make sure insert mode is the default mode when opening/switching to files
-function! InsertMode()
- if &buftype ==# 'nofile'
- \|| !&modifiable
- \|| &readonly
- \|| bufname('%') =~# 'NERD_tree_'
- exe "set noinsertmode"
- else
- exe "set insertmode"
- endif
-endfunction
-
-augroup start_insertmode
- autocmd!
- autocmd BufEnter * call InsertMode()
-augroup END
-
-" Mostly changes the way selection works.
-" See: http://vimdoc.sourceforge.net/htmldoc/gui.html#:behave
-" An extract from the docs about the difference between `behave mswin`
-" and `behave xterm`:
-" mswin xterm
-" 'selectmode' 'mouse,key' ''
-" 'mousemodel' 'popup' 'extend'
-" 'keymodel' 'startsel,stopsel' ''
-" 'selection' 'exclusive' 'inclusive'
-behave mswin
-
-" Copy and paste stuff
-inoremap <C-V> <C-O>p
-vnoremap <C-C> y
-vnoremap <C-X> x
-
-" CTRL-A for selecting all text
-inoremap <C-A> <C-O>gg<C-O>gH<C-O>G
-cnoremap <C-A> <C-C>gggH<C-O>G
-vnoremap <C-A> <C-C>gggH<C-O>G
-
-" Indenting
-" TODO: TAB doesn't work in mswin selection mode, but SHIFT+TAB does??
-vnoremap <Tab> >
-vnoremap <S-Tab> <
-
-" CTRL+q to exit pane/app
-inoremap <C-Q> <C-O>:q<CR>
-" Useful for exiting buffers like NERDTree that don't use insertmode
-nnoremap <C-Q> :q<CR>
-
-" Find
-inoremap <C-F> <C-O>/
-" Find next
-inoremap <F3> <C-O>n
-" Find previous
-inoremap <S-F3> <C-O>N
-" Clears highlighting. NB. Overriding ESC makes it very hard to get into
-" NORMAL mode.
-inoremap <silent> <Esc> <C-O>:noh<CR>
-
-" Undo/redo
-" Doesn't use Ctrl+Z because that's already a significant *nix shortcut
-" Unfortunately Vim can't use uppercase (shifted) key combos, otherwise
-" we'd use CTRL+SHIFT+U here.
-inoremap <silent> <C-U> <C-O>u
-inoremap <silent> <C-R> <C-O><C-R>
-
-" CTRL+s saves
-inoremap <silent> <C-S> <C-O>:update<CR>
-
-" CTRL+k deletes the current line
-inoremap <silent> <C-K> <C-O>"_dd
-
-" CTRL+d duplicates current line
-" TODO: don't put it in vim's clipboard, so CTRL+V works as expected
-inoremap <silent> <C-D> <C-O>yy<C-O>p
-
-" Alt+/- moves the current line up and down
-inoremap <silent> <M--> <C-O>:m -2<CR>
-inoremap <silent> <M-=> <C-O>:m +1<CR>
-
-" Home goes back to the first non-whitespace character of the line
-inoremap <silent> <Home> <C-O>^
-
-" Allow text to wrap in text files
-au BufNewFile,BufRead *.txt,*.md,*.markdown setlocal linebreak spell
-" Make arrow keys move through wrapped lines
-" TODO:
-" * Make this feature optional.
-" * Make End key move to end of current wrapped piece of line.
-" * Scroll window 1 wrapped soft line at a time rather an entire block of wrapped
-" lines.
-au BufNewFile,BufRead *.txt,*.md,*.markdown inoremap <buffer> <Up> <C-O>gk
-au BufNewFile,BufRead *.txt,*.md,*.markdown inoremap <buffer> <Down> <C-O>gj
diff --git a/plugin/novim_mode.vim b/plugin/novim_mode.vim
new file mode 100644
index 0000000..fbfdb1b
--- /dev/null
+++ b/plugin/novim_mode.vim
@@ -0,0 +1,38 @@
+let s:save_cpo = &cpo
+set cpo&vim
+
+let s:settings = {
+ \ 'use_general_app_shortcuts': 1,
+ \ 'use_editor_fixes': 1,
+ \ 'use_pane_controls': 1,
+ \ 'use_copypasting': 1,
+ \ 'use_indenting': 1,
+ \ 'use_finding': 1,
+ \ 'use_undoing': 1,
+ \ 'use_text_tricks': 1,
+ \ 'use_better_wrap_navigation': 1,
+ \ 'use_shortcuts': 1
+\}
+
+" Fetches existing values from user and sets defaults if not set.
+function! s:init_settings(settings)
+ for [key, value] in items(a:settings)
+ let sub = ''
+ if type(value) == 0
+ let sub = '%d'
+ elseif type(value) == 1
+ let sub = '"%s"'
+ endif
+ let fmt = printf("let g:novim_mode_%%s=get(g:, 'novim_mode_%%s', %s)",
+ \ sub)
+ exec printf(fmt, key, key, value)
+ endfor
+endfunction
+
+call s:init_settings(s:settings)
+
+" Plugin entry point
+call novim_mode#StartNoVimMode()
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
diff --git a/spec/novim_mode_spec.rb b/spec/novim_mode_spec.rb
new file mode 100644
index 0000000..b960b10
--- /dev/null
+++ b/spec/novim_mode_spec.rb
@@ -0,0 +1,105 @@
+# Tip of the hat to terryma/vim-multiple-cursors for this style
+# of testing Vim.
+require 'spec_helper'
+
+TEST_FILE = 'test_file.txt'.freeze
+
+def write_file_content(string)
+ string = normalize_string_indent(string)
+ File.open(TEST_FILE, 'w') { |f| f.write(string) }
+ vim.edit TEST_FILE
+end
+
+def load_file_content
+ vim.write
+ IO.read(TEST_FILE).strip
+end
+
+def before(string)
+ options.each { |x| vim.command(x) } if options
+ write_file_content(string)
+end
+
+def after(string)
+ expect(load_file_content).to eq normalize_string_indent(string)
+end
+
+def type(string)
+ string.scan(/<.*?>|./).each do |key|
+ if key =~ /<.*>/
+ vim.feedkeys "\\#{key}"
+ else
+ vim.feedkeys key
+ end
+ end
+end
+
+describe 'Basic editing' do
+ let(:options) {}
+
+ specify 'writing simple text' do
+ before <<-EOF
+ EOF
+
+ type 'hello world'
+
+ after <<-EOF
+ hello world
+ EOF
+ end
+
+ specify 'copy and paste' do
+ before <<-EOF
+ copy me
+ EOF
+
+ # Conventional behaviour shouldn't need the <End> right?
+ type '<S-End><C-c><End><Space><C-v>'
+
+ after <<-EOF
+ copy me copy me
+ EOF
+ end
+
+ specify 'select all and replace' do
+ before <<-EOF
+ select me
+ EOF
+
+ # Conventional behaviour shouldn't need the <End>?
+ type '<C-a>gone'
+
+ after <<-EOF
+ gone
+ EOF
+ end
+end
+
+describe 'Pane control' do
+ let(:options) {}
+
+ specify 'moving to another pane' do
+ # Open Quickfix window (auto focuses to it)
+ type '<M-;>:copen<CR>'
+ pane_type = vim.command 'echo &buftype'
+ expect(pane_type).to eq 'quickfix'
+
+ # Focus back to file
+ type '<M-Up>'
+ pane_type = vim.command 'echo &buftype'
+
+ expect(pane_type).to eq ''
+ end
+
+ specify 'closing a pane' do
+ # Open Netrw file manager in a sidebar
+ type '<M-;>:Vexplore<CR>'
+ buffer_id = vim.command "echo bufnr('%')"
+ expect(buffer_id).to eq '2'
+
+ # Close Netrw pane
+ type '<C-w>'
+ buffer_id = vim.command "echo bufnr('%')"
+ expect(buffer_id).to eq '1'
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..fa8e548
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,21 @@
+require 'vimrunner'
+require 'vimrunner/rspec'
+
+Vimrunner::RSpec.configure do |config|
+ # Use a single Vim instance for the test suite. Set to false to use an
+ # instance per test (slower, but can be easier to manage).
+ config.reuse_server = false
+
+ # Decide how to start a Vim instance. In this block, an instance should be
+ # spawned and set up with anything project-specific.
+ config.start_vim do
+ vim = Vimrunner.start
+
+ # Setup your plugin in the Vim instance
+ plugin_path = File.expand_path('../..', __FILE__)
+ vim.add_plugin(plugin_path, 'plugin/novim_mode.vim')
+
+ # The returned value is the Client available in the tests.
+ vim
+ end
+end