From 66834e720914f4db4f410f31ab69275a9783a194 Mon Sep 17 00:00:00 2001 From: Israel Chauca Fuentes Date: Wed, 26 Nov 2014 15:03:00 -0500 Subject: [PATCH] Allow custom regex for smart_quotes. Close #164. - Use search() to match on the current line and remove most of the old code in IsSmartQuote(). - Allow using ! to negate the pattern. - Add tests. - s:s() :unlet variables before setting them. --- autoload/delimitMate.vim | 26 +++++++++++++------------- doc/delimitMate.txt | 28 ++++++++++++++++++++-------- plugin/delimitMate.vim | 23 ++++++++++++++++++++++- test/autoclose_quotes.txt | 16 ++++++++++++++++ 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/autoload/delimitMate.vim b/autoload/delimitMate.vim index 035e558..b92617a 100644 --- a/autoload/delimitMate.vim +++ b/autoload/delimitMate.vim @@ -20,11 +20,12 @@ function! s:s(name, value, ...) "{{{ let s:options[bufnr] = {} endif if scope == 's' - let name = 'options.' . bufnr . '.' . a:name + let name = 's:options.' . bufnr . '.' . a:name else - let name = 'delimitMate_' . a:name + let name = scope . ':delimitMate_' . a:name endif - exec 'let ' . scope . ':' . name . ' = a:value' + exec 'silent! unlet! ' . name + exec 'let ' . name . ' = a:value' endfunction "}}} function! s:g(name, ...) "{{{ @@ -293,19 +294,18 @@ function! delimitMate#BalancedParens(char) "{{{ endfunction "}}} function! delimitMate#IsSmartQuote(char) "{{{ - if !s:g('smart_quotes') + " TODO: Allow using a:char in the pattern. + let tmp = s:g('smart_quotes') + if empty(tmp) return 0 endif - let char_at = delimitMate#GetCharFromCursor(0) - let char_before = delimitMate#GetCharFromCursor(-1) - let valid_char_re = '\w\|[^[:punct:][:space:]]' - let word_before = char_before =~ valid_char_re - let word_at = char_at =~ valid_char_re - let escaped = delimitMate#CursorIdx() >= 1 - \ && delimitMate#GetCharFromCursor(-1) == '\' + let regex = matchstr(tmp, '^!\?\zs.*') + " Flip matched value if regex starts with ! + let mod = tmp =~ '^!' ? [1, 0] : [0, 1] + let matched = search(regex, 'ncb', line('.')) > 0 let noescaped = substitute(getline('.'), '\\.', '', 'g') let odd = (count(split(noescaped, '\zs'), a:char) % 2) - let result = word_before || escaped || word_at || odd + let result = mod[matched] || odd return result endfunction "delimitMate#SmartQuote }}} @@ -391,7 +391,7 @@ function! delimitMate#QuoteDelim(char) "{{{ " Seems like a smart quote, insert a single char. return a:char elseif (char_before == a:char && char_at != a:char) - \ && s:g('smart_quotes') + \ && !empty(s:g('smart_quotes')) " Seems like we have an unbalanced quote, insert one quotation " mark and jump to the middle. return a:char . "\" diff --git a/doc/delimitMate.txt b/doc/delimitMate.txt index 7d9d33b..3b8da98 100644 --- a/doc/delimitMate.txt +++ b/doc/delimitMate.txt @@ -240,14 +240,25 @@ e.g.: > ------------------------------------------------------------------------------ *'delimitMate_smart_quotes'* *'b:delimitMate_smart_quotes'* -Values: 1 or 0 ~ -Default: 1 ~ +Values: String with an optional ! at the beginning followed by a regexp ~ +Default: '\%(\w\|[^[:punct:][:space:]]\|\%(\\\\\)*\\\)\%#\|\%#\w' ~ + +A bang (!) at the beginning is removed and used to "negate" the pattern. The +remaining text is used as a regexp to be matched on the current line. A single +quote is inserted when the pattern matches and a bang is not present. The bang +changes that, so a single quote is inserted only if the regexp does not match. + +This feature is disabled when the variable is set to an empty string, with the +exception of apostrophes. + +Note that you need to use '\%#' to match the position of the cursor. Keep in +mind that '\%#' matches with zero width, so if you need to match the char +under the cursor (which would be the one to the right on insert mode) use +something like '\%#.'. -This option turns on/off the smart quotes feature. Read -|delimitMateSmartQuotes| for details. e.g.: > - let delimitMate_smart_quotes = 0 - au FileType tcl let b:delimitMate_smart_quotes = 1 + let delimitMate_smart_quotes = '\w\%#' + au FileType tcl let b:delimitMate_smart_quotes = '!\s\%#\w' < ------------------------------------------------------------------------------ *'delimitMate_smart_matchpairs'* @@ -449,8 +460,8 @@ e.g. typing at the "|": > Only one quote will be inserted following a quote, a "\", following or preceding a keyword character, or when the number of quotes in the current line is odd. This should cover closing quotes after a string, opening quotes -before a string, escaped quotes and apostrophes. Except for apostrophes, this -feature can be disabled setting the option |'delimitMate_smart_quotes'| to 0. +before a string, escaped quotes and apostrophes. See more details about +customizing this feature on |'delimitMate_smart_quotes'|. e.g. typing at the "|": > @@ -740,6 +751,7 @@ This script was inspired by the auto-completion of delimiters on TextMate. - Reduce the number of mappings. - Stop using setline(). - Better handling of nested quotes. + - Allow a custom pattern for smart_quotes. |---------|------------|-----------------------------------------------------| 2.6 2011-01-14 * - Add smart_matchpairs feature. - Add mapping to jump over contiguous delimiters. diff --git a/plugin/delimitMate.vim b/plugin/delimitMate.vim index 427c828..2daa942 100644 --- a/plugin/delimitMate.vim +++ b/plugin/delimitMate.vim @@ -107,7 +107,28 @@ function! s:init() "{{{ " smart_matchpairs call s:option_init("smart_matchpairs", '^\%(\w\|\!\|£\|\$\|_\|["'']\s*\S\)') " smart_quotes - call s:option_init("smart_quotes", 1) + " XXX: backward compatibility. Ugly, should go the way of the dodo soon. + let default_smart_quotes = '\%(\w\|[^[:punct:][:space:]]\|\%(\\\\\)*\\\)\%#\|\%#\%(\w\|[^[:space:][:punct:]]\)' + if exists('g:delimitMate_smart_quotes') && type(g:delimitMate_smart_quotes) == type(0) + if g:delimitMate_smart_quotes + unlet g:delimitMate_smart_quotes + else + unlet g:delimitMate_smart_quotes + let g:delimitMate_smart_quotes = '' + endif + endif + if exists('b:delimitMate_smart_quotes') && type(b:delimitMate_smart_quotes) == type(0) + if b:delimitMate_smart_quotes + unlet b:delimitMate_smart_quotes + if exists('g:delimitMate_smart_quotes') && type(g:delimitMate_smart_quotes) && g:delimitMate_smart_quotes + let b:delimitMate_smart_quotes = default_smart_quotes + endif + else + unlet b:delimitMate_smart_quotes + let b:delimitMate_smart_quotes = '' + endif + endif + call s:option_init("smart_quotes", default_smart_quotes) " apostrophes call s:option_init("apostrophes", "") call s:option_init("apostrophes_list", split(s:g('apostrophes'), ":\s*")) diff --git a/test/autoclose_quotes.txt b/test/autoclose_quotes.txt index ef2ce0b..4135c6c 100644 --- a/test/autoclose_quotes.txt +++ b/test/autoclose_quotes.txt @@ -38,6 +38,8 @@ set cpo=ces$ "'x" "'x'" # Make sure smart quote works beyond first column. " 'x" " 'x'" +# smart quote, check fo char on the right. +"a\b\'" "a 'b" # Make sure we jump over a quote on the right. #89. "('test'x" "('test'x)" # Duplicate whole line when inserting quote at bol #105 @@ -54,3 +56,17 @@ unlet g:delimitMate_nesting_quotes # expand iabbreviations iabb def ghi "def'" "ghi'" +let g:delimitMate_smart_quotes = '\w\%#\_.' +"xyz'x" "xyz'x" +"xyz 'x" "xyz 'x'" +let g:delimitMate_smart_quotes = '\s\%#\_.' +"abc'x" "abc'x'" +"abc 'x" "abc 'x" +# let's try the negated form +let g:delimitMate_smart_quotes = '!\w\%#\_.' +"cba'x" "cba'x'" +"cba 'x" "cba 'x" +let g:delimitMate_smart_quotes = '!\s\%#\_.' +"zyx'x" "zyx'x" +"zyx 'x" "zyx 'x'" +unlet g:delimitMate_smart_quotes