Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 6701ff9

Browse files
committed
implement SurroundingContext
1 parent 5d8286b commit 6701ff9

File tree

5 files changed

+131
-21
lines changed

5 files changed

+131
-21
lines changed

cmd/frontend/graphqlbackend/codeintel.codenav.graphql

+1-1
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ type Usage {
733733
or exceeds the number of available lines, the value is interpreted as
734734
until the start/end of the file.
735735
"""
736-
surroundingContent(surroundingLines: SurroundingLines = { linesBefore: 0, linesAfter: 0 }): String
736+
surroundingContent(surroundingLines: SurroundingLines = { linesBefore: 0, linesAfter: 0 }): String!
737737

738738
"""
739739
Describes the relationship between this usage and the input symbol

internal/codeintel/codenav/transport/graphql/root_resolver.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import (
2828
"github.com/sourcegraph/sourcegraph/lib/errors"
2929
)
3030

31+
var ErrNotEnabled = errors.New("experimentalFeatures.scipBasedAPIs is not enabled")
32+
3133
type rootResolver struct {
3234
svc CodeNavService
3335
autoindexingSvc AutoIndexingService
@@ -118,7 +120,7 @@ func (r *rootResolver) CodeGraphData(ctx context.Context, opts *resolverstubs.Co
118120
endObservation.OnCancel(ctx, 1, observation.Args{})
119121

120122
if !conf.SCIPBasedAPIsEnabled() {
121-
return nil, nil
123+
return nil, ErrNotEnabled
122124
}
123125

124126
makeResolvers := func(prov resolverstubs.CodeGraphDataProvenance) ([]resolverstubs.CodeGraphDataResolver, error) {
@@ -198,7 +200,7 @@ func (r *rootResolver) UsagesForSymbol(ctx context.Context, unresolvedArgs *reso
198200
ctx, _, endObservation := r.operations.usagesForSymbol.WithErrors(ctx, &err, observation.Args{Attrs: unresolvedArgs.Attrs()})
199201

200202
if !conf.SCIPBasedAPIsEnabled() {
201-
return nil, nil
203+
return nil, ErrNotEnabled
202204
}
203205

204206
numPreciseResults := 0
@@ -219,6 +221,7 @@ func (r *rootResolver) UsagesForSymbol(ctx context.Context, unresolvedArgs *reso
219221
}
220222
remainingCount := int(args.RemainingCount)
221223
provsForSCIPData := args.Symbol.ProvenancesForSCIPData()
224+
linesGetter := newCachedLinesGetter(r.gitserverClient, 5*1024*1024 /* 5MB */)
222225
usageResolvers := []resolverstubs.UsageResolver{}
223226

224227
if provsForSCIPData.Precise {
@@ -248,7 +251,7 @@ func (r *rootResolver) UsagesForSymbol(ctx context.Context, unresolvedArgs *reso
248251
}
249252
} else {
250253
for _, result := range syntacticResult.Matches {
251-
usageResolvers = append(usageResolvers, NewSyntacticUsageResolver(result, args.Repo, args.CommitID))
254+
usageResolvers = append(usageResolvers, NewSyntacticUsageResolver(result, args.Repo, args.CommitID, linesGetter))
252255
}
253256
numSyntacticResults = len(syntacticResult.Matches)
254257
remainingCount = remainingCount - numSyntacticResults
@@ -268,7 +271,7 @@ func (r *rootResolver) UsagesForSymbol(ctx context.Context, unresolvedArgs *reso
268271
}
269272
} else {
270273
for _, result := range results {
271-
usageResolvers = append(usageResolvers, NewSearchBasedUsageResolver(result, args.Repo, args.CommitID))
274+
usageResolvers = append(usageResolvers, NewSearchBasedUsageResolver(result, args.Repo, args.CommitID, linesGetter))
272275
}
273276
}
274277
}

internal/codeintel/codenav/transport/graphql/root_resolver_usages.go

+28-15
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,16 @@ func (u *usageConnectionResolver) PageInfo() resolverstubs.PageInfo {
2929
}
3030

3131
type usageResolver struct {
32-
symbol *symbolInformationResolver
33-
provenance resolverstubs.CodeGraphDataProvenance
34-
kind resolverstubs.SymbolUsageKind
35-
usageRange resolverstubs.UsageRangeResolver
32+
symbol *symbolInformationResolver
33+
provenance resolverstubs.CodeGraphDataProvenance
34+
kind resolverstubs.SymbolUsageKind
35+
linesGetter LinesGetter
36+
usageRange *usageRangeResolver
3637
}
3738

3839
var _ resolverstubs.UsageResolver = &usageResolver{}
3940

40-
func NewSyntacticUsageResolver(usage codenav.SyntacticMatch, repository types.Repo, revision api.CommitID) resolverstubs.UsageResolver {
41+
func NewSyntacticUsageResolver(usage codenav.SyntacticMatch, repository types.Repo, revision api.CommitID, linesGetter LinesGetter) resolverstubs.UsageResolver {
4142
var kind resolverstubs.SymbolUsageKind
4243
if scip.SymbolRole_Definition.Matches(usage.Occurrence) {
4344
kind = resolverstubs.UsageKindDefinition
@@ -48,8 +49,9 @@ func NewSyntacticUsageResolver(usage codenav.SyntacticMatch, repository types.Re
4849
symbol: &symbolInformationResolver{
4950
name: usage.Occurrence.Symbol,
5051
},
51-
provenance: resolverstubs.ProvenanceSyntactic,
52-
kind: kind,
52+
provenance: resolverstubs.ProvenanceSyntactic,
53+
kind: kind,
54+
linesGetter: linesGetter,
5355
usageRange: &usageRangeResolver{
5456
repository: repository,
5557
revision: revision,
@@ -58,17 +60,18 @@ func NewSyntacticUsageResolver(usage codenav.SyntacticMatch, repository types.Re
5860
},
5961
}
6062
}
61-
func NewSearchBasedUsageResolver(usage codenav.SearchBasedMatch, repository types.Repo, revision api.CommitID) resolverstubs.UsageResolver {
63+
func NewSearchBasedUsageResolver(usage codenav.SearchBasedMatch, repository types.Repo, revision api.CommitID, linesGetter LinesGetter) resolverstubs.UsageResolver {
6264
var kind resolverstubs.SymbolUsageKind
6365
if usage.IsDefinition {
6466
kind = resolverstubs.UsageKindDefinition
6567
} else {
6668
kind = resolverstubs.UsageKindReference
6769
}
6870
return &usageResolver{
69-
symbol: nil,
70-
provenance: resolverstubs.ProvenanceSearchBased,
71-
kind: kind,
71+
symbol: nil,
72+
provenance: resolverstubs.ProvenanceSearchBased,
73+
kind: kind,
74+
linesGetter: linesGetter,
7275
usageRange: &usageRangeResolver{
7376
repository: repository,
7477
revision: revision,
@@ -100,11 +103,21 @@ func (u *usageResolver) UsageRange(ctx context.Context) (resolverstubs.UsageRang
100103
return u.usageRange, nil
101104
}
102105

103-
func (u *usageResolver) SurroundingContent(_ context.Context, args *struct {
106+
func (u *usageResolver) SurroundingContent(ctx context.Context, args *struct {
104107
*resolverstubs.SurroundingLines `json:"surroundingLines"`
105-
}) (*string, error) {
106-
//TODO implement me
107-
panic("implement me")
108+
}) (string, error) {
109+
lines, err := u.linesGetter.Get(
110+
ctx,
111+
u.usageRange.repository.Name,
112+
u.usageRange.revision,
113+
u.usageRange.path.RawValue(),
114+
int(u.usageRange.range_.Start.Line-*args.LinesBefore),
115+
int(u.usageRange.range_.End.Line+*args.LinesAfter+1),
116+
)
117+
if err != nil {
118+
return "", err
119+
}
120+
return string(lines), nil
108121
}
109122

110123
func (u *usageResolver) UsageKind() resolverstubs.SymbolUsageKind {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package graphql
2+
3+
import (
4+
"context"
5+
"io"
6+
"sync"
7+
8+
"github.com/sourcegraph/sourcegraph/internal/api"
9+
"github.com/sourcegraph/sourcegraph/internal/byteutils"
10+
"github.com/sourcegraph/sourcegraph/internal/gitserver"
11+
)
12+
13+
type LinesGetter interface {
14+
Get(ctx context.Context, repo api.RepoName, commit api.CommitID, path string, startLine, endLine int) ([]byte, error)
15+
}
16+
17+
type cacheKey struct {
18+
repo api.RepoName
19+
revision api.CommitID
20+
path string
21+
}
22+
23+
type cacheValue struct {
24+
contents []byte
25+
index byteutils.LineIndex
26+
}
27+
28+
type cachedLinesGetter struct {
29+
mu sync.RWMutex
30+
cache map[cacheKey]cacheValue
31+
maxCachedBytes int
32+
freeBytes int
33+
gitserver gitserver.Client
34+
}
35+
36+
var _ LinesGetter = (*cachedLinesGetter)(nil)
37+
38+
func newCachedLinesGetter(gitserver gitserver.Client, size int) *cachedLinesGetter {
39+
return &cachedLinesGetter{
40+
cache: make(map[cacheKey]cacheValue),
41+
maxCachedBytes: size,
42+
freeBytes: size,
43+
gitserver: gitserver,
44+
}
45+
}
46+
47+
func (c *cachedLinesGetter) Get(ctx context.Context, repo api.RepoName, commit api.CommitID, path string, startLine, endLine int) ([]byte, error) {
48+
key := cacheKey{repo, commit, path}
49+
50+
c.mu.RLock()
51+
if value, ok := c.cache[key]; ok {
52+
c.mu.RUnlock()
53+
start, end := value.index.LinesRange(startLine, endLine)
54+
return value.contents[start:end], nil
55+
}
56+
c.mu.RUnlock()
57+
58+
r, err := c.gitserver.NewFileReader(ctx, repo, commit, path)
59+
if err != nil {
60+
return nil, err
61+
}
62+
defer r.Close()
63+
64+
contents, err := io.ReadAll(r)
65+
if err != nil {
66+
return nil, err
67+
}
68+
index := byteutils.NewLineIndex(contents)
69+
start, end := index.LinesRange(startLine, endLine)
70+
lines := contents[start:end]
71+
72+
if len(contents) > c.maxCachedBytes {
73+
// Don't both trying to fit it in the cache
74+
return lines, nil
75+
}
76+
77+
c.mu.Lock()
78+
defer c.mu.Unlock()
79+
80+
// Make room for the file in the cache. This cache doesn't need to be high
81+
// performance -- just randomly delete things until we have room.
82+
for k, v := range c.cache {
83+
if c.freeBytes >= len(contents) {
84+
break
85+
}
86+
delete(c.cache, k)
87+
c.freeBytes += len(v.contents)
88+
}
89+
90+
c.cache[key] = cacheValue{contents, index}
91+
c.freeBytes -= len(contents)
92+
93+
return lines, nil
94+
}

internal/codeintel/resolvers/codenav.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ type UsageResolver interface {
622622
UsageRange(context.Context) (UsageRangeResolver, error)
623623
SurroundingContent(_ context.Context, args *struct {
624624
*SurroundingLines `json:"surroundingLines"`
625-
}) (*string, error)
625+
}) (string, error)
626626
UsageKind() SymbolUsageKind
627627
}
628628

0 commit comments

Comments
 (0)