├── README.markdown ├── addon-info.json └── plugin └── enchanted.vim /README.markdown: -------------------------------------------------------------------------------- 1 | # Enchanted Vim 2 | 3 | This is a vim script which makes searches with `\v` (very magic) persistent. 4 | You can turn it off temporarily with 5 | ```viml 6 | let g:VeryMagic = 0 (default is 1) 7 | ``` 8 | or if you are already in the command line you can type `\m` or `\M` (see :help 9 | `\m`). There are also: very magic `substitute`, `global` and `vimgrep` 10 | (`lvimgrep`), which you have to turn on if you want to use them: 11 | ```viml 12 | let g:VeryMagicSubstitute = 1 " (default is 0) 13 | let g:VeryMagicGlobal = 1 " (default is 0) 14 | let g:VeryMagicVimGrep = 1 " (default is 0) 15 | let g:VeryMagicSearchArg = 1 " (default is 0, :edit +/{pattern})) 16 | let g:VeryMagicFunction = 1 " (default is 0, :fun /{pattern}) 17 | let g:VeryMagicHelpgrep = 1 " (default is 0) 18 | let g:VeryMagicRange = 1 " (default is 0, search patterns in command ranges) 19 | let g:VeryMagicEscapeBackslashesInSearchArg = 1 " (default is 0, :edit +/{pattern})) 20 | let g:SortEditArgs = 1 " (default is 0, see below) 21 | ``` 22 | 23 | The `g:VeryMagicSearchArg` turns on the support for very magic `:edit +/pat 24 | file` for various commands which accepts this syntax, i.e. `edit`, `view`, 25 | `visual`, `ex`, `split`, `vsplit`, `new`, `vnew`, `sview`, `find`, `sfind`. 26 | Furthermore with `g:VeryMagicEscapeBackslashesInSearchArg` the backslashes in 27 | the `+/` argument will be escaped (yes vim requires that and probably not only 28 | I forget about this) if there is at least one unescaped backslash (this 29 | prevents from double escaping when resuing the command from command history). 30 | You still need to escape backslashes when you run `vim +/\\vpat` from the 31 | command line, e.g. `vim +"/\\vpattern" file.vim`. 32 | 33 | If you set `g:SortEditArgs = 1` the arguments for `:edit` like commands will 34 | be reordered. This allows to use `:edit file.txt +/pattern` which will be 35 | reordered into `:edit +/pattern file.txt`. 36 | 37 | If you use `incsearch` setting, you probably want to use `g:VeryMagic = 0` and 38 | set two mappings: 39 | ```viml 40 | nm / /\v 41 | nm ? ?\v 42 | ``` 43 | otherwise `incsearch` will not work for patterns which contains non 44 | alphanumeric characters. 45 | 46 | ## How it works 47 | It simply injects `\v` at the beginning of your pattern *after you press enter* 48 | or after c_CTRL-f. 49 | 50 | Note: if you are using one of the two other of my plugins which are defining 51 | maps to <CR> in the command line, you need to update them to the latest 52 | version so that they will all work: 53 | * [System](https://github.com/coot/System) 54 | * [CommandAlias](https://github.com/coot/cmdalias_vim) 55 | 56 | ## Macros 57 | 58 | The plugin works well with vim macros which involve a search command like `/`. 59 | Used register will not contain `\v` but it will by added when you play it. 60 | 61 | # Requirements 62 | You have to also install 63 | [CRDispatcher](https://www.github.com/coot/CRDispatcher) plugin. 64 | -------------------------------------------------------------------------------- /addon-info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EnchantedVim", 3 | "author": "Marcin Szamotulski", 4 | "repository": { 5 | "type": "git", 6 | "url": "git@github.com:coot/EnchantedVim.git", 7 | "script-type": "plugin", 8 | "vim_script_nr": 4849, 9 | }, 10 | "dependencies": { 11 | "CRDispatcher": { 12 | "type": "git", 13 | "url": "git@github.com:coot/CRDispatcher.git", 14 | "script-type": "plugin", 15 | "vim_script_nr": 4856, 16 | }, 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /plugin/enchanted.vim: -------------------------------------------------------------------------------- 1 | " Author: Marcin Szamotulski 2 | " Email: mszamot [AT] gmail [DOT] com 3 | " License: vim-license, see :help license 4 | 5 | if !exists('g:VeryMagic') 6 | let g:VeryMagic = 1 7 | endif 8 | if !exists('g:VeryMagicFilter') 9 | let g:VeryMagicFilter = 0 10 | endif 11 | if !exists('g:VeryMagicSubstitute') 12 | let g:VeryMagicSubstitute = 0 13 | endif 14 | if !exists('g:VeryMagicSubstituteNormalise') 15 | let g:VeryMagicSubstituteNormalise = 0 16 | endif 17 | if !exists('g:VeryMagicGlobal') 18 | let g:VeryMagicGlobal = 0 19 | endif 20 | if !exists('g:VeryMagicVimGrep') 21 | let g:VeryMagicVimGrep = 0 22 | endif 23 | if !exists('g:VeryMagicRange') 24 | let g:VeryMagicRange = 0 25 | endif 26 | if !exists('g:VeryMagicSearchArg') 27 | let g:VeryMagicSearchArg = 0 28 | endif 29 | if !exists('g:VeryMagicFunction') 30 | let g:VeryMagicFunction = 0 31 | endif 32 | if !exists('g:VeryMagicHelpgrep') 33 | let g:VeryMagicHelpgrep = 0 34 | endif 35 | if !exists('g:SmartCaseVimGrep') 36 | let g:SmartCaseVimGrep = 0 37 | endif 38 | if !exists('g:VeryMagicEscapeBackslashesInSearchArg') 39 | " This is very experimental. It has to detect when to escape the pattern 40 | " to not double escape it. 41 | let g:VeryMagicEscapeBackslashesInSearchArg = 0 42 | endif 43 | if !exists('g:SortEditArgs') 44 | let g:SortEditArgs = 0 45 | endif 46 | 47 | let g:DetectVeryMagicPattern = '\v%(%(\\\\)*)@>\\v' " or '^\\v\>' 48 | let g:DetectSmartCasePattern = '\v%(%(\\\\)*)@>\\[cC]' 49 | let g:DetectVeryMagicBackslashEscapedPattern = '\v%(%(\\\\\\\\)*)@>\\\\v' " or '^\\\\v\>' 50 | " The default matches even number of backslashes followed by v. 51 | 52 | fun! s:VeryMagicSearch(dispatcher) "{{{ 53 | " / and ? commands 54 | " a:dispatcher: is crdispatcher#CRDispatcher dict 55 | if !g:VeryMagic || !(a:dispatcher.cmdtype ==# '/' || a:dispatcher.cmdtype ==# '?') 56 | let a:dispatcher.state = 1 57 | return 58 | endif 59 | let cmd = a:dispatcher.cmd 60 | let cmdline = cmd.pattern 61 | let a:dispatcher.state = 1 62 | if !empty(cmdline) && cmdline[0] !=# '/' && cmdline !~# g:DetectVeryMagicPattern 63 | let cmdline = '\v'.cmdline 64 | endif 65 | let cmd.pattern = cmdline 66 | let [char, pattern] = vimlparsers#ParsePattern((a:dispatcher.cmdtype) . cmdline) 67 | let offset = cmdline[(len(pattern)+1):] 68 | " There can be more than one offset 69 | if offset =~ '\s*;\s*[?/]' 70 | let cmdline = cmdline[:len(pattern)] " cut the offset 71 | let new_offset = '' 72 | let o_pat = '^\s*\v(\d+|[+-]\d*|[esb][+-]?\d*)' 73 | while !empty(offset) 74 | let o = matchstr(offset, o_pat) 75 | if !empty(o) 76 | let new_offset .= o 77 | let offset = offset[len(o):] 78 | elseif offset =~ '\s*;\s*[?/]' 79 | let start = matchstr(offset, '\v\s*;\s*[?/]@=') " ? or / 80 | let offset = offset[len(start):] 81 | let [char, pat] = vimlparsers#ParsePattern(offset) 82 | let pat_l = len(pat) 83 | let offset = offset[(pat_l+1):] 84 | let end = matchstr(offset, '[?/]\s*') " empty string, ? or / 85 | let offset = offset[len(end):] 86 | if pat_l && pat !~# g:DetectVeryMagicPattern 87 | let pat = '\v'.pat 88 | endif 89 | let new_offset .= start . char . pat . end 90 | else 91 | let new_offset .= offset 92 | break 93 | endif 94 | endwhile 95 | let cmd.pattern = cmdline.new_offset 96 | endif 97 | let g:VeryMagicLastSearchCmd = cmd.pattern 98 | endfun 99 | try 100 | call add(crdispatcher#CRDispatcher['callbacks'], function('s:VeryMagicSearch')) 101 | catch /E121:/ 102 | echohl ErrorMsg 103 | echom 'EnchantedVim Plugin: please install "https://github.com/coot/CRDispatcher".' 104 | echohl Normal 105 | endtry "}}} 106 | 107 | fun! VeryMagicStar(searchforward, g) "{{{ 108 | " used to replace * and # normal commands 109 | " This keeps the search history clean (no no very magic patterns which 110 | " then would be missunderstood). Another approach would be to use 111 | " normal * 112 | " and only manipulate with the hisory, but this approach is more 113 | " consistent. 114 | let word = expand('') 115 | let pat = escape(word, '.?=@*+&()[]{}^$|/\~') 116 | if !a:g 117 | let pat = '<'.pat.'>' 118 | endif 119 | let pat = '\v'.pat 120 | if !a:searchforward 121 | " emulate vim's behaviour 122 | call search(pat, 'bsc') 123 | call search(pat, 'b') 124 | else 125 | call search(pat, 's') 126 | endif 127 | let g:VeryMagicLastSearchCmd = pat 128 | let @/ = pat 129 | call histadd('/', pat) 130 | endfun 131 | 132 | if g:VeryMagic 133 | " We make this two maps so that the search history contains very magic 134 | " patterns. 135 | nm * :call VeryMagicStar(1, 0) 136 | nm # :call VeryMagicStar(0, 0) 137 | " This map in general should not be necessary, unless isk contains 138 | " characters which needs to be escaped 139 | nm g* :call VeryMagicStar(1, 1) 140 | nm g# :call VeryMagicStar(0, 1) 141 | endif "}}} 142 | 143 | let s:Range = copy(crdispatcher#CallbackClass) "{{{ 144 | fun! s:Range.__transform_cmd__(dispatcher) dict "{{{2 145 | if !g:VeryMagicRange || a:dispatcher.cmdtype !=# ':' 146 | return 147 | endif 148 | let a:dispatcher.state = 1 149 | let range = a:dispatcher.cmd.range 150 | let idx = 0 151 | let new_range = '' 152 | while idx < len(range) 153 | let char = range[idx] 154 | let rest = range[(idx):] 155 | if char ==# '/' || char ==# '?' 156 | let [char, pattern] = vimlparsers#ParsePattern(rest) 157 | let g:VeryMagicLastSearchCmd = pattern 158 | let idx += len(pattern) + 1 " + 1 is added at the end 159 | if pattern !~# g:DetectVeryMagicPattern 160 | let pattern = '\v' . pattern 161 | endif 162 | let new_range .= char . pattern . char 163 | else 164 | let new_range .= char 165 | endif 166 | let idx += 1 167 | endwhile 168 | let a:dispatcher.cmd.range = new_range 169 | endfun "}}}2 170 | try 171 | call add(crdispatcher#CRDispatcher['callbacks'], s:Range) 172 | catch /E121:/ 173 | endtry "}}} 174 | 175 | let s:Substitute = copy(crdispatcher#CallbackClass) "{{{ 176 | call s:Substitute.__init__( 177 | \ 'g:VeryMagicSubstitute', 178 | \ ':', 179 | \ '^\C\v\s*s%[ubstitute]\s*$', 180 | \ 2) 181 | try 182 | call add(crdispatcher#CRDispatcher['callbacks'], s:Substitute) 183 | catch /E121:/ 184 | endtry "}}} 185 | 186 | let s:VimGrep = copy(crdispatcher#CallbackClass) "{{{ 187 | call s:VimGrep.__init__('g:VeryMagicVimGrep', 188 | \ ':', 189 | \ '^\C\v(\s*%(vim%[grep]|lv%[imgrep])\s*)$') 190 | try 191 | call add(crdispatcher#CRDispatcher['callbacks'], s:VimGrep) 192 | catch /E121:/ 193 | endtry "}}} 194 | 195 | let s:SmartCaseVimGrep = copy(crdispatcher#CallbackClass) "{{{ 196 | call s:SmartCaseVimGrep.__init__('g:SmartCaseVimGrep', 197 | \ ':', 198 | \ '^\C\v(\s*%(vim%[grep]|lv%[imgrep])\s*)$') 199 | fun! s:SmartCaseVimGrep.__transform_cmd__(dispatcher) dict 200 | if !{self.variableName} || a:dispatcher.cmdtype !=# self.cmdtype 201 | let a:dispatcher.state = 1 202 | return 203 | endif 204 | let matched = a:dispatcher.cmd.cmd =~# self.pattern 205 | if matched 206 | let a:dispatcher.state = 1 207 | let cmd = a:dispatcher.cmd 208 | let cmd.args = self.__transform_args__(a:dispatcher, cmd.args) 209 | let pat = cmd.pattern 210 | let mlist = matchlist(pat, '\v^(\s*)(.{-})(\s*)$') 211 | let pat = mlist[2] 212 | let pat_len = len(pat) 213 | if pat[0] ==# pat[len(pat)-1] 214 | let pat_len -= 2 215 | let char = pat[0] 216 | let pat = pat[1:len(pat)-2] 217 | else 218 | let char = '' 219 | endif 220 | if pat_len > 0 && pat !~# g:DetectSmartCasePattern && pat =~# '\u' 221 | let cmd.pattern = mlist[1].char.'\C'.pat.char.mlist[3] 222 | endif 223 | let a:dispatcher.cmd = cmd 224 | let a:dispatcher.cmdline = cmd.Join() 225 | endif 226 | endfun 227 | try 228 | call add(crdispatcher#CRDispatcher['callbacks'], s:SmartCaseVimGrep) 229 | catch /E121:/ 230 | endtry "}}} 231 | 232 | let s:Function = copy(crdispatcher#CallbackClass) "{{{ 233 | call s:Function.__init__('g:VeryMagicFunction', 234 | \ ':', 235 | \ '^\C\v\s*fu%[nction]\s*$') 236 | try 237 | call add(crdispatcher#CRDispatcher['callbacks'], s:Function) 238 | catch /E121:/ 239 | endtry "}}} 240 | 241 | let s:Global = copy(crdispatcher#CallbackClass) "{{{ 242 | call s:Global.__init__( 243 | \ 'g:VeryMagicGlobal', 244 | \ ':', 245 | \ '^\v(%(\s*g%[lobal]|v%[global])!?\s*)$') 246 | fun! s:Global.__transform_args__(dispatcher, cmd_args) 247 | let disp = copy(a:dispatcher) 248 | let disp.state = 0 249 | return disp.dispatch(a:dispatcher.ctrl_f, a:cmd_args, a:dispatcher.cmdtype) 250 | endfun 251 | try 252 | call add(crdispatcher#CRDispatcher['callbacks'], s:Global) 253 | catch /E121:/ 254 | endtry "}}} 255 | 256 | fun! s:SortEditArgs(dispatcher) "{{{ 257 | " Rewrite ":edit fname.txt +/pattern" into ":edit +/pattern fname.txt" 258 | " this works for all g:vimlparsers#edit_cmd_pat 259 | let cmd = a:dispatcher.cmd 260 | let c = cmd.cmd 261 | let match = matchstr(c, g:vimlparsers#edit_cmd_pat) 262 | if (!g:SortEditArgs) || a:dispatcher.cmdtype !=# ':' || empty(match) 263 | let a:dispatcher.state = 1 264 | return 265 | endif 266 | let c = c[len(match):] 267 | let _c = { 268 | \ 'cmd': match, 269 | \ 'fname': '', 270 | \ } 271 | while c !~ '^\s*$' 272 | let match = matchstr(c, '\v^\s*\+\/%(\S|%(%(%(\\\\)*)@>\\)@10<=\s)*') 273 | if !empty(match) 274 | let c = c[len(match):] 275 | let _c.cmd .= match 276 | cont 277 | endif 278 | let match = matchstr(c, '\v^\s*\++%(\S|%(%(%(\\\\)*)@>\\)@10<=\s)*') 279 | if !empty(match) 280 | let c = c[len(match):] 281 | let _c.cmd .= match 282 | cont 283 | endif 284 | let match = matchstr(c, '\v^\s*[^+](\S|%(%(%(\\\\)*)@>\\)@10<=\s)*') 285 | if !empty(match) 286 | let c = c[len(match):] 287 | let _c.fname .= match 288 | cont 289 | endif 290 | endwhile 291 | let cmd.cmd = _c.cmd . _c.fname 292 | endfun 293 | try 294 | call add(crdispatcher#CRDispatcher['callbacks'], function('s:SortEditArgs')) 295 | catch /E121:/ 296 | endtry "}}} 297 | 298 | fun! s:VeryMagicSearchArg(dispatcher) "{{{ 299 | " :edit +/pattern but also :view, :sview, :visual, :ex, :split, :vsplit, :new, 300 | " :vnew, :find, :sfind. 301 | if (!g:VeryMagicSearchArg && !g:VeryMagicEscapeBackslashesInSearchArg) || a:dispatcher.cmdtype !=# ':' 302 | let a:dispatcher.state = 2 303 | return 304 | endif 305 | let cmd = a:dispatcher.cmd 306 | let cmdline = cmd.cmd 307 | let pat = g:vimlparsers#edit_cmd_pat . 308 | \ '(\s.{-})'. 309 | \ '(\s@1<=\+/\S@=)'. 310 | \ '(%(\S|%(%(%(%(\\\\)*)@>)\\)@10<=\s)+)'. 311 | \ '(.*)' 312 | let matches = matchlist(cmdline, pat) 313 | if !empty(matches) 314 | let a:dispatcher.state = 1 315 | let pat = matches[4] 316 | if g:VeryMagicEscapeBackslashesInSearchArg && pat =~# '\v\\@1