aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2018-03-08 22:00:25 -0500
committerJesse Luehrs <doy@tozt.net>2018-03-08 22:00:25 -0500
commitbe08e358b9c80919812376848c5bcab0649489b5 (patch)
tree773eeed35cf54d1f26aca99b1e976055494e843e
parent03acca189618a0ccd7ba49ef04b531b758be48e1 (diff)
downloadvim-autobrace-be08e358b9c80919812376848c5bcab0649489b5.tar.gz
vim-autobrace-be08e358b9c80919812376848c5bcab0649489b5.zip
initial implementation
-rw-r--r--LICENSE19
-rw-r--r--README.md26
-rw-r--r--doc/autobrace.txt36
-rw-r--r--plugin/autobrace.vim146
4 files changed, 227 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d59eb6e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright 2018 Jesse Luehrs
+
+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
new file mode 100644
index 0000000..e73dc24
--- /dev/null
+++ b/README.md
@@ -0,0 +1,26 @@
+# Overview
+
+This plugin rebinds some characters to make typing matching characters easier.
+Specifically, it does these things:
+
+* Opening brace and quote characters (specifically, `(`,
+ `[`, `{`, `'`, and `"`) are mapped to make them automatically insert their
+ corresponding closing character.
+* Typing a closing brace or quote character while the next character past the
+ cursor is that character will skip over it instead of inserting it.
+* Pressing <CR> when the next character is a closing brace will insert two
+ newlines, and leave the cursor in the middle.
+* Pressing <BS> when the previous character is an open brace and there is
+ nothing but whitespace between the open brace and closing brace will delete
+ both the open and closing brace.
+
+# Configuration
+
+None yet, but I'm open to suggestions!
+
+# Bugs
+
+Due to limitations in Vimscript, using this plugin will tend to break using
+the `.` operator to repeat insert operations, since the mappings bounce in and
+out of normal mode. Suggestions welcome for how to fix this, but as far as I
+can tell, it's not actually possible to fix in general currently.
diff --git a/doc/autobrace.txt b/doc/autobrace.txt
new file mode 100644
index 0000000..fbb1b2d
--- /dev/null
+++ b/doc/autobrace.txt
@@ -0,0 +1,36 @@
+*autobrace.txt* Automatically insert closing braces and quotes
+
+Author: Jesse Luehrs <https://tozt.net/>
+License: MIT
+
+=============================================================================
+OVERVIEW *autobrace-overview*
+
+This plugin rebinds some characters to make typing matching characters easier.
+Specifically, it does these things:
+
+* Opening brace and quote characters (specifically, `(`,
+ `[`, `{`, `'`, and `"`) are mapped to make them automatically insert their
+ corresponding closing character.
+* Typing a closing brace or quote character while the next character past the
+ cursor is that character will skip over it instead of inserting it.
+* Pressing <CR> when the next character is a closing brace will insert two
+ newlines, and leave the cursor in the middle.
+* Pressing <BS> when the previous character is an open brace and there is
+ nothing but whitespace between the open brace and closing brace will delete
+ both the open and closing brace.
+
+=============================================================================
+CONFIGURATION *autobrace-configuration*
+
+None yet, but I'm open to suggestions!
+
+=============================================================================
+BUGS *autobrace-bugs*
+
+Due to limitations in Vimscript, using this plugin will tend to break using
+the `.` operator to repeat insert operations, since the mappings bounce in and
+out of normal mode. Suggestions welcome for how to fix this, but as far as I
+can tell, it's not actually possible to fix in general currently.
+
+ vim:tw=78:et:ft=help:norl:
diff --git a/plugin/autobrace.vim b/plugin/autobrace.vim
new file mode 100644
index 0000000..acb92b8
--- /dev/null
+++ b/plugin/autobrace.vim
@@ -0,0 +1,146 @@
+if exists('g:loaded_autobrace') || &cp
+ finish
+endif
+let g:loaded_autobrace = 1
+
+let s:pair_chars = {
+\ '(': ')',
+\ '[': ']',
+\ '{': '}',
+\}
+let s:pair_cr_maps = {
+\ ')': "<SID>go_up()",
+\ ']': "<SID>go_up()",
+\ '}': "<SID>go_up()",
+\}
+let s:pair_bs_maps = {
+\ '"': "<SID>maybe_remove_adjacent_char('\"')",
+\ "'": "<SID>maybe_remove_adjacent_char(\"'\")",
+\ '(': "<SID>maybe_remove_empty_pair(')')",
+\ '[': "<SID>maybe_remove_empty_pair(']')",
+\ '{': "<SID>maybe_remove_empty_pair('}')",
+\ '': "<SID>maybe_collapse_pair()",
+\}
+
+function! s:move_cursor_left()
+ return "\<Esc>i"
+endfunction
+
+function! s:skip_closing_char(char)
+ if s:nextchar() == a:char
+ return "\<Esc>la"
+ else
+ return a:char
+ endif
+endfunction
+
+function! s:has_bs_mapping(char)
+ return has_key(s:pair_bs_maps, a:char)
+endfunction
+
+function! s:run_bs_mapping(char)
+ return eval(s:pair_bs_maps[a:char])
+endfunction
+
+function! s:has_cr_mapping(char)
+ return has_key(s:pair_cr_maps, a:char)
+endfunction
+
+function! s:run_cr_mapping(char)
+ return eval(s:pair_cr_maps[a:char])
+endfunction
+
+function! s:go_up()
+ return "\<CR>\<Esc>O"
+endfunction
+
+function! s:maybe_remove_adjacent_char(char)
+ if s:nextchar() == a:char
+ return "\<BS>\<Del>"
+ else
+ return "\<BS>"
+ endif
+endfunction
+
+function! s:maybe_remove_empty_pair(char)
+ let l:start = [line('.'), col('.')]
+ let l:end = searchpos('[^ \t]', 'cnWz')
+ if l:end == [0, 0]
+ return "\<BS>"
+ endif
+
+ let l:next_nonblank = s:charat(l:end[0], l:end[1])
+ if l:next_nonblank != a:char
+ return "\<BS>"
+ endif
+
+ let l:diff = [l:end[0] - l:start[0], l:end[1] - l:start[1]]
+ if l:diff[0] == 0
+ return "\<BS>" . repeat("\<Del>", l:diff[1] + 1)
+ elseif l:diff[0] == 1
+ return "\<Esc>" . (l:diff[0] + 1) . "Ji" . "\<BS>\<Del>"
+ else
+ return "\<Esc>" . (l:diff[0] + 1) . "Ji" . "\<BS>\<BS>\<Del>"
+ endif
+endfunction
+
+function! s:maybe_collapse_pair()
+ let l:prev_line_idx = line('.') - 1
+ if l:prev_line_idx < 1
+ return "\<BS>"
+ endif
+
+ let l:prev_line_char = s:charat(l:prev_line_idx, col([l:prev_line_idx, '$']) - 1)
+ if l:prev_line_char !~ '[([{]'
+ return "\<BS>"
+ endif
+
+ let l:end = searchpos('[^ \t]', 'cnWz')
+ if l:end == [0, 0]
+ return "\<BS>"
+ endif
+
+ let l:next_nonblank = s:charat(l:end[0], l:end[1])
+ if l:next_nonblank != s:pair_chars[l:prev_line_char]
+ return "\<BS>"
+ endif
+
+ return "\<Esc>\<BS>" . (l:end[0] - l:prev_line_idx + 1) . "Ji\<BS>"
+endfunction
+
+function! s:prevchar()
+ return s:charat(line('.'), col('.') - 1)
+endfunction
+
+function! s:nextchar()
+ return s:charat(line('.'), col('.'))
+endfunction
+
+function! s:charat(line, col)
+ return getline(a:line)[a:col - 1]
+endfunction
+
+for [s:start, s:end] in [['(', ')'], ['{', '}'], ['[', ']']]
+ exe "inoremap <silent> ".s:start.
+ \ " ".s:start.s:end."<C-R>=<SID>move_cursor_left()<CR>"
+ exe "inoremap <silent> ".s:end.
+ \ " <C-R>=<SID>skip_closing_char('".s:end."')<CR>"
+endfor
+inoremap <silent><expr> '
+ \ <SID>nextchar() == "'"
+ \ ? "\<C-R>=\<SID>skip_closing_char(\"'\")\<CR>"
+ \ : col('.') == 1 \|\| match(<SID>prevchar(), '\W') != -1
+ \ ? "''\<C-R>=\<SID>move_cursor_left()\<CR>"
+ \ : "'"
+inoremap <silent><expr> "
+ \ <SID>nextchar() == '"'
+ \ ? "\<C-R>=\<SID>skip_closing_char('\"')\<CR>"
+ \ : "\"\"\<C-R>=\<SID>move_cursor_left()\<CR>"
+inoremap <silent><expr> <BS>
+ \ <SID>has_bs_mapping(<SID>prevchar())
+ \ ? "\<C-R>=\<SID>run_bs_mapping(\<SID>prevchar())\<CR>"
+ \ : "\<BS>"
+inoremap <silent><expr> <CR>
+ \ <SID>has_cr_mapping(<SID>nextchar())
+ \ ? "\<C-R>=\<SID>run_cr_mapping(\<SID>nextchar())\<CR>"
+ \ : "\<CR>"