Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions src/ulsp/controller/scip/scip.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,49 @@ func (c *controller) gotoTypeDefinition(ctx context.Context, params *protocol.Ty
}

func (c *controller) gotoImplementation(ctx context.Context, params *protocol.ImplementationParams, result *[]protocol.LocationLink) error {
// TODO: Implement code navigation
// https://t3.uberinternal.com/browse/IDE-642
sesh, err := c.sessions.GetFromContext(ctx)
if err != nil {
return err
}
reg := c.registries[sesh.WorkspaceRoot]
if reg == nil {
return nil
}

mappedPosition, err := c.getBasePosition(ctx, params.TextDocument, params.Position)
if err != nil {
return err
} else if mappedPosition == nil {
return nil
}

baseLocations, err := reg.Implementation(params.TextDocument.URI, *mappedPosition)
if err != nil {
return fmt.Errorf("failed to get implementations: %w", err)
}

if len(baseLocations) == 0 {
return nil
}

var originSel *protocol.Range
if _, occ, err := reg.Hover(params.TextDocument.URI, *mappedPosition); err == nil && occ != nil {
rng := mapper.ScipToProtocolRange(occ.Range)
mapped := c.getLatestRange(ctx, params.TextDocument, rng)
originSel = &mapped
}

for _, implLoc := range baseLocations {
l := protocol.LocationLink{TargetURI: implLoc.URI}
latestRange := c.getLatestRange(ctx, protocol.TextDocumentIdentifier{URI: implLoc.URI}, implLoc.Range)
l.TargetRange = latestRange
l.TargetSelectionRange = latestRange
if originSel != nil {
l.OriginSelectionRange = originSel
}
*result = append(*result, l)
}

return nil
}

Expand Down
86 changes: 83 additions & 3 deletions src/ulsp/controller/scip/scip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1128,10 +1128,90 @@ func TestGotoImplementation(t *testing.T) {
ctx := context.Background()
ctrl := gomock.NewController(t)

c, _ := getMockedController(t, ctrl)
err := c.gotoImplementation(ctx, nil, nil)
tests := []struct {
name string
setupMocks func(t *testing.T, c *controller, reg *MockRegistry)
expected []protocol.LocationLink
expectedErr error
}{
{
name: "no implementations",
setupMocks: func(t *testing.T, c *controller, reg *MockRegistry) {
positionMapper := docsyncmock.NewMockPositionMapper(ctrl)
positionMapper.EXPECT().MapCurrentPositionToBase(gomock.Any()).Return(protocol.Position{Line: 1, Character: 1}, false, nil)
documents := docsyncmock.NewMockController(ctrl)
documents.EXPECT().GetPositionMapper(gomock.Any(), gomock.Any()).Return(positionMapper, nil)
c.documents = documents

assert.NoError(t, err)
reg.EXPECT().Implementation(gomock.Any(), gomock.Any()).Return([]protocol.Location{}, nil)
},
expected: []protocol.LocationLink{},
},
{
name: "has error return",
setupMocks: func(t *testing.T, c *controller, reg *MockRegistry) {
positionMapper := docsyncmock.NewMockPositionMapper(ctrl)
positionMapper.EXPECT().MapCurrentPositionToBase(gomock.Any()).Return(protocol.Position{Line: 1, Character: 1}, false, nil)
documents := docsyncmock.NewMockController(ctrl)
documents.EXPECT().GetPositionMapper(gomock.Any(), gomock.Any()).Return(positionMapper, nil)
c.documents = documents

reg.EXPECT().Implementation(gomock.Any(), gomock.Any()).Return(nil, errors.New("test error"))
},
expected: []protocol.LocationLink{},
expectedErr: errors.New("test error"),
},
{
name: "normal return with origin selection",
setupMocks: func(t *testing.T, c *controller, reg *MockRegistry) {
positionMapper := docsyncmock.NewMockPositionMapper(ctrl)
positionMapper.EXPECT().MapCurrentPositionToBase(gomock.Any()).Return(protocol.Position{Line: 2, Character: 2}, false, nil)

positionMapper.EXPECT().MapBasePositionToCurrent(gomock.Any()).DoAndReturn(func(pos protocol.Position) (protocol.Position, error) { return pos, nil }).AnyTimes()
documents := docsyncmock.NewMockController(ctrl)
documents.EXPECT().GetPositionMapper(gomock.Any(), gomock.Any()).Return(positionMapper, nil).AnyTimes()
c.documents = documents

reg.EXPECT().Implementation(gomock.Any(), gomock.Any()).Return([]protocol.Location{
{
URI: uri.URI("file:///impl.go"),
Range: protocol.Range{
Start: protocol.Position{Line: 10, Character: 3},
End: protocol.Position{Line: 10, Character: 9},
},
},
}, nil)

reg.EXPECT().Hover(gomock.Any(), gomock.Any()).Return("", &model.Occurrence{Range: []int32{2, 2, 3}}, nil)
},
expected: []protocol.LocationLink{
{
TargetURI: uri.URI("file:///impl.go"),
TargetRange: protocol.Range{Start: protocol.Position{Line: 10, Character: 3}, End: protocol.Position{Line: 10, Character: 9}},
TargetSelectionRange: protocol.Range{Start: protocol.Position{Line: 10, Character: 3}, End: protocol.Position{Line: 10, Character: 9}},
OriginSelectionRange: &protocol.Range{Start: protocol.Position{Line: 2, Character: 2}, End: protocol.Position{Line: 2, Character: 3}},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c, reg := getMockedController(t, ctrl)
tt.setupMocks(t, &c, reg)

req := &protocol.ImplementationParams{TextDocumentPositionParams: getMockTextDocumentPositionParams()}
res := []protocol.LocationLink{}
err := c.gotoImplementation(ctx, req, &res)

if tt.expectedErr != nil {
assert.ErrorContains(t, err, tt.expectedErr.Error())
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expected, res)
}
})
}
}

func TestReferences(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions src/ulsp/controller/scip/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ type Registry interface {
Definition(uri uri.URI, loc protocol.Position) (*model.SymbolOccurrence, *model.SymbolOccurrence, error)
// References returns the locations a symbol is referenced at in the entire index
References(uri uri.URI, loc protocol.Position) ([]protocol.Location, error)
// Implementation returns the locations where a symbol is implemented
Implementation(uri uri.URI, loc protocol.Position) ([]protocol.Location, error)
// Hover returns the hover information for a given position, as well as it's occurrence
Hover(uri uri.URI, loc protocol.Position) (string, *model.Occurrence, error)
// DocumentSymbols returns the document symbols for a given document
Expand Down
Loading