├── LICENSE ├── README.md └── plugin └── gemini.vim /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 KaraMCC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-gemini 2 | vim-gemini is a customizable vim plugin that automatically adds closing, parenthesis, brackets, quotes, and more! 3 | 4 |
5 | 6 | ![Gemini Gif](https://user-images.githubusercontent.com/56435971/68540940-42799300-0367-11ea-9389-cc77efafc4c2.gif) 7 | 8 | # How do I use it? 9 | Simply type a character that vim-gemini is built to handle, and the plugin will automatically add the closing counterpart. 10 | ``` 11 | What is typed: ("| 12 | What appears ("|") 13 | ``` 14 | 15 | ``` 16 | What is typed: if(foo {| 17 | What appears: 18 | if(foo) { 19 | | 20 | } 21 | ``` 22 | Without modifiying any settings, vim-gemini should start working as soon as it's installed. 23 | 24 |
25 | 26 | # Settings 27 | ### How do I define custom pairs for gemini to complete? 28 | Add this to your .vimrc. The first argument is what type of file the matching will take place in. The second argument is a list containing the opening character and the closing character in position 0 and position 1 respectively. 29 | ``` 30 | let g:gemini#match_list = { 31 | \'': [['', '']] 32 | \} 33 | ``` 34 | 35 |
36 | 37 | You can use regex to expand the use of filetype matches. The example below will allow Gemini to close carets and parentheses, but only when editing an html or xml file. 38 | ``` 39 | let g:gemini#match_list = { 40 | \'html\|xml': [['<', '>'], ['(', ')']] 41 | \} 42 | ``` 43 | 44 |
45 | 46 | Regex can also be used to define universal matches. The example below will automatically close curly brackets and parentheses when editing any file. 47 | ``` 48 | let g:gemini#match_list = { 49 | \'.*': [['{', '}'], ['(', ')']] 50 | \} 51 | ``` 52 | 53 |
54 | 55 | ### How do I change the default matches that Gemini uses? 56 | The plugin uses a list of default matches which can be edited by the user by modifying the global variable ```g:gemini#override_matches```. In the example below, the default matches are changed so that only curly brackets will be matched. 57 | ``` 58 | let g:gemini#override_matches = { 59 | \'.*': [['{', '}']], 60 | \} 61 | ``` 62 | 63 |
64 | 65 | ### How do I allow Gemini to match characters in comments? 66 | By default, Gemini will detect when your cursor is in a commented out area of text, and not complete the matches. You can let the plugin match characters simply by adding this to your .vimrc. 67 | ``` 68 | let g:gemini#match_in_comment = 1 69 | ``` 70 | 71 |
72 | 73 | ### How do I disable matching when the cursor is imediately before a character? 74 | By default, Gemini will append matching characters when the cursor is positioned immediately before a non-whitespace characer. You can disable matching in that scenario by putting ```let g:gemini#cozy_matching = 0``` in your .vimrc. This will make sure Gemini only appends matching characters when there is extra room around the cursor. 75 | 76 |
77 | 78 | # Installation 79 | #### With [pathogen](https://github.com/tpope/vim-pathogen): 80 | ``` 81 | cd ~/.vim/bundle && \ 82 | git clone https://github.com/KaraMCC/vim-gemini.git 83 | ``` 84 | 85 | #### With [vim-plug](https://github.com/junegunn/vim-plug): 86 | ``` 87 | Plug 'KaraMCC/vim-gemini' 88 | ``` 89 | 90 | #### With [Vundle](https://github.com/VundleVim/Vundle.vim): 91 | ``` 92 | Plugin 'KaraMCC/vim-gemini' 93 | ``` 94 | -------------------------------------------------------------------------------- /plugin/gemini.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_gemini_plugin') 2 | finish 3 | endif 4 | let g:loaded_gemini_plugin = 1 5 | 6 | " Define default matches 7 | let s:default_matches = { 8 | \'.*': [['(', ')'], ['{', '}'], ['[', ']'], ['"', '"']], 9 | \'.*python\|.*php\|.*c\|.*cpp\|.*cs\|.*sh\|.*html\|.*xml\|.*vim\|.*perl\|.*rust\|.*java\|.*javascript': [["'", "'"]], 10 | \'.*html\|xml': [['<', '>']], 11 | \'r\|go\|sh\|javascript': [['`', '`']], 12 | \} 13 | " Allow user to override default matches 14 | let s:base_match_list = get(g:, 'gemini#override_matches', s:default_matches) 15 | 16 | " Get user settings 17 | let g:gemini#match_list = get(g:, 'gemini#match_list', {}) 18 | let g:gemini#match_in_comment = get(g:, 'gemini#match_in_comment', 0) 19 | let g:gemini#cozy_matching = get(g:, 'gemini#cozy_matching', 1) 20 | 21 | augroup create_gemini_mappings 22 | autocmd! 23 | autocmd BufEnter * call CreateMappings() 24 | augroup END 25 | 26 | function! s:CreateMatchList() 27 | let b:match_chars = [] 28 | " Get full list of potential matches by combining base matches and matches defined in user settings 29 | let l:potential_matches = extend(s:base_match_list, g:gemini#match_list) 30 | " Loop through all potential matches, and add them if the regex matches 31 | for key in keys(l:potential_matches) 32 | if &filetype =~? key 33 | let b:match_chars += l:potential_matches[key] 34 | endif 35 | endfor 36 | endfunction 37 | 38 | function! s:CreateMappings() 39 | if !exists('b:match_chars') 40 | call s:CreateMatchList() 41 | endif 42 | " Loop through characters to match 43 | for matches in b:match_chars 44 | if matches[0] !=# matches[1] 45 | exec printf('inoremap %s OnOpeningCharacter("\%s","\%s")', matches[0], matches[0], matches[1]) 46 | exec printf('inoremap %s OnClosingCharacter("\%s")', matches[1], matches[1]) 47 | else 48 | " A different function is required if the opening and closing char are the same 49 | exec printf('inoremap %s OnTypedDuplicate("\%s")', matches[0], matches[1]) 50 | endif 51 | endfor 52 | inoremap TryDeletePair() 53 | endfunction 54 | 55 | function! s:OnOpeningCharacter(open, close) 56 | if g:gemini#match_in_comment || !s:IsComment() 57 | " Get the string index of a:open, starting at the cursor's position 58 | let l:open_idx = stridx(getline('.'), a:open, 0) 59 | " Do the same for a:close 60 | let l:close_idx = stridx(getline('.'), a:close, 0) 61 | if l:open_idx == l:close_idx || l:open_idx < l:close_idx && l:open_idx != -1 62 | " Only match if the cursor is next to whitespace -- 63 | " disregard if cozy matching is enabled 64 | if g:gemini#cozy_matching || getline('.')[col('.') - 1] =~? '\s' || col('.') == col('$') 65 | " Add both the characters, then move the cursor to the left 66 | return a:open . a:close . "\" 67 | endif 68 | endif 69 | endif 70 | return a:open 71 | endfunction 72 | 73 | function! s:OnClosingCharacter(close) 74 | " If pair of characters is already closed, move right 75 | if getline('.')[col('.')-1] ==# a:close 76 | return "\" 77 | else 78 | return a:close 79 | endif 80 | endfunction 81 | 82 | function! s:OnTypedDuplicate(char) 83 | " Don't repeat if in comment, unless user has enabled it 84 | if g:gemini#cozy_matching || getline('.')[col('.') - 1] =~? '\s' || col('.') == col('$') 85 | " If the specified character is adjacent to the cursor, do not repeat 86 | if g:gemini#match_in_comment || !s:IsComment() 87 | if stridx(getline('.'), a:char, col('.') - 2) == -1 88 | " Don't close if an even number of a:char exists on the line 89 | if count(getline('.'), a:char) % 2 == 0 90 | return repeat(a:char, 2) . "\" 91 | endif 92 | " If cursor is already on the specified character, move right 93 | elseif getline('.')[col('.') - 1] ==# a:char 94 | return "\" 95 | endif 96 | endif 97 | endif 98 | return a:char 99 | endfunction 100 | 101 | " If the opening character is deleted, also delete the closing one 102 | function! s:TryDeletePair() 103 | for chars in b:match_chars 104 | if getline('.')[col('.')-2] ==# chars[0] && getline('.')[col('.')-1] ==# chars[1] 105 | return "\\\" 106 | endif 107 | endfor 108 | return "\" 109 | endfunction 110 | 111 | function! s:IsComment() 112 | " Return 1 if the name of the syntax id under cursor is 'Comment' 113 | return synIDattr(synIDtrans(synID(line('.'), col('.') - 1, 1)), 'name') ==? 'Comment' 114 | endfunction 115 | --------------------------------------------------------------------------------