Skip to content

Commit

Permalink
Add root_location to the incoming calls hierarchy responses
Browse files Browse the repository at this point in the history
```go
package main

func f() (int) {
    return 5;
}
func g() (int) {
    return f() + f();
}
func h() (int) {
    var x = g();
    return f() + x;
}
```

With incoming calls request on `g()`, the response should be something
like this

```python
{
  'locations': [ location_of_call_to_g_inside_h ],
  'root_location': location_of_h_function_name
}
```

This allows clients to:

1. Jump to the actual call site, using `locations` list.
2. Switch to outgoing calls, by preparing a new hierarchy, using
   `root_location` object.
  • Loading branch information
bstaletic committed Jun 13, 2024
1 parent 92a91c6 commit d6010fc
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 42 deletions.
17 changes: 17 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4650,6 +4650,23 @@ <h3 class="panel-title"><a name="/definitions/Hierarchy"></a>Hierarchy:
</div>
</section> </div>
</dd>
<dt data-property-name="root_location">
<span class="json-property-name">root_location:</span>

<span class="json-property-type"> <a class="json-schema-ref" href="#/definitions/Location">Location</a>
</span>
<span class="json-property-range" title="Value limits"></span>

</dt>
<dd>
<p>In call hierarchies, it is useful to differentiate between the call
site (which end up in the <code>locations</code> property) and the calling
function. This property, if present, represents the latter.</p>

<div class="json-inner-schema">

</div>
</dd>
</dl>
</section>
</div>
Expand Down
7 changes: 7 additions & 0 deletions docs/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ definitions:
type: array
items:
$ref: "#/defintions/Location"
root_location:
type: object
description: |-
In call hierarchies, it is useful to differentiate between the call
site (which end up in the `locations` property) and the calling
function. This property, if present, represents the latter.
$ref: "#/definitions/Location"
# Due to the way the API combines keys at the top level, we are not able to
# compose this item per-request. So this definition must be copy-pasted for
# some requests.
Expand Down
15 changes: 12 additions & 3 deletions ycmd/completers/language_server/language_server_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2721,17 +2721,26 @@ def Hierarchy( self, request_data, args ):
for item in result:
if kind == 'call':
name_and_kind_key = 'to' if direction == 'outgoingCalls' else 'from'
kind_string = lsp.SYMBOL_KIND[ item[ name_and_kind_key ][ 'kind' ] ]
hierarchy_item = item[ name_and_kind_key ]
kind_string = lsp.SYMBOL_KIND[ hierarchy_item[ 'kind' ] ]
item[ 'kind' ] = kind_string
item[ 'name' ] = item[ name_and_kind_key ][ 'name' ]
item[ 'name' ] = hierarchy_item[ 'name' ]
lsp_locations = [ {
'uri': item[ name_and_kind_key ][ 'uri' ],
'uri': hierarchy_item[ 'uri' ],
'range': r }
for r in item[ 'fromRanges' ] ]
item[ 'locations' ] = [
responses.BuildGoToResponseFromLocation(
*_LspLocationToLocationAndDescription( request_data, location ) )
for location in lsp_locations ]

if direction == 'incomingCalls':
loc = {
'uri': hierarchy_item[ 'uri' ],
'range': hierarchy_item[ 'range' ]
}
item[ 'root_location' ] = responses.BuildGoToResponseFromLocation(
*_LspLocationToLocationAndDescription( request_data, loc ) )
else:
item[ 'kind' ] = lsp.SYMBOL_KIND[ item[ 'kind' ] ]
item[ 'locations' ] = [
Expand Down
29 changes: 16 additions & 13 deletions ycmd/tests/clangd/subcommands_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1395,22 +1395,25 @@ def test_Subcommands_IncomingCallHierarchy( self, app ):
for location, response, code in [
[ ( filepath, 1, 5 ),
contains_inanyorder(
has_entry( 'locations',
contains_exactly(
LocationMatcher( filepath, 4, 12 ),
LocationMatcher( filepath, 4, 18 )
) ),
has_entry( 'locations',
contains_exactly(
LocationMatcher( filepath, 9, 12 )
) ) ),
has_entries( {
'locations': contains_exactly(
LocationMatcher( filepath, 4, 12 ),
LocationMatcher( filepath, 4, 18 ) ),
'root_location': LocationMatcher( filepath, 3, 5 )
} ),
has_entries( {
'locations': contains_exactly( LocationMatcher( filepath, 9, 12 ) ),
'root_location': LocationMatcher( filepath, 7, 5 )
} )
),
requests.codes.ok ],
[ ( filepath, 3, 5 ),
contains_inanyorder(
has_entry( 'locations',
contains_exactly(
LocationMatcher( filepath, 8, 13 )
) ) ),
has_entries( {
'locations': contains_exactly( LocationMatcher( filepath, 8, 13 ) ),
'root_location': LocationMatcher( filepath, 7, 5 )
} )
),
requests.codes.ok ],
[ ( filepath, 7, 5 ),
ErrorMatcher( RuntimeError, 'No incoming calls found.' ),
Expand Down
31 changes: 18 additions & 13 deletions ycmd/tests/go/subcommands_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,22 +598,27 @@ def test_Subcommands_IncomingCallHierarchy( self, app ):
for location, response, code in [
[ ( filepath, 3, 6 ),
contains_inanyorder(
has_entry( 'locations',
contains_exactly(
LocationMatcher( filepath, 7, 12 ),
LocationMatcher( filepath, 7, 18 )
) ),
has_entry( 'locations',
contains_exactly(
LocationMatcher( filepath, 11, 12 )
) ) ),
has_entries( {
'locations': contains_exactly(
LocationMatcher( filepath, 7, 12 ),
LocationMatcher( filepath, 7, 18 ) ),
'root_location': LocationMatcher( filepath, 6, 6 )
} ),
has_entries( {
'locations': contains_exactly(
LocationMatcher( filepath, 11, 12 ) ),
'root_location': LocationMatcher( filepath, 9, 6 )
} )
),
requests.codes.ok ],
[ ( filepath, 6, 6 ),
contains_inanyorder(
has_entry( 'locations',
contains_exactly(
LocationMatcher( filepath, 10, 13 )
) ) ),
has_entries( {
'locations': contains_exactly(
LocationMatcher( filepath, 10, 13 ) ),
'root_location': LocationMatcher( filepath, 9, 6 )
} )
),
requests.codes.ok ],
[ ( filepath, 9, 6 ),
ErrorMatcher( RuntimeError, 'No incoming calls found.' ),
Expand Down
37 changes: 24 additions & 13 deletions ycmd/tests/rust/subcommands_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,22 +629,33 @@ def test_Subcommands_IncomingCallHierarchy( self, app ):
for location, response, code in [
[ ( filepath, 1, 4 ),
contains_inanyorder(
has_entry( 'locations',
contains_exactly(
LocationMatcher( filepath, 6, 5 ),
LocationMatcher( filepath, 6, 11 )
) ),
has_entry( 'locations',
contains_exactly(
LocationMatcher( filepath, 11, 5 )
) ) ),
has_entries( {
'locations': contains_exactly(
LocationMatcher( filepath, 6, 5 ),
LocationMatcher( filepath, 6, 11 ) ),
# rust-analyzer always returns column 1 for root_location,
# which is useless for us... unfortunately.
'root_location': LocationMatcher( filepath, 5, 1 )
} ),
has_entries( {
'locations': contains_exactly(
LocationMatcher( filepath, 11, 5 ) ),
# rust-analyzer always returns column 1 for root_location,
# which is useless for us... unfortunately.
'root_location': LocationMatcher( filepath, 9, 1 )
} )
),
requests.codes.ok ],
[ ( filepath, 5, 4 ),
contains_inanyorder(
has_entry( 'locations',
contains_exactly(
LocationMatcher( filepath, 10, 13 )
) ) ),
has_entries( {
'locations': contains_exactly(
LocationMatcher( filepath, 10, 13 ) ),
# rust-analyzer always returns column 1 for root_location,
# which is useless for us... unfortunately.
'root_location': LocationMatcher( filepath, 9, 1 )
} )
),
requests.codes.ok ],
[ ( filepath, 9, 4 ),
ErrorMatcher( RuntimeError, 'No incoming calls found.' ),
Expand Down

0 comments on commit d6010fc

Please sign in to comment.