install.fairie/dotfiles/.vim/plugged/YouCompleteMe/python/ycm/signature_help.py

199 lines
6.4 KiB
Python
Raw Normal View History

# Copyright (C) 2011-2018 YouCompleteMe contributors
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import vim
import json
from ycm import vimsupport
from ycmd import utils
from ycm.vimsupport import memoize, GetIntValue
class SignatureHelpState:
ACTIVE = 'ACTIVE'
INACTIVE = 'INACTIVE'
ACTIVE_SUPPRESSED = 'ACTIVE_SUPPRESSED'
def __init__( self,
popup_win_id = None,
state = INACTIVE ):
self.popup_win_id = popup_win_id
self.state = state
self.anchor = None
def ToggleVisibility( self ):
if self.state == 'ACTIVE':
self.state = 'ACTIVE_SUPPRESSED'
vim.eval( f'popup_hide( { self.popup_win_id } )' )
elif self.state == 'ACTIVE_SUPPRESSED':
self.state = 'ACTIVE'
vim.eval( f'popup_show( { self.popup_win_id } )' )
def IsActive( self ):
if self.state in ( 'ACTIVE', 'ACTIVE_SUPPRESSED' ):
return 'ACTIVE'
return 'INACTIVE'
def _MakeSignatureHelpBuffer( signature_info ):
active_parameter = int( signature_info.get( 'activeParameter', 0 ) )
lines = []
signatures = ( signature_info.get( 'signatures' ) or [] )
for sig_index, signature in enumerate( signatures ):
props = []
sig_label = signature[ 'label' ]
parameters = ( signature.get( 'parameters' ) or [] )
for param_index, parameter in enumerate( parameters ):
param_label = parameter[ 'label' ]
begin = int( param_label[ 0 ] )
end = int( param_label[ 1 ] )
if param_index == active_parameter:
props.append( {
'col': begin + 1, # 1-based
'length': end - begin,
'type': 'YCM-signature-help-current-argument'
} )
lines.append( {
'text': sig_label,
'props': props
} )
return lines
@memoize()
def ShouldUseSignatureHelp():
return ( vimsupport.VimHasFunctions( 'screenpos', 'pum_getpos' ) and
vimsupport.VimSupportsPopupWindows() )
def UpdateSignatureHelp( state, signature_info ): # noqa
if not ShouldUseSignatureHelp():
return state
signatures = signature_info.get( 'signatures' ) or []
if not signatures:
if state.popup_win_id:
# TODO/FIXME: Should we use popup_hide() instead ?
vim.eval( f"popup_close( { state.popup_win_id } )" )
return SignatureHelpState( None, SignatureHelpState.INACTIVE )
if state.state == SignatureHelpState.INACTIVE:
state.anchor = vimsupport.CurrentLineAndColumn()
state.state = SignatureHelpState.ACTIVE
# Generate the buffer as a list of lines
buf_lines = _MakeSignatureHelpBuffer( signature_info )
screen_pos = vimsupport.ScreenPositionForLineColumnInWindow(
vim.current.window,
state.anchor[ 0 ] + 1, # anchor 0-based
state.anchor[ 1 ] + 1 ) # anchor 0-based
# Simulate 'flip' at the screen boundaries by using screenpos and hiding the
# signature help menu if it overlaps the completion popup (pum).
#
# FIXME: revert to cursor-relative positioning and the 'flip' option when that
# is implemented (if that is indeed better).
# By default display above the anchor
line = int( screen_pos[ 'row' ] ) - 1 # -1 to display above the cur line
pos = "botleft"
cursor_line = vimsupport.CurrentLineAndColumn()[ 0 ] + 1
if int( screen_pos[ 'row' ] ) <= len( buf_lines ):
# No room at the top, display below
line = int( screen_pos[ 'row' ] ) + 1
pos = "topleft"
# Don't allow the popup to overlap the cursor
if ( pos == 'topleft' and
line < cursor_line and
line + len( buf_lines ) >= cursor_line ):
line = 0
# Don't allow the popup to overlap the pum
if line > 0 and GetIntValue( 'pumvisible()' ):
pum_line = GetIntValue( 'pum_getpos().row' ) + 1
if pos == 'botleft' and pum_line <= line:
line = 0
elif ( pos == 'topleft' and
pum_line >= line and
pum_line < ( line + len( buf_lines ) ) ):
line = 0
if line <= 0:
# Nowhere to put it so hide it
if state.popup_win_id:
# TODO/FIXME: Should we use popup_hide() instead ?
vim.eval( f"popup_close( { state.popup_win_id } )" )
return SignatureHelpState( None, SignatureHelpState.INACTIVE )
if int( screen_pos[ 'curscol' ] ) <= 1:
col = 1
else:
# -1 for padding,
# -1 for the trigger character inserted (the anchor is set _after_ the
# character is inserted, so we remove it).
# FIXME: multi-byte characters would be wrong. Need to set anchor before
# inserting the char ?
col = int( screen_pos[ 'curscol' ] ) - 2
if col <= 0:
col = 1
options = {
"line": line,
"col": col,
"pos": pos,
"wrap": 0,
# NOTE: We *dont'* use "cursorline" here - that actually uses PMenuSel,
# which is just too invasive for us (it's more selected item than actual
# cursorline. So instead, we manually set 'cursorline' in the popup window
# and enable syntax based on the current file syntax)
"flip": 1,
"padding": [ 0, 1, 0, 1 ], # Pad 1 char in X axis to match completion menu
"hidden": int( state.state == SignatureHelpState.ACTIVE_SUPPRESSED )
}
if not state.popup_win_id:
state.popup_win_id = GetIntValue(
f'popup_create( { json.dumps( buf_lines ) }, '
f'{ json.dumps( options ) } )' )
else:
vim.eval( f'popup_settext( { state.popup_win_id }, '
f'{ json.dumps( buf_lines ) } )' )
# Should do nothing if already visible
vim.eval( f'popup_move( { state.popup_win_id }, { json.dumps( options ) } )' )
if state.state == SignatureHelpState.ACTIVE:
vim.eval( f'popup_show( { state.popup_win_id } )' )
syntax = utils.ToUnicode( vim.current.buffer.options[ 'syntax' ] )
active_signature = int( signature_info.get( 'activeSignature', 0 ) )
vim.eval( f"win_execute( { state.popup_win_id }, "
f"'set syntax={ syntax } cursorline | "
f"call cursor( [ { active_signature + 1 }, 1 ] )' )" )
return state