Files
Server.Config-Deploy-Data/home-rc/dot-files/.vim/bundle/vim-signature/autoload/signature/sign.vim

376 lines
15 KiB
VimL
Raw Normal View History

2024-10-08 17:58:44 +09:00
" vim: fdm=marker:et:ts=4:sw=2:sts=2
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! signature#sign#Place(sign, lnum) "{{{1
" Description: Place signs for marks/markers on the specified line number
" Arguments:
" sign : The mark/marker whose sign is to be placed
" lnum : Line number on/from which the sign is to be placed/removed
"echom "DEBUG: sign = " . a:sign . ", lnum = " . a:lnum
" If Signature is not enabled, return
if !b:sig_enabled | return | endif
" FIXME: Highly inefficient. Needs work
" Place sign only if there are no signs from other plugins (eg. syntastic)
"let l:present_signs = s:GetInfo(1)
"if ( b:SignatureDeferPlacement
" \ && has_key(l:present_signs, a:lnum)
" \ && (l:present_signs[a:lnum]['name'] !~# '^sig_Sign_')
" \ )
" return
"endif
if signature#utils#IsValidMarker(a:sign)
let b:sig_markers[a:lnum] = a:sign . get(b:sig_markers, a:lnum, "")
elseif signature#utils#IsValidMark(a:sign)
let b:sig_marks[a:lnum] = a:sign . get(b:sig_marks, a:lnum, "")
else
echoerr "Unexpected sign found: " . a:sign
endif
"}}}3
call s:RefreshLine(a:lnum)
endfunction
function! signature#sign#Remove(sign, lnum) "{{{1
" Description: Remove signs for marks/markers from the specified line number
" Arguments:
" sign : The mark/marker whose sign is to be placed/removed/toggled
" lnum : Line number from which the sign is to be removed
" If sign is a marker and lnum is 0, the sign will be removed from all lines
" If sign is a mark and lnum is 0, the lnum will be found and the sign will be removed from that line
"echom "DEBUG: sign = " . a:sign . ", lnum = " . a:lnum
" If Signature is not enabled, return
if !b:sig_enabled | return | endif
" Remove sign for markers
if signature#utils#IsValidMarker(a:sign)
let b:sig_markers[a:lnum] = substitute(b:sig_markers[a:lnum], "\\C" . escape( a:sign, '$^' ), "", "")
" If there are no markers on the line, delete signs on that line
if b:sig_markers[a:lnum] == ""
call remove(b:sig_markers, a:lnum)
endif
call s:RefreshLine(a:lnum)
" Remove sign for marks
else
" For marks, if a:lnum == 0, find out the line where the mark was placed
if a:lnum == 0
let l:arr = keys(filter(copy(b:sig_marks), 'v:val =~# a:sign'))
if empty(l:arr) | return | endif
else
let l:arr = [a:lnum]
endif
if (v:version >= 800)
call assert_true(len(l:arr) == 1, "Multiple marks found where one was expected")
elseif (len(l:arr) != 1)
echoerr "Multiple marks found where one was expected"
endif
for l:lnum in l:arr
" FIXME: Placed guard to avoid triggering issue #53
if has_key(b:sig_marks, l:lnum)
let b:sig_marks[l:lnum] = substitute(b:sig_marks[l:lnum], "\\C" . a:sign, "", "")
" If there are no marks on the line, delete signs on that line
if b:sig_marks[l:lnum] == ""
call remove(b:sig_marks, l:lnum)
endif
endif
call s:RefreshLine(l:lnum)
endfor
endif
endfunction
function! s:EvaluateHL(expr, lnum, ...) "{{{1
" Description: If expr points to a function, call it and use its output as the highlight group.
" If it is a string, use it directly.
" If the optional argument is specified, use it as a fallback. If not, return an empty string
if type(a:expr) == type("")
return a:expr
elseif type(a:expr) == type(function("tr"))
let l:retval = a:expr(a:lnum)
if (l:retval != "")
return l:retval
endif
endif
return (a:0 > 0 ? a:1 : "")
endfunction
function! s:RefreshLine(lnum) "{{{1
" Description: Decides what the sign string should be based on if there are any marks or markers (using b:sig_marks
" and b:sig_markers) on the current line and the value of b:SignaturePrioritizeMarks.
" Arguments:
" lnum : Line number for which the sign string is to be modified
let l:id = abs(a:lnum * 1000 + bufnr('%'))
let l:str = ""
" Place the sign
if ( has_key(b:sig_marks, a:lnum)
\ && ( b:SignaturePrioritizeMarks
\ || !has_key(b:sig_markers, a:lnum)
\ )
\ )
let l:SignatureMarkTextHL = s:EvaluateHL(g:SignatureMarkTextHL, a:lnum, "SignatureMarkText")
let l:SignatureMarkLineHL = s:EvaluateHL(g:SignatureMarkLineHL, a:lnum, "SignatureMarkLine")
let l:str = substitute(b:SignatureMarkOrder, "\m", signature#utils#GetChar(b:sig_marks[a:lnum], 0), '')
let l:str = substitute(l:str, "\p", signature#utils#GetChar(b:sig_marks[a:lnum], 1), '')
execute 'sign define Signature_' . l:str . ' text=' . l:str . ' texthl=' . l:SignatureMarkTextHL . ' linehl=' . l:SignatureMarkLineHL
elseif has_key(b:sig_markers, a:lnum)
let l:SignatureMarkerTextHL = s:EvaluateHL(g:SignatureMarkerTextHL, a:lnum, "SignatureMarkerText")
let l:SignatureMarkerLineHL = s:EvaluateHL(g:SignatureMarkerLineHL, a:lnum, "SignatureMarkerLine")
" Since the same marker can be placed on multiple lines, we can't use the same sign for all of them.
" This is because if dynamic highlighting of markers is enabled then the sign placed on eg. a modified line should
" be highlighted differently than the one placed on an unchanged line.
" In order to support this, I append the name of the TextHL and LineHL group to the name of the sign.
let l:txt = signature#utils#GetChar(b:sig_markers[a:lnum], 0)
let l:str = l:txt . '_' . l:SignatureMarkerTextHL . '_' . l:SignatureMarkerLineHL
execute 'sign define Signature_' . l:str . ' text=' . l:txt . ' texthl=' . l:SignatureMarkerTextHL . ' linehl=' . l:SignatureMarkerLineHL
else
call signature#sign#Unplace(a:lnum)
endif
if (l:str != "")
execute 'sign place ' . l:id . ' line=' . a:lnum . ' name=Signature_' . l:str . ' buffer=' . bufnr('%')
endif
" If there is only 1 mark/marker in the file, place a dummy to prevent flickering of the gutter when it is moved
" If there are no signs left, remove the dummy
call signature#sign#ToggleDummy()
endfunction
function! signature#sign#Refresh(...) "{{{1
" Description: Add signs for new marks/markers and remove signs for deleted marks/markers
" Arguments: Specify an argument to force a sign refresh
call s:InitializeVars(a:0 && a:1)
" If Signature is not enabled, return
if !b:sig_enabled | return | endif
for i in signature#mark#GetList('free', 'buf_curr')
" ... remove it
call signature#sign#Remove(i, 0)
endfor
" Add signs for marks ...
for [l:mark, l:lnum, _] in signature#mark#GetList('used', 'buf_curr')
" ... if mark is not present in our b:sig_marks list or if it is present but at the wrong line,
" remove the old sign and add a new one
if ( !has_key(b:sig_marks, l:lnum)
\ || (b:sig_marks[l:lnum] !~# l:mark)
\ || a:0
\ )
call signature#sign#Remove(l:mark, 0)
call signature#sign#Place (l:mark, l:lnum)
endif
endfor
call signature#sign#ToggleDummy()
" We do not add signs for markers as SignRefresh is executed periodically and we don't have a way to determine if the
" marker already has a sign or not
endfunction
function! signature#sign#Unplace(lnum) "{{{1
" Description: Remove the sign from the specified line number
" FIXME: Clean-up. Undefine the sign
let l:id = abs(a:lnum * 1000 + bufnr('%'))
silent! execute 'sign unplace ' . l:id
endfunction
function! signature#sign#ToggleDummy(...) "{{{1
" Description: Places a dummy sign to prevent flickering of the gutter when the mark is moved or the line containing
" a mark/marker is deleted and then the delete is undone
" Arguments: (optional) 0 : force remove
" 1 : force place
let l:place = a:0 ? a:1 : (len(b:sig_marks) + len(b:sig_markers) == 1) && !b:sig_DummyExists
let l:remove = a:0 ? !a:1 : (len(b:sig_marks) + len(b:sig_markers) == 0) && b:sig_DummyExists
if (l:place)
sign define Signature_Dummy
execute 'sign place 666 line=1 name=Signature_Dummy buffer=' . bufnr('%')
let b:sig_DummyExists = 1
elseif (l:remove)
silent! execute 'sign unplace 666 buffer=' . bufnr('%')
let b:sig_DummyExists = 0
endif
endfunction
function! s:GetInfo(...) "{{{1
" Description: Returns a dic of filenames, each of which is a dic of line numbers on which signs are placed
" Arguments: filename (optional).
" If filename is provided, the return value will contain signs only present in the given file
" Eg. {
" 'vimrc': {
" '711': {
" 'id': '1422',
" 'name': 'sig_Sign_1422'
" },
" '676': {
" 'id': '1352',
" 'name': 'sig_Sign_1352'
" }
" }
" }
" Redirect the input to a variable
redir => l:sign_str
silent! sign place
redir END
" Create a Hash of files to store the info.
let l:signs_dic = {}
" The file that is currently being processed is stored into l:file
let l:match_file = ""
let l:file_found = 0
" Split the string into an array of sentences and filter out empty lines
for i in filter( split( l:sign_str, '\n' ), 'v:val =~ "^[S ]"' )
let l:temp_file = matchstr( i, '\v(Signs for )@<=\S+:@=' )
if l:temp_file != ""
let l:match_file = l:temp_file
let l:signs_dic[l:match_file] = {}
elseif l:match_file != ""
" Get sign info
let l:info_match = matchlist( i, '\vline\=(\d+)\s*id\=(\S+)\s*name\=(\S+)' )
if !empty( l:info_match )
let l:signs_dic[l:match_file][l:info_match[1]] = {
\ 'id' : l:info_match[2],
\ 'name' : l:info_match[3],
\ }
endif
endif
endfor
if a:0
"" Search for the full path first in the hash ...
"let l:curr_filepath = expand('%:p')
"if has_key( l:signs_dic, l:curr_filepath )
" return filter( l:signs_dic, 'v:key ==# l:curr_filepath' )[l:curr_filepath]
"else
" ... if no entry is found for the full path, search for the filename in the hash ...
" Since we're searching for the current file, if present in the hash, it'll be as a filename and not the full path
let l:curr_filename = expand('%:t')
if has_key( l:signs_dic, l:curr_filename )
return filter( l:signs_dic, 'v:key ==# l:curr_filename' )[l:curr_filename]
endif
" ... if nothing is found, then return an empty hash to indicate that no signs are present in the current file
return {}
endif
return l:signs_dic
endfunction
function! signature#sign#GetGitGutterHLGroup(lnum) "{{{1
" Description: This returns the highlight group used by vim-gitgutter depending on how the line was edited
let l:current_bufnr = bufnr('%')
let l:line_state = filter(copy(gitgutter#diff#process_hunks(l:current_bufnr, gitgutter#hunk#hunks(l:current_bufnr))), 'v:val[0] == a:lnum')
if len(l:line_state) == 0
return ""
endif
if (l:line_state[0][1]) =~ 'added' | return 'GitGutterAdd'
elseif (l:line_state[0][1]) =~ 'modified_removed' | return 'GitGutterChangeDelete'
elseif (l:line_state[0][1]) =~ 'modified' | return 'GitGutterChange'
elseif (l:line_state[0][1]) =~ 'removed' | return 'GitGutterDelete'
endif
endfunction
function! signature#sign#GetSignifyHLGroup(lnum) "{{{1
" Description: This returns the highlight group used by vim-signify depending on how the line was edited
" Thanks to @michaelmior
if !exists('b:sy')
return ""
endif
call sy#sign#get_current_signs(b:sy)
if has_key(b:sy.internal, a:lnum)
let l:line_state = b:sy.internal[a:lnum]['type']
if l:line_state =~ 'SignifyAdd' | return 'SignifySignAdd'
elseif l:line_state =~ 'SignifyChange' | return 'SignifySignChange'
elseif l:line_state =~ 'SignifyDelete' | return 'SignifySignDelete'
end
endif
return ""
endfunction
" function! signature#sign#GetMarkSignLine(mark) "{{{1
" if !signature#utils#IsValidMark(a:mark)
" echoe "Signature: Invalid mark " . a:mark
" return
" endif
" let l:sign_info=filter(split(execute('sign place'), '\n'),
" \ 'v:val =~ "\\vSignature_(.?' . a:mark . '|' . a:mark . '.?)$"')
" if (len(l:sign_info) != 1)
" echoe "Signature: Expected single match, found " . len(l:sign_info)
" return
" endif
" return matchstr(l:sign_info[0], '\v(line\=)@<=\d+')
" endfunction
function! s:InitializeVars(...) "{{{1
" Description: Initialize variables
" Arguments: Specify an argument to re-init
if !exists('b:sig_marks')
" b:sig_marks = { lnum => signs_str }
let b:sig_marks = {}
else
" Lines can be removed using an external tool. Hence, we need to filter out marks placed on line numbers that are
" now greater than the total number of lines in the file.
let l:line_tot = line('$')
call filter( b:sig_marks, 'v:key <= l:line_tot' )
endif
if !exists('b:sig_markers')
" b:sig_markers = { lnum => marker }
let b:sig_markers = {}
else
" Lines can be removed using an external tool. Hence, we need to filter out marks placed on line numbers that are
" now greater than the total number of lines in the file.
let l:line_tot = line('$')
call filter( b:sig_markers, 'v:key <= l:line_tot' )
endif
call signature#utils#Set('b:sig_DummyExists' , 0 , a:0 && a:1)
call signature#utils#Set('b:sig_enabled' , g:SignatureEnabledAtStartup, a:0 && a:1)
call signature#utils#Set('b:SignatureIncludeMarks' , g:SignatureIncludeMarks , a:0 && a:1)
call signature#utils#Set('b:SignatureIncludeMarkers' , g:SignatureIncludeMarkers , a:0 && a:1)
call signature#utils#Set('b:SignatureMarkOrder' , g:SignatureMarkOrder , a:0 && a:1)
call signature#utils#Set('b:SignaturePrioritizeMarks', g:SignaturePrioritizeMarks , a:0 && a:1)
call signature#utils#Set('b:SignatureDeferPlacement' , g:SignatureDeferPlacement , a:0 && a:1)
call signature#utils#Set('b:SignatureWrapJumps' , g:SignatureWrapJumps , a:0 && a:1)
endfunction