Skip to content

Commit 601026e

Browse files
committed
improve hierarchy
add commands: 1. reopen hierarchy window. 2. Add a caller to current node manually. 3. PageUp and PageDown. 4. Close callers. 5. Remove callers.
1 parent b5fe27b commit 601026e

File tree

5 files changed

+165
-21
lines changed

5 files changed

+165
-21
lines changed

autoload/youcompleteme.vim

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,6 +1767,10 @@ silent! nnoremap <silent> <plug>(YCMTypeHierarchy)
17671767
\ <cmd>call youcompleteme#hierarchy#StartRequest( 'type' )<cr>
17681768
silent! nnoremap <silent> <plug>(YCMCallHierarchy)
17691769
\ <cmd>call youcompleteme#hierarchy#StartRequest( 'call' )<cr>
1770+
silent! nnoremap <silent> <plug>(YCMResumeHierarchy)
1771+
\ <cmd>call youcompleteme#hierarchy#StartRequest( 'resume' )<cr>
1772+
silent! nnoremap <silent> <plug>(YCMAddCallHierarchy)
1773+
\ <cmd>call youcompleteme#hierarchy#StartRequest( 'addcall' )<cr>
17701774
17711775
" This is basic vim plugin boilerplate
17721776
let &cpo = s:save_cpo

autoload/youcompleteme/hierarchy.vim

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,33 @@ function! youcompleteme#hierarchy#StartRequest( kind )
4141
endif
4242

4343
call youcompleteme#symbol#InitSymbolProperties()
44-
py3 ycm_state.ResetCurrentHierarchy()
4544
py3 from ycm.client.command_request import GetRawCommandResponse
45+
46+
if a:kind == 'resume'
47+
call s:SetUpMenu()
48+
return
49+
endif
50+
51+
if a:kind == 'addcall'
52+
let handle = s:lines_and_handles[ s:select - 1 ][ 1 ]
53+
let lines_and_handles = py3eval(
54+
\ 'ycm_state.AddCurrentHierarchy( ' .
55+
\ 'vimsupport.GetIntValue( "handle" ), ' .
56+
\ 'GetRawCommandResponse( ' .
57+
\ '[ "CallHierarchy" ], False ))' )
58+
let s:lines_and_handles = lines_and_handles
59+
call s:SetUpMenu()
60+
return
61+
endif
62+
63+
py3 ycm_state.ResetCurrentHierarchy()
4664
if a:kind == 'call'
4765
let lines_and_handles = py3eval(
4866
\ 'ycm_state.InitializeCurrentHierarchy( GetRawCommandResponse( ' .
4967
\ '[ "CallHierarchy" ], False ), ' .
5068
\ 'vim.eval( "a:kind" ) )' )
5169
else
52-
let lines_and_handles = py3eval(
70+
let lines_and_handles = py3eval(
5371
\ 'ycm_state.InitializeCurrentHierarchy( GetRawCommandResponse( ' .
5472
\ '[ "TypeHierarchy" ], False ), ' .
5573
\ 'vim.eval( "a:kind" ) )' )
@@ -62,7 +80,22 @@ function! youcompleteme#hierarchy#StartRequest( kind )
6280
endif
6381
endfunction
6482

83+
function! s:RedrawMenu()
84+
let pos = popup_getpos( s:popup_id )
85+
call win_execute( s:popup_id,
86+
\ 'call cursor( [' . string( s:select ) . ', 1 ] )' )
87+
call win_execute( s:popup_id,
88+
\ 'set cursorline cursorlineopt&' )
89+
if s:select < pos.firstline
90+
call win_execute( s:popup_id, "normal z\<CR>" )
91+
endif
92+
if s:select >= (pos.firstline + pos.core_height )
93+
call win_execute( s:popup_id, ':normal z-' )
94+
endif
95+
endfunction
96+
6597
function! s:MenuFilter( winid, key )
98+
let pos = popup_getpos( s:popup_id )
6699
if a:key == "\<S-Tab>"
67100
" Root changes if we're showing super-tree of a sub-tree of the root
68101
" (indicated by the handle being positive)
@@ -81,6 +114,20 @@ function! s:MenuFilter( winid, key )
81114
\ [ s:select - 1, 'resolve_down', will_change_root ] )
82115
return 1
83116
endif
117+
if a:key == "c"
118+
let will_change_root = 0
119+
call popup_close(
120+
\ s:popup_id,
121+
\ [ s:select - 1, 'resolve_close', will_change_root ] )
122+
return 1
123+
endif
124+
if a:key == "d"
125+
let will_change_root = 0
126+
call popup_close(
127+
\ s:popup_id,
128+
\ [ s:select - 1, 'resolve_remove', will_change_root ] )
129+
return 1
130+
endif
84131
if a:key == "\<CR>"
85132
call popup_close( s:popup_id, [ s:select - 1, 'jump', v:none ] )
86133
return 1
@@ -90,21 +137,31 @@ function! s:MenuFilter( winid, key )
90137
if s:select < 1
91138
let s:select = 1
92139
endif
93-
call win_execute( s:popup_id,
94-
\ 'call cursor( [' . string( s:select ) . ', 1 ] )' )
95-
call win_execute( s:popup_id,
96-
\ 'set cursorline cursorlineopt&' )
140+
call s:RedrawMenu()
141+
return 1
142+
endif
143+
if a:key == "\<PageUp>" || a:key == "\<kPageUp>"
144+
let s:select -= pos.core_height
145+
if s:select < 1
146+
let s:select = 1
147+
endif
148+
call s:RedrawMenu()
97149
return 1
98150
endif
99151
if a:key == "\<Down>" || a:key == "\<C-n>" || a:key == "\<C-j>" || a:key == "j"
100152
let s:select += 1
101153
if s:select > len( s:lines_and_handles )
102154
let s:select = len( s:lines_and_handles )
103155
endif
104-
call win_execute( s:popup_id,
105-
\ 'call cursor( [' . string( s:select ) . ', 1 ] )' )
106-
call win_execute( s:popup_id,
107-
\ 'set cursorline cursorlineopt&' )
156+
call s:RedrawMenu()
157+
return 1
158+
endif
159+
if a:key == "\<PageDown>" || a:key == "\<kPageDown>"
160+
let s:select += pos.core_height
161+
if s:select > len( s:lines_and_handles )
162+
let s:select = len( s:lines_and_handles )
163+
endif
164+
call s:RedrawMenu()
108165
return 1
109166
endif
110167
if index( s:ingored_keys, a:key ) >= 0
@@ -125,14 +182,15 @@ function! s:MenuCallback( winid, result )
125182
call s:ResolveItem( selection, 'down', a:result[ 2 ] )
126183
elseif operation == 'resolve_up'
127184
call s:ResolveItem( selection, 'up', a:result[ 2 ] )
185+
elseif operation == 'resolve_close'
186+
call s:ResolveItem( selection, 'close', a:result[ 2 ] )
187+
elseif operation == 'resolve_remove'
188+
call s:ResolveItem( selection, 'remove', a:result[ 2 ] )
128189
else
129190
if operation == 'jump'
130191
let handle = s:lines_and_handles[ selection ][ 1 ]
131192
py3 ycm_state.JumpToHierarchyItem( vimsupport.GetIntValue( "handle" ) )
132193
endif
133-
py3 ycm_state.ResetCurrentHierarchy()
134-
let s:kind = ''
135-
let s:select = 1
136194
endif
137195
endfunction
138196

@@ -208,6 +266,7 @@ function! s:SetUpMenu()
208266
\ . "\t"
209267
\ .. trunc_desc
210268
call add( menu_lines, { 'text': line, 'props': props } )
269+
211270
endfor
212271
call win_execute( s:popup_id,
213272
\ 'setlocal tabstop=' . tabstop )

doc/youcompleteme.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2162,13 +2162,20 @@ are supported:
21622162
- Call hierarchy '<Plug>(YCMCallHierarchy)': Display callees and callers of
21632163
the symbol under cursor. Expand down to callers and up to callees.
21642164

2165+
- Resume hierarchy '<Plug>(YCMResumeHierarchy)': Reopen the Hierarchy window.
2166+
2167+
- Add hierarchy '<Plug>(YCMAddCallHierarchy)': Add a caller to current node
2168+
manually.
2169+
21652170
Take a look at this Image: asciicast [85] for brief demo.
21662171

21672172
Hierarchy UI can be initiated by mapping something to the indicated plug
21682173
mappings, for example:
21692174
>
21702175
nmap <leader>yth <Plug>(YCMTypeHierarchy)
21712176
nmap <leader>ych <Plug>(YCMCallHierarchy)
2177+
nmap <leader>ycr <Plug>(YCMResumeHierarchy)
2178+
nmap <leader>yca <Plug>(YCMAddCallHierarchy)
21722179
<
21732180
This opens a "modal" popup showing the current element in the hierarchy tree.
21742181
The current tree root is aligned to the left and child and parent nodes are
@@ -2181,6 +2188,16 @@ inheritance where a "child" of the current root may actually have other,
21812188
invisible, parent links. '<S-Tab>' on that row will show these by setting the
21822189
display root to the selected item.
21832190

2191+
When YCMCallHierarchy cannot find the actual caller, '<Plug>(YCMAddCallHierarchy)'
2192+
will be very useful. For example, when tracking the caller of callback functions
2193+
in C language, YCMCallHierarchy may not be able to find the true caller; instead,
2194+
it may trace related registration functions or initialization functions. The
2195+
relevant code passes the callback function to a function pointer, resulting in
2196+
a call stack that may not be what you are looking for. In this case, you can
2197+
manually find the function that calls the function pointer, which is the true
2198+
caller, and use '<Plug>(YCMAddCallHierarchy)' to manually add the true caller
2199+
to the call stack, thus extending the call stack.
2200+
21842201
When the hierarchy is displayed, the following keys are intercepted:
21852202

21862203
- '<Tab>': Drill into the hierarchy at the selected item: expand and show
@@ -2191,6 +2208,11 @@ When the hierarchy is displayed, the following keys are intercepted:
21912208
- '<CR>': Jump to the symbol currently selected.
21922209
- '<Down>', '<C-n>', '<C-j>', 'j': Select the next item
21932210
- '<Up>', '<C-p>', '<C-k>', 'k'; Select the previous item
2211+
- '<PageUp>': Select the item on the previous page.
2212+
- '<PageDown>': Select the item on the next page.
2213+
- '<c>': Close the current item, in other words, remove the caller of the
2214+
item under the cursorline.
2215+
- '<d>': Remove the current item.
21942216
- Any other key: closes the popup without jumping to any location
21952217

21962218
**Note:** you might think the call hierarchy tree is inverted, but we think

python/ycm/hierarchy_tree.py

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121

2222

2323
class HierarchyNode:
24-
def __init__( self, data, distance : int ):
24+
def __init__( self, data, distance : int, parent ):
2525
self._references : Optional[ List[ int ] ] = None
2626
self._data = data
2727
self._distance_from_root = distance
28-
28+
self._parent = parent
2929

3030
def ToRootLocation( self, subindex : int ):
3131
if location := self._data.get( 'root_location' ):
@@ -70,8 +70,8 @@ def SetRootNode( self, items, kind : str ):
7070
if items:
7171
assert len( items ) == 1
7272
self._root_node_indices = [ 0 ]
73-
self._down_nodes.append( HierarchyNode( items[ 0 ], 0 ) )
74-
self._up_nodes.append( HierarchyNode( items[ 0 ], 0 ) )
73+
self._down_nodes.append( HierarchyNode( items[ 0 ], 0, None ) )
74+
self._up_nodes.append( HierarchyNode( items[ 0 ], 0, None ) )
7575
self._kind = kind
7676
return self.HierarchyToLines()
7777
return []
@@ -80,15 +80,58 @@ def SetRootNode( self, items, kind : str ):
8080
def UpdateHierarchy( self, handle : int, items, direction : str ):
8181
current_index = handle_to_index( handle )
8282
nodes = self._down_nodes if direction == 'down' else self._up_nodes
83+
node = nodes[ current_index ]
8384
if items:
8485
nodes.extend( [
8586
HierarchyNode( item,
86-
nodes[ current_index ]._distance_from_root + 1 )
87+
node._distance_from_root + 1, node )
8788
for item in items ] )
88-
nodes[ current_index ]._references = list(
89+
node._references = list(
8990
range( len( nodes ) - len( items ), len( nodes ) ) )
9091
else:
91-
nodes[ current_index ]._references = []
92+
node._references = []
93+
94+
95+
def AddNodes( self, handle : int, items ):
96+
if not items:
97+
return
98+
current_index = handle_to_index( handle )
99+
nodes = self._down_nodes
100+
node = nodes[ current_index ]
101+
nodes.extend( [
102+
HierarchyNode( item, node._distance_from_root + 1, node )
103+
for item in items ] )
104+
new_refs = list( range( len( nodes ) - len( items ), len( nodes ) ) )
105+
if node._references:
106+
node._references.extend( new_refs)
107+
else:
108+
node._references = new_refs
109+
110+
111+
def RemoveNode( self, handle : int ):
112+
current_index = handle_to_index( handle )
113+
nodes = self._down_nodes
114+
node = nodes[ current_index ]
115+
self._CloseNode( node )
116+
if node._parent:
117+
idx = node._parent._references.remove( current_index )
118+
nodes[ current_index ] = None
119+
120+
def _CloseNode( self, node: HierarchyNode ):
121+
nodes = self._down_nodes
122+
if node._references:
123+
for subindex in node._references:
124+
if nodes[ subindex ]:
125+
self._CloseNode( nodes[ subindex ])
126+
nodes[ subindex ] = None
127+
node._references = None
128+
129+
130+
def CloseNode( self, handle : int):
131+
current_index = handle_to_index( handle )
132+
nodes = self._down_nodes
133+
node = nodes[ current_index ]
134+
self._CloseNode( node )
92135

93136

94137
def Reset( self ):

python/ycm/youcompleteme.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,13 @@ def InitializeCurrentHierarchy( self, items, kind ):
118118

119119

120120
def UpdateCurrentHierarchy( self, handle : int, direction : str ):
121-
if not self._current_hierarchy.UpdateChangesRoot( handle, direction ):
121+
if direction == 'close':
122+
self._current_hierarchy.CloseNode( handle )
123+
return self._current_hierarchy.HierarchyToLines(), 0
124+
elif direction == 'remove':
125+
self._current_hierarchy.RemoveNode( handle )
126+
return self._current_hierarchy.HierarchyToLines(), 0
127+
elif not self._current_hierarchy.UpdateChangesRoot( handle, direction ):
122128
items = self._ResolveHierarchyItem( handle, direction )
123129
self._current_hierarchy.UpdateHierarchy( handle, items, direction )
124130

@@ -143,6 +149,16 @@ def UpdateCurrentHierarchy( self, handle : int, direction : str ):
143149
return self.UpdateCurrentHierarchy( handle, direction )
144150

145151

152+
def AddCurrentHierarchy( self, handle : int, items ):
153+
self._current_hierarchy.AddNodes( handle, items )
154+
return self._current_hierarchy.HierarchyToLines()
155+
156+
157+
def RemoveCurrentHierarchy( self, handle : int ):
158+
self._current_hierarchy.RemoveNode( handle )
159+
return self._current_hierarchy.HierarchyToLines()
160+
161+
146162
def _ResolveHierarchyItem( self, handle : int, direction : str ):
147163
return GetRawCommandResponse(
148164
self._current_hierarchy.ResolveArguments( handle, direction ),

0 commit comments

Comments
 (0)