├── LICENSE ├── README.md ├── autoload └── miniSnip.vim ├── doc └── miniSnip.txt └── plugin └── miniSnip.vim /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Jorengarenar 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | ┏━━━┓ 3 | ┃┏━┓┃ 4 | ┏┓┏┳┳━┓┏┫┗━━┳━┓┏┳━━┓ 5 | ┃┗┛┣┫┏┓╋╋━━┓┃┏┓╋┫┏┓┃ 6 | ┃┃┃┃┃┃┃┃┃┗━┛┃┃┃┃┃┗┛┃ 7 | ┗┻┻┻┻┛┗┻┻━━━┻┛┗┻┫┏━┛ 8 | ┃┃ 9 | ┗┛ 10 | ``` 11 | 12 | **miniSnip** is lightweight and minimal snippet plugin written in Vim Script. 13 | 14 | ## Installation 15 | 16 | Use your favourite plugin manager to install miniSnip: 17 | 18 | #### [minPlug](https://github.com/Jorengarenar/minPlug): 19 | ```vim 20 | MinPlug Jorengarenar/miniSnip 21 | ``` 22 | 23 | #### [vim-plug](https://github.com/junegunn/vim-plug): 24 | ```vim 25 | Plug 'Jorengarenar/miniSnip' 26 | ``` 27 | 28 | #### Vim's packages 29 | ```bash 30 | cd ~/.vim/pack/plugins/start 31 | git clone git://github.com/Jorengarenar/miniSnip.git 32 | ``` 33 | 34 | #### [NeoBundle](https://github.com/Shougo/neobundle.vim) 35 | ```vim 36 | NeoBundle 'Jorengarenar/miniSnip' 37 | ``` 38 | 39 | ## Usage 40 | 41 | To get started with miniSnip, in your runtime (on UNIX usually: `~/.vim`) 42 | create a directory called `miniSnip/all`. Then placing a file called `foo.snip` 43 | inside it will create the `foo` snippet, which you can access by typing 44 | `foo` in insert mode. 45 | 46 | Filetype-aware snippets are also available. For example, a file called 47 | `main` in `~/.vim/miniSnip/java/main.snip` will create a `main` snippet only when 48 | `filetype=java`, allowing you to add a `main` for C, `main` for C++ and so on. 49 | 50 | See [the documentation](doc/miniSnip.txt) to learn the snippet syntax and options. 51 | 52 | ## Features 53 | 54 | * default values of placeholders (e.g. `<{example}>`) 55 | * references to previous tags (e.g. `<{~2}>` refers to second placeholder) 56 | * evaluation of Vim functions (e.g. `<{!expand('%')}>`) 57 | * ins-completion function 58 | * `<{+}>` will be targeted last (equivalent of `$0` in UltiSnips) 59 | * filetype-aware snippets 60 | * changing delimiters, snippet file filetype etc. (`:h miniSnip-configuration`) 61 | * local snippets (`:h g:miniSnip_local`) 62 | * named placeholders (`:h g:miniSnip_named`) 63 | * global and local to buffer configuration (`:h g:miniSnip-configuration`) 64 | 65 | ## Examples 66 | 67 | * `c/incl.snip` 68 | ``` 69 | ? Include header file 70 | #include "<{!substitute(expand('%:t'), '\.c', '\.h', '')}>" 71 | ``` 72 | 73 | * `html/html.snip` 74 | ``` 75 | ? HTML basic structure 76 | $ `{{` `}}` 77 | 78 | 79 | 80 | 81 | {{Joren}} 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | {{}} 90 | 91 | 92 | ``` 93 | 94 | * `sql/join_no_match.snip` 95 | ``` 96 | SELECT <{table1}>.<{ID_1}> 97 | FROM <{~1}> 98 | LEFT JOIN <{table2}> ON <{~1}>.<{~2}> = <{~3}>.<{ID_2}> 99 | WHERE <{~3}>.<{~4}> IS NULL 100 | ``` 101 | 102 | * `sh/wrapper_exec.snip` 103 | ``` 104 | for dir in $(echo "$PATH" | tr ":" "\n" | grep -Fxv "$(dirname $0)"); do 105 | [ -x "$dir/$(basename $0)" ] && exec "$dir/$(basename $0)" $@ 106 | done 107 | ``` 108 | --- 109 | 110 | For more information, see [the documentation](doc/miniSnip.txt). 111 | 112 | --- 113 | 114 | Based on [vim-minisnip](https://github.com/joereynolds/vim-minisnip) 115 | 116 | Main differences: 117 | * reference tags aren't relative, but absolute, i.e. `<{~1}>` will refer to 118 | first placeholder insted to the previous, `<{~2}>` will refer to the second 119 | instead of the penultimate 120 | * references aren't in quotes 121 | * references to bigger numbers than 9 122 | * descriptions (`:h g:miniSnip_descmark`) 123 | * option to change delimiters for specific snippet (`:h g:miniSnip_delimChg`) 124 | * extending filetypes (`:h g:miniSnip_extends`) 125 | * cursor at the end of snippets without placeholders, not the beginning 126 | * directories instead of prefixes for snippets file management 127 | * respect `expandtab` 128 | * one final placeholder (`:h g:miniSnip_finalTag`) instead of final delimiters 129 | * local snippets 130 | * named placeholders 131 | * configurable character class used to get snippet name 132 | -------------------------------------------------------------------------------- /autoload/miniSnip.vim: -------------------------------------------------------------------------------- 1 | let s:SNIP = {} 2 | 3 | function! s:SID() abort 4 | return matchstr(expand(''), '\d\+_\zeSID$') 5 | endfunction 6 | let s:sid = s:SID() 7 | 8 | function! miniSnip#trigger() abort 9 | let ret = "" 10 | 11 | if empty(s:SNIP) || empty(s:findPlaceholder(1)) 12 | let cword = s:getCword() 13 | let s:SNIP = s:Snip(cword) 14 | if empty(s:SNIP.file) 15 | let s:SNIP = {} 16 | echo "miniSnip: no snippet with key '".cword."'" 17 | return "" 18 | endif 19 | let ret .= "\:call ".s:sid."parseFile()\" 20 | let ret .= "\:call ".s:sid."insertSnippet()\" 21 | else 22 | let ret .= "\:call ".s:sid."replaceRefs()\" 23 | endif 24 | 25 | let ret .= "\:call ".s:sid."selectPlaceholder()\" 26 | 27 | if pumvisible() 28 | call timer_start(50, {-> feedkeys(ret)}) 29 | return "\" 30 | endif 31 | 32 | return ret 33 | endfunction 34 | 35 | function! miniSnip#clear() abort 36 | let s:SNIP = {} 37 | return "" 38 | endfunction 39 | 40 | 41 | function! s:getVar(var) abort 42 | return get(b:, "miniSnip_".a:var, eval("g:miniSnip_".a:var)) 43 | endfunction 44 | 45 | function! s:directories() abort 46 | let filetypes = [] 47 | if !empty(&ft) 48 | let filetypes = [ &ft ] 49 | let filetypes += get(s:getVar("extends"), &ft, []) 50 | endif 51 | let filetypes += [ "all" ] 52 | 53 | let dirs = !empty(s:getVar("local")) ? [ "./" . s:getVar("local") ] : [] 54 | 55 | if empty(s:getVar("dirs")) 56 | let dirs += map(split(&runtimepath, ","), {_, val -> val."/miniSnip" }) 57 | else 58 | let dirs += s:getVar("dirs") 59 | endif 60 | 61 | let ft_dirs = [] 62 | 63 | for dir in dirs 64 | for ft in filetypes 65 | let d = expand(dir) . "/" . ft 66 | if isdirectory(d) 67 | call add(ft_dirs, d) 68 | endif 69 | endfor 70 | endfor 71 | 72 | return ft_dirs 73 | endfunction 74 | 75 | function! s:getCword() abort 76 | return matchstr(getline('.'), s:getVar("exppat") . '\v%' . col('.') . 'c') 77 | endfunction 78 | 79 | function! s:findSnippetFile(cword) abort 80 | let ext = "." . s:getVar("ext") 81 | let files = globpath(join(s:directories(), ','), a:cword.ext, 0, 1) 82 | return len(files) ? files[0] : "" 83 | endfunction 84 | 85 | function! s:Snip(cword) abort 86 | return #{ 87 | \ key: a:cword, 88 | \ file: s:findSnippetFile(a:cword), 89 | \ count: 0, 90 | \ pos: #{ 91 | \ start: line('.'), 92 | \ end: line('.'), 93 | \ start_xy: [], 94 | \ }, 95 | \ marks: #{ 96 | \ op: s:getVar("opening"), 97 | \ ed: s:getVar("closing"), 98 | \ ref: s:getVar("refmark"), 99 | \ named: s:getVar("named"), 100 | \ finalTag: s:getVar("finalTag"), 101 | \ delimChg: s:getVar("delimChg"), 102 | \ desc: s:getVar("descmark"), 103 | \ noskip: s:getVar("noskip"), 104 | \ eval: s:getVar("evalmark"), 105 | \ }, 106 | \ flags: #{ 107 | \ customDelims: 0, 108 | \ named: 0, 109 | \ }, 110 | \ temp: #{ 111 | \ ph_begin_pos: [], 112 | \ }, 113 | \ } 114 | endfunction 115 | 116 | function! s:updatePattern(str) abort 117 | " Check for delimeters changes 118 | if a:str =~ '^\V'.s:SNIP.marks.delimChg 119 | let delims = matchlist(a:str, '\V`\(\.\{-}\)` `\(\.\{-}\)`') 120 | if !empty(delims) 121 | let [ s:SNIP.marks.op, s:SNIP.marks.ed ] = delims[1:2] 122 | let s:SNIP.flags.customDelims = 1 123 | endif 124 | endif 125 | 126 | let op = s:SNIP.marks.op 127 | let ed = s:SNIP.marks.ed 128 | let ref = s:SNIP.marks.ref 129 | let finalTag = s:SNIP.marks.finalTag 130 | 131 | let s:SNIP.patterns = #{ 132 | \ regular: '\V' . op . finalTag . '\@!\.\{-}' . ed, 133 | \ final: '\V' . op . finalTag . ed, 134 | \ counted: '\V'. op . ref . 'COUNT' . ed, 135 | \ named: '~ created in selectPlaceholder()', 136 | \ } 137 | 138 | return s:SNIP.flags.customDelims 139 | endfunction 140 | 141 | function! s:parseFile() abort 142 | let file = readfile(s:SNIP.file) 143 | 144 | " Remove description 145 | if file[0] =~ '^'.(s:SNIP.marks.desc) 146 | call remove(file, 0) 147 | endif 148 | 149 | " If custom delims were applied, remove line with them 150 | if s:updatePattern(file[0]) 151 | call remove(file, 0) 152 | endif 153 | 154 | let s:SNIP.snippet = file 155 | endfunction 156 | 157 | function! s:setPseudoPaste(...) abort 158 | let s = get(a:, 1, {}) 159 | 160 | if !empty(s) 161 | let [ &l:fo ] = [ s.fo ] 162 | let [ &ve, &l:ve ] = [ s.ve, s.lve ] 163 | let [ &l:ai, &l:cin, &l:si, &l:inde ] = [ s.ai, s.cin, s.si, s.inde ] 164 | return 165 | endif 166 | 167 | let [ s.fo, &l:fo ] = [ &l:fo, "l" ] 168 | let [ s.ai, &l:ai ] = [ &l:ai, 0 ] 169 | let [ s.cin, &l:cin ] = [ &l:cin, 0 ] 170 | let [ s.si, &l:si ] = [ &l:si, 0 ] 171 | let [ s.inde, &l:inde ] = [ &l:inde, "" ] 172 | let [ s.ve, s.lve, &ve, &l:ve ] = [ &ve, &l:ve, '', '' ] 173 | return s 174 | endfunction 175 | 176 | function! s:insertSnippet() abort 177 | let snippet = s:SNIP.snippet 178 | 179 | " Adjust indentation (using current line as reference) 180 | let ws = matchstr(getline('.'), '^\s\+') 181 | let snippet = [ "\".snippet[0] ] + map(snippet[1:], 'ws.v:val') 182 | 183 | " Delete snippet key 184 | let snippet += [ strpart(getline('.'), col('.')) ] " save part after snippet 185 | if len(s:SNIP.key) == 1 186 | exec 'norm! "_D' 187 | else 188 | exec 'norm! ?\%'. line('.').'l' . s:getVar("exppat") ."\" . '"_D' 189 | call histdel('/', -1) 190 | endif 191 | 192 | " Get XY position of beginning of the snippet 193 | let s:SNIP.pos.start_xy = getpos('.') 194 | 195 | " Insert snippet 196 | let opts_old = s:setPseudoPaste() 197 | exec "norm! i".join(snippet, "\\i")."\kgJ" 198 | call s:setPseudoPaste(opts_old) 199 | 200 | " Get line the snippet ends at 201 | let s:SNIP.pos.end = line('.') 202 | endfunction 203 | 204 | function! s:evaluate(str) abort 205 | if a:str =~ '\V\^' . s:SNIP.marks.eval 206 | return eval(a:str[1:]) 207 | elseif a:str =~ '\V\^' . (s:SNIP.marks.noskip) . (s:SNIP.marks.eval) 208 | return eval(a:str[2:]) 209 | endif 210 | return a:str 211 | endfunction 212 | 213 | function! s:getInsertedText() abort 214 | let [line_start, column_start] = s:SNIP.temp.ph_begin_pos 215 | let [line_end, column_end] = getpos('.')[1:2] 216 | let lines = getline(line_start, line_end) 217 | if empty(lines) | return "" | endif 218 | let lines[-1] = lines[-1][: column_end-1] 219 | let lines[0] = lines[0][column_start - 1:] 220 | return join(lines, "\n") 221 | endfunction 222 | 223 | function! s:replaceRefs() abort 224 | let txt = escape(s:getInsertedText(), '/\\') 225 | let pos = getpos('.') 226 | let s:SNIP.count += 1 227 | 228 | let cnt_pattern = substitute(s:SNIP.patterns.counted, "COUNT", s:SNIP.count, "") 229 | let boundries = (s:SNIP.pos.start).','.(s:SNIP.pos.end) 230 | 231 | let [ magic_old, &magic ] = [ &magic, 0 ] 232 | silent! exec boundries.'s/'.cnt_pattern.'/'.txt.'/g' 233 | if s:SNIP.flags.named 234 | silent! exec boundries.'s/'.s:SNIP.patterns.named.'/'.txt.'/g' 235 | let s:SNIP.flags.named = 0 236 | endif 237 | let &magic = magic_old 238 | 239 | call setpos('.', pos) 240 | endfunction 241 | 242 | function! s:findPlaceholderPos(pat, dry) abort 243 | let pos = getpos('.') 244 | call setpos('.', s:SNIP.pos.start_xy) 245 | 246 | let [sl, sc] = searchpos(a:pat, 'cw', s:SNIP.pos.end) 247 | if !sl " didn't found any placeholders 248 | call setpos('.', pos) 249 | return "" 250 | endif 251 | let [_, ec] = searchpos(a:pat, 'cnew', s:SNIP.pos.end) 252 | 253 | " Restore cursor position if only checking presence of placeholders 254 | if a:dry | call setpos('.', pos) | endif 255 | 256 | return getline(sl)[sc-1:ec-1] 257 | endfunction 258 | 259 | function! s:findPlaceholder(dry) abort 260 | let ph = s:findPlaceholderPos(s:SNIP.patterns.regular, a:dry) 261 | if empty(ph) 262 | let ph = s:findPlaceholderPos(s:SNIP.patterns.final, a:dry) 263 | endif 264 | return ph 265 | endfunction 266 | 267 | function! s:selectPlaceholder() abort 268 | let ph = s:findPlaceholder(0) 269 | 270 | if empty(ph) 271 | call miniSnip#clear() 272 | call feedkeys('a', 'n') 273 | return 274 | endif 275 | 276 | let ph_begin = virtcol('.') 277 | let s:SNIP.temp.ph_begin_pos = getpos('.')[1:2] 278 | 279 | " Delete placeholder 280 | exec 'norm! "_d'.strchars(ph).'l' 281 | 282 | let op = s:SNIP.marks.op 283 | let ed = s:SNIP.marks.ed 284 | let ph = ph[strchars(op) : -strchars(ed)-1] 285 | 286 | if ph == s:SNIP.marks.finalTag 287 | let ph = "" 288 | endif 289 | 290 | " Check for named reference; if yes, create pattern and set a flag 291 | if ph =~ '\V\^'.s:SNIP.marks.named 292 | let s:SNIP.patterns.named = '\V'.op.ph.ed 293 | let s:SNIP.flags.named = 1 294 | let ph = ph[1:] 295 | endif 296 | 297 | let canSkip = ph =~ '\V\^' . s:SNIP.marks.eval 298 | let ph = s:evaluate(ph) 299 | 300 | " Choose 'append' if placeholder is the last element in a line 301 | let ia = virtcol('.') == ph_begin - 1 ? 'a' : 'i' 302 | 303 | if empty(ph) " the placeholder was empty, so just enter insert mode directly 304 | call feedkeys(ia, 'n') 305 | elseif canSkip 306 | " Placeholder was evaluated and isn't marked 'noskip', so replace references and go to next 307 | exec 'norm! ' . ia . ph 308 | call s:replaceRefs() 309 | call s:selectPlaceholder() 310 | else " paste the placeholder's default value in and enter select mode on it 311 | exec 'norm! '. ia . ph . "\v" . ph_begin . "|o\" 312 | endif 313 | endfunction 314 | 315 | 316 | function! s:buildComp(_, path) abort 317 | let l:name = fnamemodify(a:path, ':t:r') 318 | let l:snip = readfile(a:path) 319 | let l:description = "" 320 | 321 | let descmark = s:getVar("descmark") 322 | if l:snip[0] =~ '^'.descmark 323 | let l:description = substitute(l:snip[0], '^'.descmark.'\s\?', '', '') 324 | endif 325 | 326 | return { 327 | \ 'word': l:name, 328 | \ 'menu': l:description, 329 | \ 'info': join(l:snip, "\n"), 330 | \ 'kind': 's', 331 | \ } 332 | endfunction 333 | 334 | function! miniSnip#completeFunc(findstart, base) abort 335 | if a:findstart 336 | " Locate the start of the word 337 | let line = getline('.') 338 | let start = virtcol('.') - 1 339 | while start > 0 && line[l:start - 1] =~ s:getVar("exppat") 340 | let start -= 1 341 | endwhile 342 | 343 | return start 344 | endif 345 | 346 | " Load all snippets that match. 347 | let dirs = join(s:directories(), ',') 348 | let all = globpath(dirs, a:base.'*.'.s:getVar("ext"), 0, 1) 349 | call filter(all, {_, path -> filereadable(path)}) 350 | call map(all, funcref('s:buildComp')) 351 | call sort(all, 'i') 352 | 353 | return all 354 | endfunction 355 | 356 | function! miniSnip#completeMapping() abort 357 | let cword = matchstr(getline('.'), s:getVar("exppat") . '\v%' . col('.') . 'c') 358 | if cword is# ' ' 359 | let cword = '' 360 | endif 361 | let start = virtcol('.') - len(cword) 362 | call complete(start, miniSnip#completeFunc(0, cword)) 363 | return '' 364 | endfunction 365 | -------------------------------------------------------------------------------- /doc/miniSnip.txt: -------------------------------------------------------------------------------- 1 | *miniSnip* *miniSnip.txt* lightweight snippets 2 | 3 | Author: Jorengarenar 4 | License: MIT 5 | 6 | 1. Overview |miniSnip-overview| 7 | 2. Examples |miniSnip-examples| 8 | 3. Mappings |miniSnip-mappings| 9 | 4. Configuration |miniSnip-configuration| 10 | 5. Miscellaneous |miniSnip-miscellaneous| 11 | 12 | =============================================================================== 13 | OVERVIEW *miniSnip-overview* 14 | 15 | miniSnip is lightweight and minimal snippet plugin written in Vim Script. 16 | 17 | To get started with miniSnip, in your runtime (on UNIX usually: `~/.vim`) 18 | create a directory called `miniSnip/all`. Then placing a file called `foo.snip` 19 | inside it will create the `foo` snippet, which you can access by typing 20 | `foo` in insert mode. 21 | 22 | Filetype-aware snippets are also available. For example, a file called 23 | `main` in `~/.vim/miniSnip/java/main.snip` will create a `main` snippet only when 24 | |filetype|=java, allowing you to add a `main` for C, `main` for C++ and so on. 25 | 26 | You can also use snippets for one filetype in another (e.g. C snippets in C++). 27 | To do so, use |g:miniSnip_extends| 28 | 29 | Features: 30 | * default values of placeholders (e.g. `<{example}>`) 31 | * references to previous tags (e.g. `<{~2}>` refers to second placeholder) 32 | * evaluation of Vim functions (e.g. `<{!expand('%')}>`) 33 | * |ins-completion| function 34 | * `<{+}>` will be targeted last (equivalent of `$0` in UltiSnips) 35 | * filetype-aware snippets 36 | * changing delimiters, snippet file filetype etc. (|miniSnip-configuration|) 37 | * local snippets (|g:miniSnip_local|) 38 | * named placeholders (|g:miniSnip_named|) 39 | * global and local to buffer configuration (|miniSnip-configuration|) 40 | 41 | =============================================================================== 42 | EXAMPLES *miniSnip-examples* 43 | 44 | ------------------------------------------------------------------------------- 45 | c/incl.snip > 46 | ? Include header file 47 | #include "<{!substitute(expand('%:t'), '\.c', '\.h', '')}>" 48 | < 49 | ------------------------------------------------------------------------------- 50 | html/html.snip > 51 | ? HTML basic structure 52 | $ `{{` `}}` 53 | 54 | 55 | 56 | 57 | {{Joren}} 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {{}} 66 | 67 | 68 | < 69 | ------------------------------------------------------------------------------- 70 | sql/join_no_match.snip > 71 | SELECT <{table1}>.<{ID_1}> 72 | FROM <{~1}> 73 | LEFT JOIN <{table2}> ON <{~1}>.<{~2}> = <{~3}>.<{ID_2}> 74 | WHERE <{~3}>.<{~4}> IS NULL 75 | < 76 | ------------------------------------------------------------------------------- 77 | sh/wrapper_exec.snip > 78 | for dir in $(echo "$PATH" | tr ":" "\n" | grep -Fxv "$(dirname $0)"); do 79 | [ -x "$dir/$(basename $0)" ] && exec "$dir/$(basename $0)" $@ 80 | done 81 | < 82 | 83 | =============================================================================== 84 | MAPPINGS *miniSnip-mappings* 85 | 86 | ------------------------------------------------------------------------------- 87 | *miniSnip_* 88 | In Insert mode: expand a mapping or jump to the next 89 | placeholder and enter select mode 90 | In Select mode: jump to the next placeholder 91 | In Normal mode: stop jumping to placeholders 92 | 93 | Can be changed with |g:miniSnip_trigger| 94 | 95 | ------------------------------------------------------------------------------- 96 | *miniSnip_* 97 | Start |ins-completion| for the snippet. 98 | 99 | Can be changed with |g:miniSnip_complkey| 100 | 101 | =============================================================================== 102 | CONFIGURATION *miniSnip-configuration* 103 | 104 | All presented global variables have their |buffer-variable| version, so you can 105 | e.g. change delimeters for filetype without the need of putting 'chane delim' 106 | string in every snippet file. 107 | 108 | ------------------------------------------------------------------------------- 109 | *'g:miniSnip_dirs'* 110 | Default: '[]' 111 | 112 | A list of directories to look for snippet files. If empty, |runtimepath| is used. 113 | 114 | For example to share system-wide snippets: > 115 | let g:miniSnip_dirs = [ '/usr/share/miniSnip', '~/.vim/miniSnip' ] 116 | 117 | ------------------------------------------------------------------------------- 118 | *'g:miniSnip_local'* 119 | Default: ".miniSnip" 120 | 121 | If not empty, will read snippets from directory with that name under current 122 | location. 123 | 124 | ------------------------------------------------------------------------------- 125 | *'g:miniSnip_ext'* 126 | Default: 'snip' 127 | 128 | Snippet filename extension. 129 | 130 | ------------------------------------------------------------------------------- 131 | *'g:miniSnip_extends'* 132 | Default: '{}' 133 | 134 | Example: > 135 | let g:miniSnip_extends = { 136 | \ "cpp" : [ "objc", "c" ], 137 | \ } 138 | < 139 | 140 | Filetype `C++` after loading its own snippets will look first through Objective-C 141 | snippets and then through C snippets (and at the end `all`). Every snippet with 142 | unique name will be imported. 143 | 144 | ------------------------------------------------------------------------------- 145 | *'g:miniSnip_trigger'* 146 | Default: '' 147 | 148 | Specifies which key expands snippets and jumps to the next placeholder. This 149 | should be specified in the same format as you would use for a |:map|. 150 | For example: > 151 | let g:miniSnip_trigger = '' 152 | < 153 | ------------------------------------------------------------------------------- 154 | *'g:miniSnip_complkey'* 155 | Default: '' 156 | 157 | Specifies which key starts |ins-completion| for the snippet. 158 | If default value is kept, sets |completefunc|. Set to empty string to disable. 159 | 160 | ------------------------------------------------------------------------------- 161 | *'g:miniSnip_opening'* 162 | *'g:miniSnip_closing'* 163 | Defaults: '<{', '}>' 164 | 165 | The start and end delimiters of the placeholder string to use. For example, 166 | with the default values, a sample snippet could look like this: > 167 | 168 | 169 | 170 | <{default title}> 171 | 172 | 173 | <{}> 174 | 175 | 176 | < 177 | 178 | ------------------------------------------------------------------------------- 179 | *'g:miniSnip_evalmark'* 180 | Default: '!' 181 | 182 | Marks a template as meant to be executed as VimScript. With the default value, 183 | this looks something like: > 184 | It has been <{!localtime()}> seconds since the Unix epoch 185 | < 186 | The placeholder is not selected, but instead after the evaluation the next 187 | placeholder is selected. This behaviour can be altered with the 188 | |'g:miniSnip_noskip'|. 189 | 190 | ------------------------------------------------------------------------------- 191 | *'g:miniSnip_noskip'* 192 | Default: '`' 193 | 194 | By default when selecting a placeholder with the |'g:miniSnip_evalmark'| the 195 | next placeholder is selected after evaluation. To mark a placeholder which is 196 | to be evaluated so that it is selected after evaluation use the 197 | |'g:miniSnip_noskip'|. This way you can easily modify the value of 198 | the snippet. 199 | 200 | Example: > 201 | <{`!strftime("%Y-%m-%d")}> 202 | < 203 | This will be expanded to the current date and select the timestamp. 204 | 205 | ------------------------------------------------------------------------------- 206 | *'g:miniSnip_refmark'* 207 | Default: '~' 208 | 209 | When this is present, followed by number `N` in a placeholder, it will 210 | be replaced with the `N`th normal (i.e. not eval or another reference) 211 | placeholder's value. 212 | Example: > 213 | #ifndef <{INCLUDE_GUARD}>_H_ 214 | #define <{~1}>_H_ 215 | 216 | <{}> 217 | 218 | #endif 219 | < 220 | This will automatically fill the `#define` line with the value entered on the 221 | `#ifndef` line upon jumping to it and skip to the body between include guards. 222 | 223 | ------------------------------------------------------------------------------- 224 | *'g:miniSnip_named'* 225 | Default: '@' 226 | 227 | It's similiar to |g:miniSnip_refmark|, but instead of numbers operates on default 228 | values, i.e. it replaces all placeholders with the same default value (this 229 | marker included) with the user input. 230 | Example: > 231 | #ifndef <{@foo}>_HPP_ 232 | #define <{~1}>_HPP_ 233 | 234 | namespace <{}> { 235 | 236 | } // <{~2}> 237 | 238 | #endif // <{@foo}> 239 | < 240 | Comment after `#endif` will have the same text as the value entered on the 241 | `#ifndef` line. If user decides to leave default value, then `@` will be trimmed 242 | and only `foo` will be left. 243 | 244 | WARNING! Refmarks still apply! So in the above example, placeholder on `#define` 245 | line will also have the value from `#ifndef` line. 246 | 247 | ------------------------------------------------------------------------------- 248 | *'g:miniSnip_finalTag'* 249 | Default: '+' 250 | 251 | The final placeholder string to use. While the normal placeholders will be 252 | dealt with in the order they appear in the snippet, this placeholder will 253 | be targeted last. > 254 | # BEGIN FUNCTION <{}> <{+}> 255 | <{}> 256 | # END FUNCTION <{~1}> 257 | < 258 | 259 | ------------------------------------------------------------------------------- 260 | *'g:miniSnip_descmark'* 261 | Default: '?' 262 | 263 | When first line of snippet starts with `?` then text proceeding after becames 264 | the description of the snippet, shown in complete menu. 265 | Example: > 266 | ? Shebang 267 | #!/usr/bin/env sh 268 | < 269 | `Shebang` is a description of snippet and `#!/usr/bin/env sh` its actual body. 270 | 271 | ------------------------------------------------------------------------------- 272 | *'g:miniSnip_delimChg'* 273 | Default: '$' 274 | 275 | If first (second if description was provided) line starts with `$`, then change 276 | delimiters in this snippet to strings provided between backtics, e.g.: > 277 | $ `{{` `}}` 278 | let foo = {{22}} 279 | 280 | ------------------------------------------------------------------------------- 281 | *'g:miniSnip_exppat'* 282 | Default: '\w+' 283 | 284 | Specifies which letters/charaters to use to take as snippet name. 285 | This should be specified like character range `[a-z]+` or with class `\w+`. 286 | More about character classes in |/character-classes|. 287 | For example: > 288 | let g:miniSnip_exppat = '\w+' 289 | 290 | =============================================================================== 291 | MISCELLANEOUS *miniSnip-miscellaneous* 292 | 293 | Based on `https://github.com/joereynolds/vim-minisnip` 294 | 295 | Main differences: 296 | * reference tags aren't relative, but absolute, i.e. `<{~1}>` will refer to 297 | first placeholder insted to the previous, `<{~2}>` will refer to the second 298 | instead of the penultimate 299 | * references aren't in quotes 300 | * references to bigger numbers than 9 301 | * descriptions ( |g:miniSnip_descmark| ) 302 | * option to change delimiters for specific snippet ( |g:miniSnip_delimChg| ) 303 | * extending filetypes ( |g:miniSnip_extends| ) 304 | * cursor at the end of snippets without placeholders, not the beginning 305 | * directories instead of prefixes for snippets file management 306 | * respect |expandtab| 307 | * one final placeholder ( |g:miniSnip_finalTag| ) instead of final delimiters 308 | * local snippets 309 | * named placeholders 310 | * configurable character class used to get snippet name 311 | 312 | vim:tw=78:ts=4:ft=help:norl:nofen:isk+=- 313 | -------------------------------------------------------------------------------- /plugin/miniSnip.vim: -------------------------------------------------------------------------------- 1 | " miniSnip - lightweight and minimal snippet plugin 2 | " Maintainer: Jorengarenar 3 | 4 | if exists('g:loaded_miniSnip') | finish | endif 5 | let s:cpo_save = &cpo | set cpo&vim 6 | 7 | let g:miniSnip_dirs = get(g:, 'miniSnip_dirs', []) 8 | let g:miniSnip_trigger = get(g:, 'miniSnip_trigger', '') 9 | let g:miniSnip_complkey = get(g:, 'miniSnip_complkey', '') 10 | let g:miniSnip_extends = get(g:, 'miniSnip_extends', {}) 11 | let g:miniSnip_ext = get(g:, 'miniSnip_ext', 'snip') 12 | let g:miniSnip_local = get(g:, 'miniSnip_local', ".miniSnip") 13 | 14 | let g:miniSnip_delimChg = get(g:, 'miniSnip_delimChg', '$' ) 15 | let g:miniSnip_descmark = get(g:, 'miniSnip_descmark', '?' ) 16 | let g:miniSnip_opening = get(g:, 'miniSnip_opening', '<{') 17 | let g:miniSnip_closing = get(g:, 'miniSnip_closing' , '}>') 18 | let g:miniSnip_refmark = get(g:, 'miniSnip_refmark', '~' ) 19 | let g:miniSnip_evalmark = get(g:, 'miniSnip_evalmark', '!' ) 20 | let g:miniSnip_finalTag = get(g:, 'miniSnip_finalTag', '+' ) 21 | let g:miniSnip_noskip = get(g:, 'miniSnip_noskip', '`' ) 22 | let g:miniSnip_named = get(g:, 'miniSnip_named', '@' ) 23 | let g:miniSnip_exppat = get(g:, 'miniSnip_exppat', '\w\+') 24 | 25 | inoremap