├── .gitignore ├── LICENSE ├── after └── plugin │ └── signature.vim ├── plugin └── signature.vim ├── README.md ├── autoload └── signature │ ├── marker.vim │ ├── utils.vim │ ├── sign.vim │ └── mark.vim └── doc └── signature.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/tags 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kartik Shenoy 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 | 23 | -------------------------------------------------------------------------------- /after/plugin/signature.vim: -------------------------------------------------------------------------------- 1 | " vim: fdm=marker:et:ts=4:sw=2:sts=2 2 | 3 | " Maintainer: Kartik Shenoy 4 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 5 | 6 | " Exit if the signs feature is not available or if the app has already been loaded (or "compatible" mode set) 7 | if ( !has('signs') 8 | \ || &cp 9 | \ ) 10 | finish 11 | endif 12 | 13 | "" Exit if vim-signature is not loaded 14 | if !exists('g:loaded_Signature') 15 | finish 16 | endif 17 | 18 | if exists('g:loaded_gitgutter') 19 | if g:SignatureMarkTextHLDynamic 20 | unlet g:SignatureMarkTextHL 21 | let g:SignatureMarkTextHL = function("signature#sign#GetGitGutterHLGroup") 22 | endif 23 | if g:SignatureMarkerTextHLDynamic 24 | unlet g:SignatureMarkerTextHL 25 | let g:SignatureMarkerTextHL = function("signature#sign#GetGitGutterHLGroup") 26 | endif 27 | endif 28 | 29 | if exists('g:loaded_signify') 30 | if g:SignatureMarkTextHLDynamic 31 | unlet g:SignatureMarkTextHL 32 | let g:SignatureMarkTextHL = function("signature#sign#GetSignifyHLGroup") 33 | endif 34 | if g:SignatureMarkerTextHLDynamic 35 | unlet g:SignatureMarkerTextHL 36 | let g:SignatureMarkerTextHL = function("signature#sign#GetSignifyHLGroup") 37 | endif 38 | endif 39 | -------------------------------------------------------------------------------- /plugin/signature.vim: -------------------------------------------------------------------------------- 1 | " vim: fdm=marker:et:ts=4:sw=2:sts=2 2 | 3 | " Description: vim-signature is a plugin to toggle, display and navigate marks. 4 | " 5 | " Maintainer: Kartik Shenoy 6 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 7 | 8 | " Exit if the signs feature is not available or if the app has already been loaded (or "compatible" mode set) 9 | if !has('signs') || &cp 10 | finish 11 | endif 12 | if exists('g:loaded_Signature') 13 | finish 14 | endif 15 | let g:loaded_Signature = 1 16 | 17 | 18 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 19 | "" Global variables {{{1 20 | " 21 | call signature#utils#Set('g:SignaturePrioritizeMarks', 1 ) 22 | call signature#utils#Set('g:SignatureIncludeMarks', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') 23 | call signature#utils#Set('g:SignatureIncludeMarkers', ')!@#$%^&*(' ) 24 | call signature#utils#Set('g:SignatureMarkTextHL', "SignatureMarkText" ) 25 | call signature#utils#Set('g:SignatureMarkTextHLDynamic', 0 ) 26 | call signature#utils#Set('g:SignatureMarkLineHL', "SignatureMarkLine" ) 27 | call signature#utils#Set('g:SignatureMarkerTextHL', "SignatureMarkerText" ) 28 | call signature#utils#Set('g:SignatureMarkerTextHLDynamic', 0 ) 29 | call signature#utils#Set('g:SignatureMarkerLineHL', "SignatureMarkerLine" ) 30 | call signature#utils#Set('g:SignatureWrapJumps', 1 ) 31 | call signature#utils#Set('g:SignatureMarkOrder', "\p\m" ) 32 | call signature#utils#Set('g:SignatureDeleteConfirmation', 0 ) 33 | call signature#utils#Set('g:SignaturePurgeConfirmation', 0 ) 34 | call signature#utils#Set('g:SignaturePeriodicRefresh', 1 ) 35 | call signature#utils#Set('g:SignatureEnabledAtStartup', 1 ) 36 | call signature#utils#Set('g:SignatureDeferPlacement', 1 ) 37 | call signature#utils#Set('g:SignatureRecycleMarks', 0 ) 38 | call signature#utils#Set('g:SignatureErrorIfNoAvailableMarks', 1 ) 39 | call signature#utils#Set('g:SignatureForceRemoveGlobal', 0 ) 40 | call signature#utils#Set('g:SignatureForceMarkPlacement', 0 ) 41 | call signature#utils#Set('g:SignatureForceMarkerPlacement', 0 ) 42 | call signature#utils#Set('g:SignatureMap', {} ) 43 | 44 | 45 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 46 | "" Commands, Autocmds and Maps {{{1 47 | " 48 | call signature#utils#Maps('create') 49 | 50 | if has('autocmd') 51 | augroup sig_autocmds 52 | autocmd! 53 | 54 | " This needs to be called upon loading a colorscheme 55 | " VimEnter is kind of a backup if no colorscheme is explicitly loaded and the default is used 56 | autocmd VimEnter,ColorScheme * call signature#utils#SetupHighlightGroups() 57 | 58 | " This is required to remove signs for global marks that were removed when in another window 59 | autocmd BufEnter,CmdwinEnter * call signature#sign#Refresh() 60 | 61 | autocmd CursorHold * if (g:SignaturePeriodicRefresh) | call signature#sign#Refresh() | endif 62 | augroup END 63 | endif 64 | 65 | command! -nargs=0 SignatureToggleSigns call signature#utils#Toggle() 66 | command! -nargs=0 SignatureRefresh call signature#sign#Refresh(1) 67 | command! -nargs=? SignatureListBufferMarks call signature#mark#List(0, ) 68 | command! -nargs=? SignatureListGlobalMarks call signature#mark#List(1, ) 69 | command! -nargs=* SignatureListMarkers call signature#marker#List() 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### 2017-09-23: 2 | Changed the default value of `g:SignatureForceRemoveGlobal` to 0 since using `:wviminfo!`/`:wshada!` is a very heavy hammer and because it goes against a \*vim default. 3 | 4 | #### 2016-07-08: 5 | Signature used to have a mapping conflict with NERDTree as both try to map the `m` key. 6 | To get around this, I used to delete maps whenever I detected someone entering the NERDTree pane and recreate the maps upon exit. 7 | However, this had a lot of issues and as of [cfa6452](https://github.com/kshenoy/vim-signature/commit/cfa64525305dbb8cec7eefc16e4ea460f007cd33) I've decided to remove any code that was intended to work around this since it wasn't working anyways. Refer to the discussion [here](https://github.com/kshenoy/vim-signature/issues/3#issuecomment-222565292) for more details. 8 | Sorry for any inconvenience. If anyone has any ideas or suggestions please let me know. 9 | 10 | # vim-signature 11 | vim-signature is a plugin to place, toggle and display marks. 12 | 13 | Apart from the above, you can also 14 | * Navigate forward/backward by position/alphabetical order 15 | * Displaying multiple marks (upto 2, limited by the signs feature) 16 | * Placing custom signs !@#$%^&*() as visual markers 17 | 18 | 19 | ### Screenshots 20 | ![vim-signature_marks_markers](https://github.com/kshenoy/vim-signature/blob/images/screens/vim-signature_marks_markers.png?raw=true) 21 | Displays the marks as signs. Also place visual markers 22 | 23 | ![Mark jumps](https://github.com/kshenoy/vim-signature/blob/images/screens/vim-signature_mark_jumps.gif?raw=true) 24 | Alphabetical mark traversal and more. 25 | 26 | ![Dynamic Highlighting](https://github.com/kshenoy/vim-signature/blob/images/screens/vim-signature_dynamic_hl.png?raw=true) 27 | 28 | Also supports dynamic highlighting of signs. In the image above the marks are colored according to the state of the line as indicated by gitgutter. 29 | 30 | NOTE: This feature is disabled by default 31 | 32 | More screenshots [here](http://imgur.com/a/3KQyt) 33 | 34 | ### Vim.org mirror 35 | If you like the plugin, spread the love and rate at http://www.vim.org/scripts/script.php?script_id=4118 36 | 37 | 38 | ## Requirements 39 | Requires Vim to be compiled with +signs to display marks. 40 | 41 | 42 | ## Installation 43 | I recommend using a plugin manager to do the grunt work for you. 44 | If for some reason, you do not want to use any of them, then unzip the contents of the .zip file to your ~/.vim directory. 45 | 46 | Once that's done, out of the box, the followings mappings are defined 47 | 48 | ```` 49 | mx Toggle mark 'x' and display it in the leftmost column 50 | dmx Remove mark 'x' where x is a-zA-Z 51 | 52 | m, Place the next available mark 53 | m. If no mark on line, place the next available mark. Otherwise, remove (first) existing mark. 54 | m- Delete all marks from the current line 55 | m Delete all marks from the current buffer 56 | ]` Jump to next mark 57 | [` Jump to prev mark 58 | ]' Jump to start of next line containing a mark 59 | [' Jump to start of prev line containing a mark 60 | `] Jump by alphabetical order to next mark 61 | `[ Jump by alphabetical order to prev mark 62 | '] Jump by alphabetical order to start of next line having a mark 63 | '[ Jump by alphabetical order to start of prev line having a mark 64 | m/ Open location list and display marks from current buffer 65 | 66 | m[0-9] Toggle the corresponding marker !@#$%^&*() 67 | m Remove all markers of the same type 68 | ]- Jump to next line having a marker of the same type 69 | [- Jump to prev line having a marker of the same type 70 | ]= Jump to next line having a marker of any type 71 | [= Jump to prev line having a marker of any type 72 | m? Open location list and display markers from current buffer 73 | m Remove all markers 74 | ```` 75 | 76 | This will allow the use of default behavior of m to set marks and, if the line 77 | already contains the mark, it'll be unset. 78 | The default behavior of `]'`, `['`, ``]` `` and ``[` `` is supported and enhanced by 79 | wrapping around when beginning or end of file is reached. 80 | 81 | The command `:SignatureToggle` can be used to show/hide the signs. 82 | Note that this does not delete any of the marks but only hides them. 83 | This is a buffer-specific command. 84 | 85 | If for some reason, the marks and their sign displays go out of sync, 86 | use `:SignatureRefresh` to refresh them. 87 | 88 | For more details on customization refer the help 89 | 90 | 91 | ## Thanks to... 92 | * Sergey Khorev for [mark-tools](http://www.vim.org/scripts/script.php?script_id=2929) 93 | * Zak Johnson for [vim-showmarks](https://github.com/zakj/vim-showmarks) 94 | 95 | 96 | ## ToDo: 97 | * Tie the Signature functions to vim commands that affect mark placement 98 | -------------------------------------------------------------------------------- /autoload/signature/marker.vim: -------------------------------------------------------------------------------- 1 | " vim: fdm=marker:et:ts=4:sw=2:sts=1 2 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 3 | 4 | function! signature#marker#Toggle(marker) " {{{1 5 | " Description: Toggle marker on current line 6 | " Arguments: marker [!@#$%^&*()] 7 | 8 | let l:lnum = line('.') 9 | " If marker is found on current line, remove it, else place it 10 | if ( (get(b:sig_markers, l:lnum, "") =~# escape(a:marker, '$^')) 11 | \ && !g:SignatureForceMarkerPlacement 12 | \ ) 13 | call signature#sign#Remove(a:marker, l:lnum) 14 | call signature#sign#ToggleDummy() 15 | else 16 | call signature#sign#Place(a:marker, l:lnum) 17 | endif 18 | endfunction 19 | 20 | 21 | function! signature#marker#Remove(lnum, marker) " {{{1 22 | " Description: Remove marker from specified line number 23 | " Arguments: lnum - Line no. to delete marker from. If is 0, removes marker from current line 24 | " a:2 - Marker to delete. If not specified, obtains input from user 25 | 26 | if (get(b:sig_markers, a:lnum, '') =~ a:marker) 27 | call signature#sign#Remove(a:marker, a:lnum) 28 | endif 29 | endfunction 30 | 31 | 32 | function! signature#marker#Purge(...) " {{{1 33 | " Description: If argument is given, removes marker only of the specified type else all markers are removed 34 | 35 | if empty(b:sig_markers) | return | endif 36 | if g:SignaturePurgeConfirmation 37 | let choice = confirm('Are you sure you want to delete all markers? This cannot be undone.', '&Yes\n&No', 1) 38 | if choice == 2 | return | endif 39 | endif 40 | 41 | if a:0 > 0 42 | let l:markers = [ a:1 ] 43 | else 44 | let l:markers = split(b:SignatureIncludeMarkers, '\zs') 45 | endif 46 | 47 | for l:marker in l:markers 48 | for l:lnum in keys(filter(copy(b:sig_markers), 'v:val =~# l:marker')) 49 | call signature#marker#Remove(l:lnum, l:marker) 50 | endfor 51 | endfor 52 | call signature#sign#ToggleDummy() 53 | endfunction 54 | 55 | 56 | function! signature#marker#Goto( dir, marker_num, count ) " {{{1 57 | " Description: Jump to next/prev marker by location. 58 | " Arguments: dir = next : Jump forward 59 | " prev : Jump backward 60 | " marker = same : Jump to a marker of the same type 61 | " any : Jump to a marker of any type 62 | " [0-9] : Jump to the corresponding marker 63 | 64 | let l:lnum = line('.') 65 | 66 | let l:marker = '' 67 | if (a:marker_num =~ '\v<[0-9]>') 68 | let l:marker = split(b:SignatureIncludeMarkers, '\zs')[a:marker_num] 69 | elseif ( (a:marker_num ==? 'same') 70 | \ && has_key(b:sig_markers, l:lnum) 71 | \ ) 72 | let l:marker = signature#utils#GetChar(b:sig_markers[l:lnum], 0) 73 | endif 74 | 75 | " Get list of line numbers of lines with markers. 76 | " If current line has a marker, filter out line numbers of other markers ... 77 | if (l:marker != '') 78 | let l:marker_lnums = sort(keys(filter(copy(b:sig_markers), 79 | \ 'signature#utils#GetChar(v:val, 0) == l:marker')), "signature#utils#NumericSort") 80 | else 81 | let l:marker_lnums = sort(keys(b:sig_markers), "signature#utils#NumericSort") 82 | endif 83 | 84 | if (a:dir ==? 'next') 85 | let l:marker_lnums = filter(copy(l:marker_lnums), ' v:val > l:lnum') 86 | \ + filter(copy(l:marker_lnums), '(v:val <= l:lnum) && b:SignatureWrapJumps') 87 | elseif (a:dir ==? 'prev') 88 | call reverse(l:marker_lnums) 89 | let l:marker_lnums = filter(copy(l:marker_lnums), ' v:val < l:lnum') 90 | \ + filter(copy(l:marker_lnums), '(v:val >= l:lnum) && b:SignatureWrapJumps') 91 | endif 92 | 93 | if (len(l:marker_lnums) == 0) 94 | return 95 | endif 96 | 97 | let l:count = (a:count == 0 ? 1 : a:count) 98 | if (b:SignatureWrapJumps) 99 | let l:count = l:count % len(l:marker_lnums) 100 | elseif (l:count > len(l:marker_lnums)) 101 | let l:count = 0 102 | endif 103 | 104 | let l:targ = l:marker_lnums[l:count - 1] 105 | execute 'normal! ' . l:targ . 'G' 106 | endfunction 107 | 108 | 109 | function! signature#marker#List(...) " {{{1 110 | " Description: Opens and populates location list with markers from current buffer 111 | " Show all markers in location list if no argument is provided 112 | " Argument: [markers] = 0-9 or any of the specified symbols : List only the specified markers 113 | " [context] = 0 (default) : Adds context around marker 114 | " To show all markers with 1 line of context call using arguments ("", 1) 115 | 116 | let l:markers = (a:0 && (a:1 != "") ? a:1 : b:SignatureIncludeMarkers) 117 | let l:context = (a:0 > 1 ? a:2 : 0) 118 | 119 | if (l:markers =~ '^\d$') 120 | if ( ( (l:markers == 0) 121 | \ && (len(b:SignatureIncludeMarkers) != 10) 122 | \ ) 123 | \ || (l:markers > len(b:SignatureIncludeMarkers)) 124 | \ ) 125 | echoe "Signature: No corresponding marker exists for " . l:markers 126 | return 127 | endif 128 | let l:markers = split(b:SignatureIncludeMarkers, '\zs')[l:markers] 129 | endif 130 | 131 | let l:lines_tot = line('$') 132 | let l:buf_curr = bufnr('%') 133 | let l:list_sep = {'bufnr': '', 'lnum' : ''} 134 | let l:list = [] 135 | 136 | " Markers not specified in b:SignatureIncludeMarkers won't be present in b:sig_markers and hence get filtered out 137 | for l:lnum in sort(keys(filter(copy(b:sig_markers), 'v:val =~ "[" . l:markers . "]"'))) 138 | 139 | for context_lnum in range(l:lnum - l:context, l:lnum + l:context) 140 | if ( (context_lnum < 1) 141 | \ || (context_lnum > lines_tot) 142 | \ ) 143 | continue 144 | endif 145 | 146 | if (context_lnum < l:lnum) | let l:text = '-' . ": " . getline(context_lnum) 147 | elseif (context_lnum > l:lnum) | let l:text = '+' . ": " . getline(context_lnum) 148 | else | let l:text = b:sig_markers[l:lnum] . ": " . getline(context_lnum) 149 | endif 150 | 151 | let l:list = add(l:list, 152 | \ { 'text' : l:text, 153 | \ 'bufnr': l:buf_curr, 154 | \ 'lnum' : context_lnum, 155 | \ 'type' : 'M' 156 | \ } 157 | \ ) 158 | endfor 159 | 160 | " Add separator when showing context 161 | "if (a:context > 0) 162 | " let l:list = add(l:list, l:list_sep) 163 | "endif 164 | endfor 165 | 166 | " Remove the redundant separator at the end when showing context 167 | "if ( (a:context > 0) 168 | " \ && (len(l:list) > 0) 169 | " \ ) 170 | " call remove(l:list, -1) 171 | "endif 172 | 173 | call setloclist(0, l:list,) | lopen 174 | endfunction 175 | -------------------------------------------------------------------------------- /autoload/signature/utils.vim: -------------------------------------------------------------------------------- 1 | " vim: fdm=marker:et:ts=4:sw=2:sts=2 2 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 3 | 4 | function! signature#utils#Set(var, value, ...) " {{{1 5 | " Description: Assign value to var if var is unset or if an optional 3rd arg is provided to force 6 | 7 | if (!exists(a:var) || a:0 && a:1) 8 | if type(a:value) 9 | execute 'let' a:var '=' string(a:value) 10 | else 11 | execute 'let' a:var '=' a:value 12 | endif 13 | endif 14 | return a:var 15 | endfunction 16 | 17 | 18 | function! signature#utils#NumericSort(x, y) " {{{1 19 | return a:x - a:y 20 | endfunction 21 | 22 | 23 | function! s:Map(mode, key, map_lhs_default, map_rhs) " {{{1 24 | let l:map_lhs = get(g:SignatureMap, a:key, a:map_lhs_default) 25 | if (l:map_lhs ==? '') 26 | return 27 | endif 28 | if (a:mode ==? 'create') 29 | silent! execute 'nnoremap ' . l:map_lhs . ' ' . ':call signature#' . a:map_rhs . '' 30 | elseif (a:mode ==? 'remove') 31 | silent! execute 'nunmap ' . l:map_lhs 32 | endif 33 | endfunction 34 | 35 | function! signature#utils#Maps(mode) " {{{1 36 | " We create separate mappings for PlaceNextMark, mark#Purge('all') and PurgeMarkers instead of combining it with 37 | " Leader/Input as if the user chooses to use some weird key like or for any of these 3, we need to be able 38 | " to identify it. Eg. the nr2char(getchar()) will fail if the user presses a 39 | let l:SignatureMapLeader = get(g:SignatureMap, 'Leader', 'm') 40 | if (l:SignatureMapLeader == "") 41 | echoe "Signature: g:SignatureMap.Leader shouldn't be left blank" 42 | endif 43 | call s:Map(a:mode, 'Leader' , l:SignatureMapLeader , 'utils#Input()' ) 44 | call s:Map(a:mode, 'PlaceNextMark' , l:SignatureMapLeader . "," , 'mark#Toggle("next")' ) 45 | call s:Map(a:mode, 'ToggleMarkAtLine' , l:SignatureMapLeader . "." , 'mark#ToggleAtLine()' ) 46 | call s:Map(a:mode, 'PurgeMarksAtLine' , l:SignatureMapLeader . "-" , 'mark#Purge("line")' ) 47 | call s:Map(a:mode, 'PurgeMarks' , l:SignatureMapLeader . "", 'mark#Purge("all")' ) 48 | call s:Map(a:mode, 'PurgeMarkers' , l:SignatureMapLeader . "" , 'marker#Purge()' ) 49 | call s:Map(a:mode, 'DeleteMark' , "dm" , 'utils#Remove(v:count)' ) 50 | call s:Map(a:mode, 'GotoNextLineAlpha', "']" , 'mark#Goto("next", "line", "alpha")' ) 51 | call s:Map(a:mode, 'GotoPrevLineAlpha', "'[" , 'mark#Goto("prev", "line", "alpha")' ) 52 | call s:Map(a:mode, 'GotoNextSpotAlpha', "`]" , 'mark#Goto("next", "spot", "alpha")' ) 53 | call s:Map(a:mode, 'GotoPrevSpotAlpha', "`[" , 'mark#Goto("prev", "spot", "alpha")' ) 54 | call s:Map(a:mode, 'GotoNextLineByPos', "]'" , 'mark#Goto("next", "line", "pos")' ) 55 | call s:Map(a:mode, 'GotoPrevLineByPos', "['" , 'mark#Goto("prev", "line", "pos")' ) 56 | call s:Map(a:mode, 'GotoNextSpotByPos', "]`" , 'mark#Goto("next", "spot", "pos")' ) 57 | call s:Map(a:mode, 'GotoPrevSpotByPos', "[`" , 'mark#Goto("prev", "spot", "pos")' ) 58 | call s:Map(a:mode, 'GotoNextMarker' , "]-" , 'marker#Goto("next", "same", v:count)') 59 | call s:Map(a:mode, 'GotoPrevMarker' , "[-" , 'marker#Goto("prev", "same", v:count)') 60 | call s:Map(a:mode, 'GotoNextMarkerAny', "]=" , 'marker#Goto("next", "any", v:count)') 61 | call s:Map(a:mode, 'GotoPrevMarkerAny', "[=" , 'marker#Goto("prev", "any", v:count)') 62 | call s:Map(a:mode, 'ListBufferMarks' , 'm/' , 'mark#List(0, 0)' ) 63 | call s:Map(a:mode, 'ListBufferMarkers', 'm?' , 'marker#List(v:count, 0)' ) 64 | endfunction 65 | 66 | 67 | function! signature#utils#Input() " {{{1 68 | " Description: Grab input char 69 | 70 | if &ft ==# "netrw" 71 | " Workaround for #104 72 | return 73 | endif 74 | 75 | " Obtain input from user ... 76 | let l:in = nr2char(getchar()) 77 | 78 | " ... if the input is not a number eg. '!' ==> Delete all '!' markers 79 | if signature#utils#IsValidMarker(l:in) 80 | return signature#marker#Purge(l:in) 81 | endif 82 | 83 | " ... but if input is a number, convert it to corresponding marker before proceeding 84 | if match(l:in, '\d') >= 0 85 | let l:char = signature#utils#GetChar(b:SignatureIncludeMarkers, l:in) 86 | else 87 | let l:char = l:in 88 | endif 89 | 90 | if signature#utils#IsValidMarker(l:char) 91 | return signature#marker#Toggle(l:char) 92 | elseif signature#utils#IsValidMark(l:char) 93 | return signature#mark#Toggle(l:char) 94 | else 95 | " l:char is probably one of `'[]<> or a space from the gap in b:SignatureIncludeMarkers 96 | execute 'normal! m' . l:in 97 | endif 98 | endfunction 99 | 100 | 101 | function! signature#utils#Remove(lnum) " {{{1 102 | " Description: Obtain mark or marker from the user and remove it. 103 | " There can be multiple markers of the same type on different lines. If a line no. is provided 104 | " (non-zero), delete the marker from the specified line else delete it from the current line 105 | " NOTE: lnum is meaningless for a mark and will be ignored 106 | " Arguments: lnum - Line no. to delete the marker from 107 | 108 | let l:char = nr2char(getchar()) 109 | 110 | if (l:char =~ '^\d$') 111 | let l:lnum = (a:lnum == 0 ? line('.') : a:lnum) 112 | let l:char = split(b:SignatureIncludeMarkers, '\zs')[l:char] 113 | call signature#marker#Remove(lnum, l:char) 114 | elseif (l:char =~? '^[a-z]$') 115 | call signature#mark#Remove(l:char) 116 | endif 117 | endfunction 118 | 119 | 120 | function! signature#utils#Toggle() " {{{1 121 | " Description: Toggles and refreshes sign display in the buffer. 122 | 123 | let b:sig_enabled = !b:sig_enabled 124 | 125 | if b:sig_enabled 126 | " Signature enabled ==> Refresh signs 127 | call signature#sign#Refresh() 128 | 129 | " Add signs for markers ... 130 | for i in keys(b:sig_markers) 131 | call signature#sign#Place(b:sig_markers[i], i) 132 | endfor 133 | else 134 | " Signature disabled ==> Remove signs 135 | for l:lnum in keys(b:sig_markers) 136 | call signature#sign#Unplace(l:lnum) 137 | endfor 138 | for l:lnum in keys(b:sig_marks) 139 | call signature#sign#Unplace(l:lnum) 140 | endfor 141 | " Force removal. Simply toggling doesn't work as we check whether b:sig_markers and b:sig_marks are empty before 142 | " removing the dummy and b:sig_markers won't be empty 143 | call signature#sign#ToggleDummy(0) 144 | unlet b:sig_marks 145 | endif 146 | endfunction 147 | 148 | 149 | function! signature#utils#SetupHighlightGroups() " {{{1 150 | " Description: Sets up the highlight groups 151 | 152 | function! CheckAndSetHL(curr_hl, prefix, attr, targ_color) 153 | let l:curr_color = synIDattr(synIDtrans(hlID(a:curr_hl)), a:attr, a:prefix) 154 | 155 | if ( ( (l:curr_color == "") 156 | \ || (l:curr_color < 0) 157 | \ ) 158 | \ && (a:targ_color != "") 159 | \ && (a:targ_color >= 0) 160 | \ ) 161 | " echom "DEBUG: HL=" . a:curr_hl . " (" . a:prefix . a:attr . ") Curr=" . l:curr_color . ", To=" . a:targ_color 162 | execute 'highlight ' . a:curr_hl . ' ' . a:prefix . a:attr . '=' . a:targ_color 163 | endif 164 | endfunction 165 | 166 | let l:prefix = (has('gui_running') || (has('termguicolors') && &termguicolors) ? 'gui' : 'cterm') 167 | let l:sign_col_color = synIDattr(synIDtrans(hlID('SignColumn')), 'bg', l:prefix) 168 | 169 | call CheckAndSetHL('SignatureMarkText', l:prefix, 'fg', 'Red') 170 | call CheckAndSetHL('SignatureMarkText', l:prefix, 'bg', l:sign_col_color) 171 | call CheckAndSetHL('SignatureMarkerText', l:prefix, 'fg', 'Green') 172 | call CheckAndSetHL('SignatureMarkerText', l:prefix, 'bg', l:sign_col_color) 173 | 174 | delfunction CheckAndSetHL 175 | endfunction 176 | 177 | 178 | function! signature#utils#IsValidMark(mark) " {{{1 179 | return (b:SignatureIncludeMarks =~# a:mark) 180 | endfunction 181 | 182 | 183 | function! signature#utils#IsValidMarker(marker) " {{{1 184 | return ( (b:SignatureIncludeMarkers =~# a:marker) 185 | \ && (a:marker != ' ') 186 | \ ) 187 | endfunction 188 | 189 | 190 | function! signature#utils#GetChar(string, pos) " {{{1 191 | if a:pos > strchars(a:string) - 1 | return "" | endif 192 | let pattern = '.\{-' . a:pos . '}\(.\).*' 193 | return substitute(a:string, pattern, '\1', '') 194 | endfunction 195 | 196 | -------------------------------------------------------------------------------- /autoload/signature/sign.vim: -------------------------------------------------------------------------------- 1 | " vim: fdm=marker:et:ts=4:sw=2:sts=2 2 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 3 | 4 | function! signature#sign#Place(sign, lnum) "{{{1 5 | " Description: Place signs for marks/markers on the specified line number 6 | " Arguments: 7 | " sign : The mark/marker whose sign is to be placed 8 | " lnum : Line number on/from which the sign is to be placed/removed 9 | 10 | "echom "DEBUG: sign = " . a:sign . ", lnum = " . a:lnum 11 | 12 | " If Signature is not enabled, return 13 | if !b:sig_enabled | return | endif 14 | 15 | " FIXME: Highly inefficient. Needs work 16 | " Place sign only if there are no signs from other plugins (eg. syntastic) 17 | "let l:present_signs = s:GetInfo(1) 18 | "if ( b:SignatureDeferPlacement 19 | " \ && has_key(l:present_signs, a:lnum) 20 | " \ && (l:present_signs[a:lnum]['name'] !~# '^sig_Sign_') 21 | " \ ) 22 | " return 23 | "endif 24 | 25 | if signature#utils#IsValidMarker(a:sign) 26 | let b:sig_markers[a:lnum] = a:sign . get(b:sig_markers, a:lnum, "") 27 | elseif signature#utils#IsValidMark(a:sign) 28 | let b:sig_marks[a:lnum] = a:sign . get(b:sig_marks, a:lnum, "") 29 | else 30 | echoerr "Unexpected sign found: " . a:sign 31 | endif 32 | "}}}3 33 | 34 | call s:RefreshLine(a:lnum) 35 | endfunction 36 | 37 | 38 | function! signature#sign#Remove(sign, lnum) "{{{1 39 | " Description: Remove signs for marks/markers from the specified line number 40 | " Arguments: 41 | " sign : The mark/marker whose sign is to be placed/removed/toggled 42 | " lnum : Line number from which the sign is to be removed 43 | " If sign is a marker and lnum is 0, the sign will be removed from all lines 44 | " If sign is a mark and lnum is 0, the lnum will be found and the sign will be removed from that line 45 | 46 | "echom "DEBUG: sign = " . a:sign . ", lnum = " . a:lnum 47 | 48 | " If Signature is not enabled, return 49 | if !b:sig_enabled | return | endif 50 | 51 | " Remove sign for markers 52 | if signature#utils#IsValidMarker(a:sign) 53 | let b:sig_markers[a:lnum] = substitute(b:sig_markers[a:lnum], "\\C" . escape( a:sign, '$^' ), "", "") 54 | 55 | " If there are no markers on the line, delete signs on that line 56 | if b:sig_markers[a:lnum] == "" 57 | call remove(b:sig_markers, a:lnum) 58 | endif 59 | call s:RefreshLine(a:lnum) 60 | 61 | " Remove sign for marks 62 | else 63 | " For marks, if a:lnum == 0, find out the line where the mark was placed 64 | if a:lnum == 0 65 | let l:arr = keys(filter(copy(b:sig_marks), 'v:val =~# a:sign')) 66 | if empty(l:arr) | return | endif 67 | else 68 | let l:arr = [a:lnum] 69 | endif 70 | if (v:version >= 800) 71 | call assert_true(len(l:arr) == 1, "Multiple marks found where one was expected") 72 | elseif (len(l:arr) != 1) 73 | echoerr "Multiple marks found where one was expected" 74 | endif 75 | 76 | for l:lnum in l:arr 77 | " FIXME: Placed guard to avoid triggering issue #53 78 | if has_key(b:sig_marks, l:lnum) 79 | let b:sig_marks[l:lnum] = substitute(b:sig_marks[l:lnum], "\\C" . a:sign, "", "") 80 | " If there are no marks on the line, delete signs on that line 81 | if b:sig_marks[l:lnum] == "" 82 | call remove(b:sig_marks, l:lnum) 83 | endif 84 | endif 85 | call s:RefreshLine(l:lnum) 86 | endfor 87 | endif 88 | endfunction 89 | 90 | 91 | function! s:EvaluateHL(expr, lnum, ...) "{{{1 92 | " Description: If expr points to a function, call it and use its output as the highlight group. 93 | " If it is a string, use it directly. 94 | " If the optional argument is specified, use it as a fallback. If not, return an empty string 95 | 96 | if type(a:expr) == type("") 97 | return a:expr 98 | elseif type(a:expr) == type(function("tr")) 99 | let l:retval = a:expr(a:lnum) 100 | if (l:retval != "") 101 | return l:retval 102 | endif 103 | endif 104 | 105 | return (a:0 > 0 ? a:1 : "") 106 | endfunction 107 | 108 | 109 | function! s:RefreshLine(lnum) "{{{1 110 | " Description: Decides what the sign string should be based on if there are any marks or markers (using b:sig_marks 111 | " and b:sig_markers) on the current line and the value of b:SignaturePrioritizeMarks. 112 | " Arguments: 113 | " lnum : Line number for which the sign string is to be modified 114 | 115 | let l:id = abs(a:lnum * 1000 + bufnr('%')) 116 | let l:str = "" 117 | 118 | " Place the sign 119 | if ( has_key(b:sig_marks, a:lnum) 120 | \ && ( b:SignaturePrioritizeMarks 121 | \ || !has_key(b:sig_markers, a:lnum) 122 | \ ) 123 | \ ) 124 | let l:SignatureMarkTextHL = s:EvaluateHL(g:SignatureMarkTextHL, a:lnum, "SignatureMarkText") 125 | let l:SignatureMarkLineHL = s:EvaluateHL(g:SignatureMarkLineHL, a:lnum, "SignatureMarkLine") 126 | let l:str = substitute(b:SignatureMarkOrder, "\m", signature#utils#GetChar(b:sig_marks[a:lnum], 0), '') 127 | let l:str = substitute(l:str, "\p", signature#utils#GetChar(b:sig_marks[a:lnum], 1), '') 128 | 129 | execute 'sign define Signature_' . l:str . ' text=' . l:str . ' texthl=' . l:SignatureMarkTextHL . ' linehl=' . l:SignatureMarkLineHL 130 | 131 | elseif has_key(b:sig_markers, a:lnum) 132 | let l:SignatureMarkerTextHL = s:EvaluateHL(g:SignatureMarkerTextHL, a:lnum, "SignatureMarkerText") 133 | let l:SignatureMarkerLineHL = s:EvaluateHL(g:SignatureMarkerLineHL, a:lnum, "SignatureMarkerLine") 134 | 135 | " Since the same marker can be placed on multiple lines, we can't use the same sign for all of them. 136 | " This is because if dynamic highlighting of markers is enabled then the sign placed on eg. a modified line should 137 | " be highlighted differently than the one placed on an unchanged line. 138 | " In order to support this, I append the name of the TextHL and LineHL group to the name of the sign. 139 | let l:txt = signature#utils#GetChar(b:sig_markers[a:lnum], 0) 140 | let l:str = l:txt . '_' . l:SignatureMarkerTextHL . '_' . l:SignatureMarkerLineHL 141 | 142 | execute 'sign define Signature_' . l:str . ' text=' . l:txt . ' texthl=' . l:SignatureMarkerTextHL . ' linehl=' . l:SignatureMarkerLineHL 143 | else 144 | call signature#sign#Unplace(a:lnum) 145 | endif 146 | 147 | if (l:str != "") 148 | execute 'sign place ' . l:id . ' line=' . a:lnum . ' name=Signature_' . l:str . ' buffer=' . bufnr('%') 149 | endif 150 | 151 | " If there is only 1 mark/marker in the file, place a dummy to prevent flickering of the gutter when it is moved 152 | " If there are no signs left, remove the dummy 153 | call signature#sign#ToggleDummy() 154 | endfunction 155 | 156 | 157 | function! signature#sign#Refresh(...) "{{{1 158 | " Description: Add signs for new marks/markers and remove signs for deleted marks/markers 159 | " Arguments: Specify an argument to force a sign refresh 160 | 161 | call s:InitializeVars(a:0 && a:1) 162 | " If Signature is not enabled, return 163 | if !b:sig_enabled | return | endif 164 | 165 | for i in signature#mark#GetList('free', 'buf_curr') 166 | " ... remove it 167 | call signature#sign#Remove(i, 0) 168 | endfor 169 | 170 | " Add signs for marks ... 171 | for [l:mark, l:lnum, _] in signature#mark#GetList('used', 'buf_curr') 172 | " ... if mark is not present in our b:sig_marks list or if it is present but at the wrong line, 173 | " remove the old sign and add a new one 174 | if ( !has_key(b:sig_marks, l:lnum) 175 | \ || (b:sig_marks[l:lnum] !~# l:mark) 176 | \ || a:0 177 | \ ) 178 | call signature#sign#Remove(l:mark, 0) 179 | call signature#sign#Place (l:mark, l:lnum) 180 | endif 181 | endfor 182 | 183 | call signature#sign#ToggleDummy() 184 | 185 | " We do not add signs for markers as SignRefresh is executed periodically and we don't have a way to determine if the 186 | " marker already has a sign or not 187 | endfunction 188 | 189 | 190 | function! signature#sign#Unplace(lnum) "{{{1 191 | " Description: Remove the sign from the specified line number 192 | " FIXME: Clean-up. Undefine the sign 193 | let l:id = abs(a:lnum * 1000 + bufnr('%')) 194 | silent! execute 'sign unplace ' . l:id 195 | endfunction 196 | 197 | 198 | function! signature#sign#ToggleDummy(...) "{{{1 199 | " Description: Places a dummy sign to prevent flickering of the gutter when the mark is moved or the line containing 200 | " a mark/marker is deleted and then the delete is undone 201 | " Arguments: (optional) 0 : force remove 202 | " 1 : force place 203 | 204 | let l:place = a:0 ? a:1 : (len(b:sig_marks) + len(b:sig_markers) == 1) && !b:sig_DummyExists 205 | let l:remove = a:0 ? !a:1 : (len(b:sig_marks) + len(b:sig_markers) == 0) && b:sig_DummyExists 206 | 207 | if (l:place) 208 | sign define Signature_Dummy 209 | execute 'sign place 666 line=1 name=Signature_Dummy buffer=' . bufnr('%') 210 | let b:sig_DummyExists = 1 211 | elseif (l:remove) 212 | silent! execute 'sign unplace 666 buffer=' . bufnr('%') 213 | let b:sig_DummyExists = 0 214 | endif 215 | endfunction 216 | 217 | 218 | function! s:GetInfo(...) "{{{1 219 | " Description: Returns a dic of filenames, each of which is a dic of line numbers on which signs are placed 220 | " Arguments: filename (optional). 221 | " If filename is provided, the return value will contain signs only present in the given file 222 | " Eg. { 223 | " 'vimrc': { 224 | " '711': { 225 | " 'id': '1422', 226 | " 'name': 'sig_Sign_1422' 227 | " }, 228 | " '676': { 229 | " 'id': '1352', 230 | " 'name': 'sig_Sign_1352' 231 | " } 232 | " } 233 | " } 234 | 235 | " Redirect the input to a variable 236 | redir => l:sign_str 237 | silent! sign place 238 | redir END 239 | 240 | " Create a Hash of files to store the info. 241 | let l:signs_dic = {} 242 | " The file that is currently being processed is stored into l:file 243 | let l:match_file = "" 244 | let l:file_found = 0 245 | 246 | " Split the string into an array of sentences and filter out empty lines 247 | for i in filter( split( l:sign_str, '\n' ), 'v:val =~ "^[S ]"' ) 248 | let l:temp_file = matchstr( i, '\v(Signs for )@<=\S+:@=' ) 249 | 250 | if l:temp_file != "" 251 | let l:match_file = l:temp_file 252 | let l:signs_dic[l:match_file] = {} 253 | elseif l:match_file != "" 254 | " Get sign info 255 | let l:info_match = matchlist( i, '\vline\=(\d+)\s*id\=(\S+)\s*name\=(\S+)' ) 256 | if !empty( l:info_match ) 257 | let l:signs_dic[l:match_file][l:info_match[1]] = { 258 | \ 'id' : l:info_match[2], 259 | \ 'name' : l:info_match[3], 260 | \ } 261 | endif 262 | endif 263 | endfor 264 | 265 | if a:0 266 | "" Search for the full path first in the hash ... 267 | "let l:curr_filepath = expand('%:p') 268 | "if has_key( l:signs_dic, l:curr_filepath ) 269 | " return filter( l:signs_dic, 'v:key ==# l:curr_filepath' )[l:curr_filepath] 270 | "else 271 | " ... if no entry is found for the full path, search for the filename in the hash ... 272 | " Since we're searching for the current file, if present in the hash, it'll be as a filename and not the full path 273 | let l:curr_filename = expand('%:t') 274 | if has_key( l:signs_dic, l:curr_filename ) 275 | return filter( l:signs_dic, 'v:key ==# l:curr_filename' )[l:curr_filename] 276 | endif 277 | 278 | " ... if nothing is found, then return an empty hash to indicate that no signs are present in the current file 279 | return {} 280 | endif 281 | 282 | return l:signs_dic 283 | endfunction 284 | 285 | 286 | function! signature#sign#GetGitGutterHLGroup(lnum) "{{{1 287 | " Description: This returns the highlight group used by vim-gitgutter depending on how the line was edited 288 | 289 | let l:current_bufnr = bufnr('%') 290 | let l:line_state = filter(copy(gitgutter#diff#process_hunks(l:current_bufnr, gitgutter#hunk#hunks(l:current_bufnr))), 'v:val[0] == a:lnum') 291 | 292 | if len(l:line_state) == 0 293 | return "" 294 | endif 295 | 296 | if (l:line_state[0][1]) =~ 'added' | return 'GitGutterAdd' 297 | elseif (l:line_state[0][1]) =~ 'modified_removed' | return 'GitGutterChangeDelete' 298 | elseif (l:line_state[0][1]) =~ 'modified' | return 'GitGutterChange' 299 | elseif (l:line_state[0][1]) =~ 'removed' | return 'GitGutterDelete' 300 | endif 301 | endfunction 302 | 303 | 304 | function! signature#sign#GetSignifyHLGroup(lnum) "{{{1 305 | " Description: This returns the highlight group used by vim-signify depending on how the line was edited 306 | " Thanks to @michaelmior 307 | 308 | if !exists('b:sy') 309 | return "" 310 | endif 311 | call sy#sign#get_current_signs(b:sy) 312 | 313 | if has_key(b:sy.internal, a:lnum) 314 | let l:line_state = b:sy.internal[a:lnum]['type'] 315 | if l:line_state =~ 'SignifyAdd' | return 'SignifySignAdd' 316 | elseif l:line_state =~ 'SignifyChange' | return 'SignifySignChange' 317 | elseif l:line_state =~ 'SignifyDelete' | return 'SignifySignDelete' 318 | end 319 | endif 320 | 321 | return "" 322 | endfunction 323 | 324 | 325 | " function! signature#sign#GetMarkSignLine(mark) "{{{1 326 | " if !signature#utils#IsValidMark(a:mark) 327 | " echoe "Signature: Invalid mark " . a:mark 328 | " return 329 | " endif 330 | 331 | " let l:sign_info=filter(split(execute('sign place'), '\n'), 332 | " \ 'v:val =~ "\\vSignature_(.?' . a:mark . '|' . a:mark . '.?)$"') 333 | 334 | " if (len(l:sign_info) != 1) 335 | " echoe "Signature: Expected single match, found " . len(l:sign_info) 336 | " return 337 | " endif 338 | 339 | " return matchstr(l:sign_info[0], '\v(line\=)@<=\d+') 340 | " endfunction 341 | 342 | 343 | function! s:InitializeVars(...) "{{{1 344 | " Description: Initialize variables 345 | " Arguments: Specify an argument to re-init 346 | 347 | if !exists('b:sig_marks') 348 | " b:sig_marks = { lnum => signs_str } 349 | let b:sig_marks = {} 350 | else 351 | " Lines can be removed using an external tool. Hence, we need to filter out marks placed on line numbers that are 352 | " now greater than the total number of lines in the file. 353 | let l:line_tot = line('$') 354 | call filter( b:sig_marks, 'v:key <= l:line_tot' ) 355 | endif 356 | 357 | if !exists('b:sig_markers') 358 | " b:sig_markers = { lnum => marker } 359 | let b:sig_markers = {} 360 | else 361 | " Lines can be removed using an external tool. Hence, we need to filter out marks placed on line numbers that are 362 | " now greater than the total number of lines in the file. 363 | let l:line_tot = line('$') 364 | call filter( b:sig_markers, 'v:key <= l:line_tot' ) 365 | endif 366 | 367 | call signature#utils#Set('b:sig_DummyExists' , 0 , a:0 && a:1) 368 | call signature#utils#Set('b:sig_enabled' , g:SignatureEnabledAtStartup, a:0 && a:1) 369 | call signature#utils#Set('b:SignatureIncludeMarks' , g:SignatureIncludeMarks , a:0 && a:1) 370 | call signature#utils#Set('b:SignatureIncludeMarkers' , g:SignatureIncludeMarkers , a:0 && a:1) 371 | call signature#utils#Set('b:SignatureMarkOrder' , g:SignatureMarkOrder , a:0 && a:1) 372 | call signature#utils#Set('b:SignaturePrioritizeMarks', g:SignaturePrioritizeMarks , a:0 && a:1) 373 | call signature#utils#Set('b:SignatureDeferPlacement' , g:SignatureDeferPlacement , a:0 && a:1) 374 | call signature#utils#Set('b:SignatureWrapJumps' , g:SignatureWrapJumps , a:0 && a:1) 375 | endfunction 376 | -------------------------------------------------------------------------------- /doc/signature.txt: -------------------------------------------------------------------------------- 1 | *signature.txt* A plugin to toggle, display and navigate marks 2 | 3 | _________.__ __ ~ 4 | / _____/|__| ____ ____ _____ _/ |_ __ __ _______ ____ ~ 5 | \_____ \ | | / ___\ / \ \__ \ \ __\| | \\_ __ \_/ __ \ ~ 6 | / \| | / /_/ >| | \ / __ \_ | | | | / | | \/\ ___/ ~ 7 | /_______ /|__| \___ / |___| /(____ / |__| |____/ |__| \___ > ~ 8 | \/ /_____/ \/ \/ \/ ~ 9 | 10 | 11 | ============================================================================== 12 | Contents *Signature* 13 | 14 | 1. Mappings |SignatureMappings| 15 | 2. Commands |SignatureCommands| 16 | 3. Customization |SignatureCustomization| 17 | 4. Contributing |SignatureContributing| 18 | 5. Credits |SignatureCredits| 19 | 6. License |SignatureLicense| 20 | 21 | 22 | ============================================================================== 23 | 1. Mappings *SignatureMappings* 24 | 25 | Out of the box, the followings mappings are defined by default 26 | 27 | mx Toggle mark 'x' and display it in the leftmost column 28 | dmx Remove mark 'x' where x is a-zA-Z 29 | 30 | m, Place the next available mark 31 | m. If no mark on line, place the next available mark. Otherwise, 32 | remove (first) existing mark. 33 | m- Delete all marks from the current line 34 | m Delete all marks from the current buffer 35 | ]` Jump to next mark 36 | [` Jump to prev mark 37 | ]' Jump to start of next line containing a mark 38 | [' Jump to start of prev line containing a mark 39 | `] Jump by alphabetical order to next mark 40 | `[ Jump by alphabetical order to prev mark 41 | '] Jump by alphabetical order to start of next line having a mark 42 | '[ Jump by alphabetical order to start of prev line having a mark 43 | m/ Open location list and display marks from current buffer 44 | 45 | m[0-9] Toggle the corresponding marker !@#$%^&*() 46 | m Remove all markers of the same type 47 | ]- Jump to next line having a marker of the same type 48 | [- Jump to prev line having a marker of the same type 49 | ]= Jump to next line having a marker of any type 50 | [= Jump to prev line having a marker of any type 51 | m? Open location list and display markers from current buffer 52 | m Remove all markers 53 | 54 | This will allow the use of default behavior of m to set marks and, if 55 | the line already contains the mark, it'll be unset. The default behavior 56 | of ]', [', ]` and [` is supported and enhanced by wrapping around when 57 | beginning or end of file is reached. ]-, [-, ]= and [= also accept a count. 58 | 59 | To directly jump to a given marker, the following maps can be used 60 | > 61 | nnoremap [1 :call signature#marker#Goto('prev', 1, v:count) 62 | nnoremap ]1 :call signature#marker#Goto('next', 1, v:count) 63 | nnoremap [2 :call signature#marker#Goto('prev', 2, v:count) 64 | nnoremap ]2 :call signature#marker#Goto('next', 2, v:count) 65 | < 66 | etc. These are not defined by default 67 | 68 | 69 | ============================================================================== 70 | 2. Commands *SignatureCommands* 71 | 72 | *:SignatureToggleSigns* 73 | Toggle the display of signs. This won't affect the marks or the mappings. 74 | 75 | *:SignatureRefresh* 76 | Force the display of signs in the buffer to refresh. Use this to correct 77 | the signs if things go awry 78 | 79 | *:SignatureListBufferMarks* [n] 80 | List all the marks used in the current buffer in the location list 81 | Accepts an optional argument to provide 'n' lines of context 82 | 83 | *:SignatureListGlobalMarks* [n] 84 | List only the global marks used in all buffers in the location list 85 | Accepts an optional argument to provide 'n' lines of context 86 | 87 | *:SignatureListMarkers* [marker] [n] 88 | List all instances of the specified marker(s) used in the current buffer 89 | in the location list. If no argument is given, it lists all markers 90 | Accepts an optional argument to provide 'n' lines of context 91 | > 92 | :SignatureListMarkers : List all markers 93 | :SignatureListMarkers 1 : List only the '!' marker 94 | :SignatureListMarkers @ : List only the '@' marker 95 | :SignatureListMarkers 0, 2 : List only ) marker with 2 lines of context 96 | :SignatureListMarkers '', 2 : List all markers with 2 lines of context 97 | :SignatureListMarkers '!@', 2 : List only the '!' and '@' markers and show 98 | 2 lines of context around them 99 | < 100 | 101 | ============================================================================== 102 | 3. Customization *SignatureCustomization* 103 | 104 | *g:SignatureMap* 105 | Type: Dictionary, Default: 106 | To set up your own mappings copy the following dictionary and edit it 107 | > 108 | let g:SignatureMap = { 109 | \ 'Leader' : "m", 110 | \ 'PlaceNextMark' : "m,", 111 | \ 'ToggleMarkAtLine' : "m.", 112 | \ 'PurgeMarksAtLine' : "m-", 113 | \ 'DeleteMark' : "dm", 114 | \ 'PurgeMarks' : "m", 115 | \ 'PurgeMarkers' : "m", 116 | \ 'GotoNextLineAlpha' : "']", 117 | \ 'GotoPrevLineAlpha' : "'[", 118 | \ 'GotoNextSpotAlpha' : "`]", 119 | \ 'GotoPrevSpotAlpha' : "`[", 120 | \ 'GotoNextLineByPos' : "]'", 121 | \ 'GotoPrevLineByPos' : "['", 122 | \ 'GotoNextSpotByPos' : "]`", 123 | \ 'GotoPrevSpotByPos' : "[`", 124 | \ 'GotoNextMarker' : "]-", 125 | \ 'GotoPrevMarker' : "[-", 126 | \ 'GotoNextMarkerAny' : "]=", 127 | \ 'GotoPrevMarkerAny' : "[=", 128 | \ 'ListBufferMarks' : "m/", 129 | \ 'ListBufferMarkers' : "m?" 130 | \ } 131 | < 132 | By default, it defines the mappings as shown in |signature-mappings| 133 | To disable a map entirely, specify it as an empty string. 134 | If a key is not specified, the default value will be picked up. 135 | These same characters will be used to invoke the shortcuts. 136 | 137 | 138 | *g:SignatureIncludeMarks* 139 | String, Default : 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 140 | 141 | Specify the marks that can be controlled by this plugin. 142 | Only supports Alphabetical marks at the moment. 143 | 'b:SignatureIncludeMarks' can be set separately for buffer-specific marks. 144 | 145 | 146 | *g:SignatureIncludeMarkers* 147 | String, Default : ')!@#$%^&*(' 148 | 149 | Specify the symbols that can be used by the plugin to be placed by 0-9. 150 | Note that there's a 1:1 correspondence between the symbols and 0-9. 151 | This string must be at most 10 chars long. Extra char will be ignored. 152 | Also, this string must not include any alphabetical characters `[a-zA-Z]` 153 | However, it can contain spaces to indicate blanks. Eg. 154 | `m0` will place `)` by default 155 | `m1` will place `!` by default 156 | `m2` will place `@` by default etc. 157 | 158 | If set to `')!'`, `m0` and `m1` will behave as described above but `m[2-9]` will 159 | not be handled by this plugin. 160 | 161 | If set to `' !'` (note the space before !), only `m1` will have any affect 162 | while `m[0,2-9]` will not have any affect 163 | 164 | An example of a use case is for keyboards with alternate layouts wherein 165 | the symbols associated with 0-9 could be different than the default. 166 | 'b:SignatureIncludeMarkers' can be specified separately for each buffer 167 | 168 | 169 | *g:SignatureMap['Leader']* 170 | String, Default: 'm' 171 | 172 | Set the key used to toggle marks and markers. 173 | For eg. If this key is set to `m`, 174 | `ma` will toggle the mark 'a' on the current line 175 | `m,` will place the next available mark 176 | `m.` will place the next available mark if there are no 177 | marks already on the line; otherwise, will remove 178 | first mark from line 179 | `m` will delete all marks 180 | `m1` will toggle the marker '!' 181 | `m!` will remove all the '!' markers 182 | `m` will remove all markers 183 | 184 | NOTE: Currently, either marks or markers can be displayed in front of a 185 | line. Both can't be displayed simultaenously. 186 | 187 | To set this to mapleader or maplocalleader 188 | 189 | `let g:SignatureMap['Leader'] = ''` 190 | `let g:SignatureMap['Leader'] = ''` 191 | 192 | 193 | *g:SignatureWrapJumps* 194 | Boolean, Default : 1 195 | 196 | Specify if jumping to marks should wrap-around. 197 | b:SignatureWrapJumps can be set to specify buffer-specific settings. 198 | 199 | 200 | *g:SignatureMarkOrder* 201 | String, Default : "\p\m" 202 | 203 | Signature allows you to display upto marks in front of a line. This controls 204 | the order in which marks are displayed. 205 | '\m' indicates the current or latest mark placed on the line 206 | '\p' indicates the previous mark placed on the line 207 | 208 | For eg, 209 | `g:SignatureMarkOrder="\m."` : Display last mark with '.' suffixed 210 | `g:SignatureMarkOrder="_\m"` : Display last mark with '_' prefixed 211 | `g:SignatureMarkOrder=">"` : Display only a ">" for a line with a mark. 212 | The mark is not displayed 213 | `g:SignatureMarkOrder="\m\p"` : Display last two marks placed 214 | 215 | NOTE: The signs feature allows only 2 characters to be displayed. This limit 216 | is imposed by vim itself and not by the plugin 217 | 218 | 219 | *g:SignatureMarkTextHL* 220 | String, Default : 'SignatureMarkText' 221 | 222 | The highlight group used for mark signs. This can be set either to a string 223 | or to a |Funcref|. 224 | 225 | If it holds a string, it must be an expression suitable for passing to 226 | |eval()|. In the simple case, it can be the name of a highlight group. It can 227 | also hold more complicated expressions, in which case the expression `a:lnum` 228 | may be helpful. It holds the number of the line where the current mark is to 229 | be highlighted. 230 | 231 | If it holds a |Funcref|, then the function will be called with one argument, 232 | the line number of the mark to be highlighted. The function should return 233 | the name of a highlight group. 234 | 235 | Example using a string: 236 | `let g:SignatureMarkTextHL = "Exception"` 237 | 238 | Example of |Funcref|: 239 | `function Example(lineno)` 240 | `return "Exception"` 241 | `endfunction` 242 | `let g:SignatureMarkTextHL = function("Example")` 243 | 244 | By default, this is set to SignatureMarkText which is a highlight group 245 | that is linked to Exception 246 | 247 | *g:SignatureMarkTextHLDynamic* 248 | Boolean, Default: 0 249 | 250 | Highlight signs of marks dynamically based upon state indicated by 251 | vim-gitgutter. Setting this to `1` prior to plugin initialization overwrites 252 | |g:SignatureMarkTextHL|. 253 | 254 | vim-signify is not supported at the moment. For details, refer 255 | https://github.com/kshenoy/vim-signature/issues/93#issuecomment-195672354 256 | 257 | *g:SignatureMarkLineHL* 258 | String, Default : 'SignatureMarkLine' 259 | 260 | The highlight group used for hightlighting lines having marks. This can be a 261 | string or |Funcref|. SignatureMarkLine links to Normal by default. 262 | See |g:SignatureMarkTextHL| for details. 263 | 264 | 265 | *g:SignatureMarkerTextHL* 266 | String, Default : 'SignatureMarkerText' 267 | 268 | The highlight group used for marker signs. This can be a string or |Funcref|. 269 | SignatureMarkText links to WarningMsg by default. 270 | See |g:SignatureMarkTextHL| for details. 271 | 272 | 273 | *g:SignatureMarkerTextHLDynamic* 274 | Boolean, Default: 0 275 | 276 | Highlight signs of markers dynamically based upon state indicated by 277 | vim-gitgutter or vim-signify. Setting this to `1` prior to plugin 278 | initialization overwrites |g:SignatureMarkerTextHL|. 279 | 280 | 281 | *g:SignatureMarkerLineHL* 282 | String, Default : '' 283 | 284 | The highlight group used for hightlighting lines having markers. This can be 285 | a string or a |Funcref|. SignatureMarkerLine links to Normal by default. 286 | See |g:SignatureMarkTextHL| for details. 287 | 288 | 289 | *g:SignatureDeleteConfirmation* 290 | Boolean, Default: 0 291 | 292 | An option for the more clumsy-fingered of us. Asks for confirmation before 293 | moving/replacing/overwriting any marks 294 | 295 | 296 | *g:SignaturePurgeConfirmation* 297 | Boolean, Default: 0 298 | 299 | Similar to g:SignatureDeleteConfirmation. Asks for confirmation before 300 | deleting all marks/markers 301 | 302 | 303 | *g:SignaturePeriodicRefresh* 304 | Boolean, Default: 1 305 | 306 | Enable the display to refresh periodically. Generally a good thing to have. 307 | This makes use of the CursorHold autocmd event to execute periodically. 308 | The frequency of this event can be controlled by changing the value of the 309 | updatetime variable in milliseconds 310 | `set updatetime = 100` 311 | 312 | 313 | *g:SignaturePrioritizeMarks* 314 | Boolean, Default: 1 315 | 316 | When a line has both marks and markers, display the sign for marks. If set 317 | to 0, it will display the sign for markers instead 318 | 319 | 320 | *g:SignatureEnabledAtStartup* 321 | Boolean, Default: 1 322 | 323 | Control if the signs should be shown by default. If set to 0, the signs 324 | won't be visible until `:SignatureToggleSigns` has been called 325 | 326 | 327 | *g:SignatureDeferPlacement* 328 | Boolean, Default: 1 329 | 330 | NOTE: Not supported currently. Code was highly inefficient. 331 | Check if any other plugin has already placed a sign and if set to 1, 332 | Signature will hold off from placing a sign. If set to 0, Signature will 333 | overwrite any signs that are already present. 334 | 335 | 336 | *g:SignatureUnconditionallyRecycleMarks* 337 | Boolean, Default: 0 338 | 339 | Controls behavior when trying to place next available mark and all marks 340 | have been used. If set to 0, then either an error or warning message will be 341 | emitted, depending on the setting of |g:SignatureErrorIfNoAvailableMarks|. 342 | If set to 1, then the first local mark (e.g., 'a') will be removed from its 343 | existing location and applied to the current line. 344 | 345 | 346 | *g:SignatureErrorIfNoAvailableMarks* 347 | Boolean, Default: 1 348 | 349 | Controls behavior when unable to place a new mark. If set to 1, then an 350 | error is raised. If set to 0, then just a warning message. 351 | 352 | 353 | *g:SignatureForceRemoveGlobal* 354 | Boolean, Default: 0 355 | 356 | Vim's handling of global marks is a bit iffy. This option forces the removal 357 | of global marks by deleting it from the viminfo (or neovim's shada) file. 358 | 359 | Note that this is a very heavy hammer since it affects other things too. 360 | Refer 'viminfo' or 'shada' for more information on features that could be 361 | affected. 362 | 363 | 364 | *g:SignatureForceMarkPlacement* 365 | Boolean, Default: 0 366 | 367 | When set to 1, will always place marks instead of toggling them 368 | 369 | 370 | *g:SignatureForceMarkerPlacement* 371 | Boolean, Default: 0 372 | 373 | When set to 1, will always place markers instead of toggling them 374 | 375 | 376 | ============================================================================== 377 | 4. Contributing *SignatureContributing* 378 | 379 | Please post any issues and all your suggestions on Github 380 | https://github.com/kshenoy/vim-signature 381 | 382 | Show some love by spreading the word and rating on 383 | http://www.vim.org/scripts/script.php?script_id=4118 384 | 385 | 386 | ============================================================================== 387 | 5. Credits *SignatureCredits* 388 | 389 | A great thanks to these guys for providing the idea and inspiration to develop 390 | Signature 391 | 392 | * Sergey Khorev for mark-tools 393 | http://www.vim.org/scripts/script.php?script_id=2929 394 | 395 | * Zak Johnson for vim-showmarks 396 | https://github.com/zakj/vim-showmarks 397 | 398 | I'd also like to thank Steve J. Losh for learningvimscriptthehardway.com 399 | without whose detailed guide this plugin would not have seen the light of day. 400 | 401 | 402 | ============================================================================== 403 | 6. License *SignatureLicense* 404 | 405 | Signature is MIT/X11 licensed 406 | 407 | 408 | vim:tw=78:ts=2:et:sts=2:sw=2:ft=help 409 | -------------------------------------------------------------------------------- /autoload/signature/mark.vim: -------------------------------------------------------------------------------- 1 | " vim: fdm=marker:et:ts=4:sw=2:sts=1 2 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 3 | 4 | function! signature#mark#Toggle(mark) " {{{1 5 | " Description: mark = 'next' : Place new mark on current line else toggle specified mark on current line 6 | " Arguments: mark [a-z,A-Z] 7 | 8 | if a:mark == "next" 9 | " Place new mark 10 | let l:marks_list = signature#mark#GetList('free', 'buf_all') 11 | if empty(l:marks_list) 12 | if (!g:SignatureRecycleMarks) 13 | " No marks available and mark re-use not in effect 14 | call s:ReportNoAvailableMarks() 15 | return 16 | endif 17 | " Remove a local mark 18 | let l:marks_list = signature#mark#GetList('used', 'buf_curr')[0] 19 | call signature#mark#Remove(l:marks_list[0]) 20 | endif 21 | call s:Place(l:marks_list[0]) 22 | 23 | else 24 | " Toggle Mark 25 | let l:used_marks = filter(signature#mark#GetList('used', 'buf_all'), 'v:val[0] ==# a:mark') 26 | if (len(l:used_marks) > 0) 27 | let l:mark_pos = l:used_marks[0][1] 28 | let l:mark_buf = l:used_marks[0][2] 29 | 30 | if (l:mark_buf == bufnr('%')) 31 | " If the mark is not in use in current buffer then it's a global ==> Don't worry about deleting it 32 | if ( (l:mark_pos == line('.')) 33 | \ && !g:SignatureForceMarkPlacement 34 | \ ) 35 | " Mark is present on the current line. Remove it and return 36 | call signature#mark#Remove(a:mark) 37 | call signature#sign#ToggleDummy() 38 | return 39 | else 40 | " Mark is present elsewhere in the current buffer ==> Remove it and fall-through to place new mark. 41 | " If g:SignatureForceMarkPlacement is set, we remove and re-place it so that the sign string can be true 42 | " to the order in which the marks were placed. 43 | " For eg. if we place 'a, 'b and then 'a again, the sign string changes from "ab" to "ba" 44 | " Ask for confirmation before moving mark 45 | if (g:SignatureDeleteConfirmation) 46 | let choice = confirm("Mark '" . a:mark . "' has been used elsewhere. Reuse it?", "&Yes\n&No", 1) 47 | if choice == 2 | return | endif 48 | endif 49 | call signature#mark#Remove(a:mark) 50 | endif 51 | endif 52 | endif 53 | 54 | " Place new mark 55 | call s:Place(a:mark) 56 | endif 57 | endfunction 58 | 59 | 60 | function! signature#mark#Remove(mark) " {{{1 61 | " Description: Remove 'mark' and its associated sign. If called without an argument, obtain it from the user 62 | " Arguments: mark = [a-z,A-Z] 63 | 64 | if !signature#utils#IsValidMark(a:mark) 65 | return 66 | endif 67 | 68 | let l:lnum = line("'" . a:mark) 69 | call signature#sign#Remove(a:mark, l:lnum) 70 | execute 'delmarks ' . a:mark 71 | call s:ForceGlobalRemoval(a:mark) 72 | endfunction 73 | 74 | 75 | function! s:Place(mark) " {{{1 76 | " Description: Place new mark at current cursor position 77 | " Arguments: mark = [a-z,A-Z] 78 | " If a line is deleted or mark is manipulated using any non-signature method then b:sig_marks can go out of sync 79 | " Thus, we forcibly remove signs for the mark present on any line before proceeding 80 | call signature#sign#Remove(a:mark, 0) 81 | execute 'normal! m' . a:mark 82 | call signature#sign#Place(a:mark, line('.')) 83 | endfunction 84 | 85 | 86 | function! signature#mark#ToggleAtLine() " {{{1 87 | " Description: If no mark on current line, add one. If marks are on the current line, remove one. 88 | let l:marks_here = filter(signature#mark#GetList('used', 'buf_curr'), 'v:val[1] == ' . line('.')) 89 | if empty(l:marks_here) 90 | " Set up for adding a mark 91 | call signature#mark#Toggle('next') 92 | else 93 | " Delete first mark 94 | call signature#mark#Remove(l:marks_here[0][0]) 95 | endif 96 | endfunction 97 | 98 | 99 | function! signature#mark#Purge(mode) " {{{1 100 | " Description: Delete all marks from current line 101 | " Arguments: mode = 'line' : Delete all marks from current line 102 | " 'all' : Delete all marks used in the buffer 103 | 104 | let l:used_marks = signature#mark#GetList('used', 'buf_curr') 105 | if (a:mode ==? 'line') 106 | call filter(l:used_marks, 'v:val[1] == ' . line('.')) 107 | endif 108 | 109 | if ( !empty(l:used_marks) 110 | \ && g:SignaturePurgeConfirmation 111 | \ ) 112 | let l:msg = 'Are you sure you want to delete all marks' . (a:mode ==? 'line' ? ' from the current line' : '') . '?' 113 | let l:ans = confirm(l:msg . ' This cannot be undone.', "&Yes\n&No", 1) 114 | if (l:ans != 1) | return | endif 115 | endif 116 | 117 | for i in l:used_marks 118 | call signature#mark#Remove(i[0]) 119 | endfor 120 | 121 | " If marks are modified using any non-signature method, b:sig_marks can go out of sync 122 | if (a:mode ==? 'all') 123 | for l:lnum in keys(b:sig_marks) 124 | call signature#sign#Unplace(l:lnum) 125 | endfor 126 | endif 127 | call signature#sign#ToggleDummy() 128 | endfunction 129 | 130 | 131 | function! signature#mark#Goto(dir, loc, mode) " {{{1 132 | " Arguments: 133 | " dir = next : Jump forward 134 | " prev : Jump backward 135 | " loc = line : Jump to first column of line with mark 136 | " spot : Jump to exact column of the mark 137 | " mode = pos : Jump to next mark by position 138 | " alpha : Jump to next mark by alphabetical order 139 | " global : Jump only to global marks (applies to all buffers and alphabetical order) 140 | 141 | let l:mark = "" 142 | let l:dir = a:dir 143 | 144 | if a:mode ==? "global" 145 | let l:mark = s:GotoByAlphaGlobal(a:dir) 146 | elseif a:mode ==? "alpha" 147 | let l:mark = s:GotoByAlpha(a:dir) 148 | elseif a:mode ==? "pos" 149 | let l:mark = s:GotoByPos(a:dir) 150 | endif 151 | 152 | " NOTE: If l:mark is an empty string then no movement will be made 153 | if l:mark == "" | return | endif 154 | 155 | if a:loc ==? "line" 156 | execute "normal! '" . l:mark 157 | elseif a:loc ==? "spot" 158 | execute 'normal! `' . l:mark 159 | endif 160 | endfunction 161 | 162 | 163 | function! s:GotoByPos(dir) " {{{1 164 | " Description: Jump to next/prev mark by location. 165 | " Arguments: dir = next : Jump forward 166 | " prev : Jump backward 167 | 168 | " We need at least one mark to be present. If not, then return an empty string so that no movement will be made 169 | if empty(b:sig_marks) | return "" | endif 170 | 171 | let l:lnum = line('.') 172 | 173 | " Get list of line numbers of lines with marks. 174 | if a:dir ==? "next" 175 | let l:targ = min(sort(keys(b:sig_marks), "signature#utils#NumericSort")) 176 | let l:mark_lnums = sort(keys(filter(copy(b:sig_marks), 'v:key > l:lnum')), "signature#utils#NumericSort") 177 | elseif a:dir ==? "prev" 178 | let l:targ = max(sort(keys(b:sig_marks), "signature#utils#NumericSort")) 179 | let l:mark_lnums = reverse(sort(keys(filter(copy(b:sig_marks), 'v:key < l:lnum')), "signature#utils#NumericSort")) 180 | endif 181 | 182 | let l:targ = (empty(l:mark_lnums) ? (b:SignatureWrapJumps ? l:targ : "") : l:mark_lnums[0]) 183 | if empty(l:targ) | return "" | endif 184 | 185 | let l:mark = signature#utils#GetChar(b:sig_marks[l:targ], 0) 186 | return l:mark 187 | endfunction 188 | 189 | 190 | function! s:GotoByAlpha(dir) " {{{1 191 | " Description: Jump to next/prev mark by alphabetical order. Direction specified as input argument 192 | 193 | let l:used_marks = signature#mark#GetList('used', 'buf_curr') 194 | let l:line_marks = filter(copy(l:used_marks), 'v:val[1] == ' . line('.')) 195 | 196 | " If there is only one mark in the current file, then return the same 197 | if (len(l:used_marks) == 1) 198 | return l:used_marks[0][0] 199 | endif 200 | 201 | " Since we can place multiple marks on a line, to jump by alphabetical order we need to know what the current mark is. 202 | " This information is kept in the b:sig_GotoByAlpha_CurrMark variable. For instance, if we have marks a, b and c 203 | " on the current line and b:sig_GotoByAlpha_CurrMark has the value 'a' then we jump to 'b' and set the value of 204 | " the variable to 'b'. Reinvoking this function will thus now jump to 'c' 205 | if empty(l:line_marks) 206 | if exists('b:sig_GotoByAlpha_CurrMark') 207 | unlet b:sig_GotoByAlpha_CurrMark 208 | endif 209 | " If there are no marks present on the current line then call GotoByPos to jump to the next line with a mark 210 | return s:GotoByPos(a:dir) 211 | endif 212 | 213 | if (( len(l:line_marks) == 1 ) || !exists('b:sig_GotoByAlpha_CurrMark') || (b:sig_GotoByAlpha_CurrMark ==? "")) 214 | let b:sig_GotoByAlpha_CurrMark = l:line_marks[0][0] 215 | endif 216 | 217 | for i in range( 0, len(l:used_marks) - 1 ) 218 | if l:used_marks[i][0] ==# b:sig_GotoByAlpha_CurrMark 219 | if a:dir ==? "next" 220 | if (( i != len(l:used_marks)-1 ) || b:SignatureWrapJumps) 221 | let b:sig_GotoByAlpha_CurrMark = l:used_marks[(i+1)%len(l:used_marks)][0] 222 | endif 223 | elseif a:dir ==? "prev" 224 | if ((i != 0) || b:SignatureWrapJumps) 225 | let b:sig_GotoByAlpha_CurrMark = l:used_marks[i-1][0] 226 | endif 227 | endif 228 | return b:sig_GotoByAlpha_CurrMark 229 | endif 230 | endfor 231 | endfunction 232 | 233 | 234 | function! s:GotoByAlphaGlobal(dir) " {{{1 235 | " Description: Jump to next/prev Global mark in any buffer by alphabetical order. 236 | " Direction is specified as input argument 237 | 238 | let l:used_marks = signature#mark#GetList('used', 'buf_all', 'global') 239 | let l:line_marks = filter(copy(l:used_marks), 'v:val[1] == ' . line('.')) 240 | 241 | " If there is only one mark in the current file, return it 242 | if (len(l:used_marks) == 1) 243 | return l:used_marks[0][0] 244 | endif 245 | " If current line does not have a global mark on it then return the first used global mark 246 | if empty(l:line_marks) 247 | if exists('b:sig_GotoByAlphaGlobal_CurrMark') 248 | unlet b:sig_GotoByAlphaGlobal_CurrMark 249 | endif 250 | return l:used_marks[0][0] 251 | endif 252 | 253 | " Since we can place multiple marks on a line, to jump by alphabetical order we need to know what the current mark is. 254 | " This information is kept in the b:sig_GotoByAlphaGlobal_CurrMark variable. For instance, if we have marks A, B & C 255 | " on the current line and b:sig_GotoByAlphaGlobal_CurrMark has the value 'A' then we jump to 'B' and set the value of 256 | " the variable to 'B'. Reinvoking this function will thus now jump to 'C' 257 | if ( (len(l:line_marks) == 1) 258 | \ || !exists('b:sig_GotoByAlpha_CurrMark') 259 | \ || (b:sig_GotoByAlphaGlobal_CurrMark ==? "") 260 | \ ) 261 | let b:sig_GotoByAlphaGlobal_CurrMark = l:line_marks[0][0] 262 | endif 263 | 264 | for i in range( 0, len(l:used_marks) - 1 ) 265 | if l:used_marks[i][0] ==# b:sig_GotoByAlphaGlobal_CurrMark 266 | if a:dir ==? "next" 267 | if (( i != len(l:used_marks)-1 ) || b:SignatureWrapJumps) 268 | let b:sig_GotoByAlphaGlobal_CurrMark = l:used_marks[(i+1)%len(l:used_marks)][0] 269 | endif 270 | elseif a:dir ==? "prev" 271 | if ((i != 0) || b:SignatureWrapJumps) 272 | let b:sig_GotoByAlphaGlobal_CurrMark = l:used_marks[i-1][0] 273 | endif 274 | endif 275 | return b:sig_GotoByAlphaGlobal_CurrMark 276 | endif 277 | endfor 278 | endfunction 279 | 280 | 281 | function! signature#mark#GetList(mode, scope, ...) " {{{1 282 | " Arguments: mode = 'used' : Returns list of [ [used marks, line no., buf no.] ] 283 | " 'free' : Returns list of [ free marks ] 284 | " scope = 'buf_curr' : Limits scope to current buffer i.e used/free marks in current buffer 285 | " 'buf_all' : Set scope to all buffers i.e used/free marks from all buffers 286 | " [type] = 'global' : Return only global marks 287 | 288 | let l:marks_list = [] 289 | let l:line_tot = line('$') 290 | let l:buf_curr = bufnr('%') 291 | let l:type = (a:0 ? a:1 : "") 292 | 293 | " Respect order specified in g:SignatureIncludeMarks 294 | for i in split(b:SignatureIncludeMarks, '\zs') 295 | if (i =~# "[A-Z]") 296 | let [ l:buf, l:line, l:col, l:off ] = getpos( "'" . i ) 297 | let l:marks_list = add(l:marks_list, [i, l:line, l:buf]) 298 | elseif (l:type !=? "global") 299 | let l:marks_list = add(l:marks_list, [i, line("'" .i), l:buf_curr]) 300 | endif 301 | endfor 302 | 303 | if (a:mode ==? 'used') 304 | if (a:scope ==? 'buf_curr') 305 | call filter( l:marks_list, '(v:val[2] == l:buf_curr) && (v:val[1] > 0)' ) 306 | else 307 | call filter( l:marks_list, 'v:val[1] > 0' ) 308 | endif 309 | else 310 | if (a:scope ==? 'buf_all') 311 | call filter( l:marks_list, 'v:val[1] == 0' ) 312 | else 313 | call filter( l:marks_list, '(v:val[1] == 0) || (v:val[2] != l:buf_curr)' ) 314 | endif 315 | call map( l:marks_list, 'v:val[0]' ) 316 | endif 317 | 318 | return l:marks_list 319 | endfunction 320 | 321 | 322 | function! s:ForceGlobalRemoval(mark) " {{{1 323 | " Description: Edit viminfo/shada file to forcibly delete Global mark since vim's handling is iffy 324 | " Arguments: mark - The mark to delete 325 | 326 | if ( (a:mark !~# '[A-Z]') 327 | \ || !g:SignatureForceRemoveGlobal 328 | \ ) 329 | return 330 | endif 331 | 332 | if has('nvim') 333 | wshada! 334 | else 335 | wviminfo! 336 | endif 337 | endfunction 338 | 339 | 340 | function! s:ReportNoAvailableMarks() " {{{1 341 | if g:SignatureErrorIfNoAvailableMarks 342 | echoe "Signature: No free marks left." 343 | else 344 | echohl WarningMsg 345 | echomsg "Signature: No free marks left." 346 | echohl None 347 | endif 348 | endfunction 349 | 350 | 351 | function! signature#mark#List(scope, ...) " {{{1 352 | " Description: Opens and populates location list with marks 353 | " Arguments: scope = 0 : List local and global marks from current buffer 354 | " 1 : List only global marks from all buffers 355 | " [context] = 0 : Adds context around the mark 356 | 357 | let l:list = [] 358 | let l:buf_curr = bufnr('%') 359 | let l:list_sep = {'bufnr': '', 'lnum' : ''} 360 | 361 | let l:SignatureIncludeMarks = (a:scope == 0 ? b:SignatureIncludeMarks : g:SignatureIncludeMarks) 362 | for i in split(l:SignatureIncludeMarks, '\zs') 363 | let [l:bufnr, l:lnum, l:col, l:off] = getpos( "'" . i ) 364 | 365 | " Local marks set the buffer no. to 0, replace it with the actual buffer number 366 | let l:bufnr = (l:bufnr == 0 ? l:buf_curr : l:bufnr) 367 | 368 | " Check that 369 | " 1. Mark is set (lnum > 0) 370 | " 2. If buf_all = 0, filter out global marks from other buffers 371 | " 3. If buf_all = 1, filter out local marks from current buffer 372 | if ( (l:lnum == 0) 373 | \ || ( (a:scope == 0) 374 | \ && (l:bufnr != l:buf_curr) 375 | \ ) 376 | \ || ( (a:scope == 1) 377 | \ && (i =~# "[a-z]") 378 | \ ) 379 | \ ) 380 | "echom 'DEBUG: Skipping mark ' . i 381 | continue 382 | endif 383 | 384 | " If the buffer is not loaded, what's the point of showing empty context? 385 | let l:context = (bufloaded(l:bufnr) && a:0 ? a:1 : 0) 386 | 387 | for context_lnum in range(l:lnum - l:context, l:lnum + l:context) 388 | let l:text = get(getbufline(l:bufnr, context_lnum), 0, "") 389 | if (!bufloaded(l:bufnr)) 390 | " Buffer is not loaded, hence we won't be able to get the line. Opening the file should fix it 391 | let l:text = "~~~ File is not loaded into memory. Open file and rerun to see the line ~~~" 392 | elseif (l:text == "") 393 | " Line does not exist. Possibly because context_lnum > total no. of lines 394 | "echom 'DEBUG: Skipping line=' . context_lnum . ' for mark=' . i . " because line doesn't exist in buffer=" . l:bufnr 395 | continue 396 | endif 397 | 398 | if (context_lnum < l:lnum) | let l:text = '-: ' . l:text 399 | elseif (context_lnum > l:lnum) | let l:text = '+: ' . l:text 400 | else | let l:text = i . ': ' . l:text 401 | endif 402 | 403 | let l:list = add(l:list, 404 | \ { 'text' : l:text, 405 | \ 'bufnr': l:bufnr, 406 | \ 'lnum' : context_lnum, 407 | \ 'col' : l:col, 408 | \ 'type' : 'm' 409 | \ } 410 | \ ) 411 | endfor 412 | 413 | " Add separator when showing context 414 | "if (a:context > 0) 415 | " let l:list = add(l:list, l:list_sep) 416 | "endif 417 | endfor 418 | 419 | " Remove the redundant separator at the end when showing context 420 | "if ( (a:context > 0) 421 | " \ && (len(l:list) > 0) 422 | " \ ) 423 | " call remove(l:list, -1) 424 | "endif 425 | 426 | call setloclist(0, l:list) | lopen 427 | endfunction 428 | --------------------------------------------------------------------------------