diff options
author | Jesse Luehrs <doy@tozt.net> | 2018-03-08 22:00:25 -0500 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2018-03-08 22:00:25 -0500 |
commit | be08e358b9c80919812376848c5bcab0649489b5 (patch) | |
tree | 773eeed35cf54d1f26aca99b1e976055494e843e | |
parent | 03acca189618a0ccd7ba49ef04b531b758be48e1 (diff) | |
download | vim-autobrace-be08e358b9c80919812376848c5bcab0649489b5.tar.gz vim-autobrace-be08e358b9c80919812376848c5bcab0649489b5.zip |
initial implementation
-rw-r--r-- | LICENSE | 19 | ||||
-rw-r--r-- | README.md | 26 | ||||
-rw-r--r-- | doc/autobrace.txt | 36 | ||||
-rw-r--r-- | plugin/autobrace.vim | 146 |
4 files changed, 227 insertions, 0 deletions
@@ -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>" |