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

Usages API: implement surroundingContent #63730

Merged
merged 2 commits into from
Jul 11, 2024
Merged
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
2 changes: 1 addition & 1 deletion cmd/frontend/graphqlbackend/codeintel.codenav.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ type Usage {
or exceeds the number of available lines, the value is interpreted as
until the start/end of the file.
"""
surroundingContent(surroundingLines: SurroundingLines = { linesBefore: 0, linesAfter: 0 }): String
surroundingContent(surroundingLines: SurroundingLines = { linesBefore: 0, linesAfter: 0 }): String!

"""
Describes the relationship between this usage and the input symbol
Expand Down
2 changes: 2 additions & 0 deletions internal/codeintel/codenav/transport/graphql/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ go_library(
"root_resolver_stencil.go",
"root_resolver_usages.go",
"util_cursor.go",
"util_lines.go",
"util_locations.go",
],
importpath = "github.com/sourcegraph/sourcegraph/internal/codeintel/codenav/transport/graphql",
Expand All @@ -28,6 +29,7 @@ go_library(
"//cmd/frontend/graphqlbackend/graphqlutil",
"//internal/api",
"//internal/authz",
"//internal/byteutils",
"//internal/codeintel/codenav",
"//internal/codeintel/codenav/shared",
"//internal/codeintel/core",
Expand Down
11 changes: 7 additions & 4 deletions internal/codeintel/codenav/transport/graphql/root_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"github.com/sourcegraph/sourcegraph/lib/errors"
)

var ErrNotEnabled = errors.New("experimentalFeatures.scipBasedAPIs is not enabled")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated, but it's most useful to me as a consumer to get a hard error if the API is disabled. Otherwise, I have no way of distinguishing "disabled" from "no results"


type rootResolver struct {
svc CodeNavService
autoindexingSvc AutoIndexingService
Expand Down Expand Up @@ -118,7 +120,7 @@ func (r *rootResolver) CodeGraphData(ctx context.Context, opts *resolverstubs.Co
endObservation.OnCancel(ctx, 1, observation.Args{})

if !conf.SCIPBasedAPIsEnabled() {
return nil, nil
return nil, ErrNotEnabled
}

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

if !conf.SCIPBasedAPIsEnabled() {
return nil, nil
return nil, ErrNotEnabled
}

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

if provsForSCIPData.Precise {
Expand Down Expand Up @@ -248,7 +251,7 @@ func (r *rootResolver) UsagesForSymbol(ctx context.Context, unresolvedArgs *reso
}
} else {
for _, result := range syntacticResult.Matches {
usageResolvers = append(usageResolvers, NewSyntacticUsageResolver(result, args.Repo, args.CommitID))
usageResolvers = append(usageResolvers, NewSyntacticUsageResolver(result, args.Repo, args.CommitID, linesGetter))
}
numSyntacticResults = len(syntacticResult.Matches)
remainingCount = remainingCount - numSyntacticResults
Expand All @@ -268,7 +271,7 @@ func (r *rootResolver) UsagesForSymbol(ctx context.Context, unresolvedArgs *reso
}
} else {
for _, result := range results {
usageResolvers = append(usageResolvers, NewSearchBasedUsageResolver(result, args.Repo, args.CommitID))
usageResolvers = append(usageResolvers, NewSearchBasedUsageResolver(result, args.Repo, args.CommitID, linesGetter))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,16 @@ func (u *usageConnectionResolver) PageInfo() resolverstubs.PageInfo {
}

type usageResolver struct {
symbol *symbolInformationResolver
provenance resolverstubs.CodeGraphDataProvenance
kind resolverstubs.SymbolUsageKind
usageRange resolverstubs.UsageRangeResolver
symbol *symbolInformationResolver
provenance resolverstubs.CodeGraphDataProvenance
kind resolverstubs.SymbolUsageKind
linesGetter LinesGetter
usageRange *usageRangeResolver
}

var _ resolverstubs.UsageResolver = &usageResolver{}

func NewSyntacticUsageResolver(usage codenav.SyntacticMatch, repository types.Repo, revision api.CommitID) resolverstubs.UsageResolver {
func NewSyntacticUsageResolver(usage codenav.SyntacticMatch, repository types.Repo, revision api.CommitID, linesGetter LinesGetter) resolverstubs.UsageResolver {
var kind resolverstubs.SymbolUsageKind
if scip.SymbolRole_Definition.Matches(usage.Occurrence) {
kind = resolverstubs.UsageKindDefinition
Expand All @@ -48,8 +49,9 @@ func NewSyntacticUsageResolver(usage codenav.SyntacticMatch, repository types.Re
symbol: &symbolInformationResolver{
name: usage.Occurrence.Symbol,
},
provenance: resolverstubs.ProvenanceSyntactic,
kind: kind,
provenance: resolverstubs.ProvenanceSyntactic,
kind: kind,
linesGetter: linesGetter,
usageRange: &usageRangeResolver{
repository: repository,
revision: revision,
Expand All @@ -58,17 +60,18 @@ func NewSyntacticUsageResolver(usage codenav.SyntacticMatch, repository types.Re
},
}
}
func NewSearchBasedUsageResolver(usage codenav.SearchBasedMatch, repository types.Repo, revision api.CommitID) resolverstubs.UsageResolver {
func NewSearchBasedUsageResolver(usage codenav.SearchBasedMatch, repository types.Repo, revision api.CommitID, linesGetter LinesGetter) resolverstubs.UsageResolver {
var kind resolverstubs.SymbolUsageKind
if usage.IsDefinition {
kind = resolverstubs.UsageKindDefinition
} else {
kind = resolverstubs.UsageKindReference
}
return &usageResolver{
symbol: nil,
provenance: resolverstubs.ProvenanceSearchBased,
kind: kind,
symbol: nil,
provenance: resolverstubs.ProvenanceSearchBased,
kind: kind,
linesGetter: linesGetter,
usageRange: &usageRangeResolver{
repository: repository,
revision: revision,
Expand Down Expand Up @@ -100,11 +103,21 @@ func (u *usageResolver) UsageRange(ctx context.Context) (resolverstubs.UsageRang
return u.usageRange, nil
}

func (u *usageResolver) SurroundingContent(_ context.Context, args *struct {
func (u *usageResolver) SurroundingContent(ctx context.Context, args *struct {
*resolverstubs.SurroundingLines `json:"surroundingLines"`
}) (*string, error) {
//TODO implement me
panic("implement me")
}) (string, error) {
lines, err := u.linesGetter.Get(
ctx,
u.usageRange.repository.Name,
u.usageRange.revision,
u.usageRange.path.RawValue(),
int(u.usageRange.range_.Start.Line-*args.LinesBefore),
int(u.usageRange.range_.End.Line+*args.LinesAfter+1),
)
if err != nil {
return "", err
}
return string(lines), nil
}

func (u *usageResolver) UsageKind() resolverstubs.SymbolUsageKind {
Expand Down
94 changes: 94 additions & 0 deletions internal/codeintel/codenav/transport/graphql/util_lines.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package graphql

import (
"context"
"io"
"sync"

"github.com/sourcegraph/sourcegraph/internal/api"
"github.com/sourcegraph/sourcegraph/internal/byteutils"
"github.com/sourcegraph/sourcegraph/internal/gitserver"
)

type LinesGetter interface {
Get(ctx context.Context, repo api.RepoName, commit api.CommitID, path string, startLine, endLine int) ([]byte, error)
}

type cacheKey struct {
repo api.RepoName
revision api.CommitID
path string
}

type cacheValue struct {
contents []byte
index byteutils.LineIndex
}

type cachedLinesGetter struct {
mu sync.RWMutex
cache map[cacheKey]cacheValue
maxCachedBytes int
freeBytes int
gitserver gitserver.Client
}

var _ LinesGetter = (*cachedLinesGetter)(nil)

func newCachedLinesGetter(gitserver gitserver.Client, size int) *cachedLinesGetter {
return &cachedLinesGetter{
cache: make(map[cacheKey]cacheValue),
maxCachedBytes: size,
freeBytes: size,
gitserver: gitserver,
}
}

func (c *cachedLinesGetter) Get(ctx context.Context, repo api.RepoName, commit api.CommitID, path string, startLine, endLine int) ([]byte, error) {
key := cacheKey{repo, commit, path}

c.mu.RLock()
if value, ok := c.cache[key]; ok {
c.mu.RUnlock()
start, end := value.index.LinesRange(startLine, endLine)
return value.contents[start:end], nil
}
c.mu.RUnlock()

r, err := c.gitserver.NewFileReader(ctx, repo, commit, path)
if err != nil {
return nil, err
}
defer r.Close()

contents, err := io.ReadAll(r)
if err != nil {
return nil, err
}
index := byteutils.NewLineIndex(contents)
start, end := index.LinesRange(startLine, endLine)
lines := contents[start:end]

if len(contents) > c.maxCachedBytes {
// Don't both trying to fit it in the cache
return lines, nil
}

c.mu.Lock()
defer c.mu.Unlock()

// Make room for the file in the cache. This cache doesn't need to be high
// performance -- just randomly delete things until we have room.
for k, v := range c.cache {
if c.freeBytes >= len(contents) {
break
}
delete(c.cache, k)
c.freeBytes += len(v.contents)
}

c.cache[key] = cacheValue{contents, index}
c.freeBytes -= len(contents)

return lines, nil
}
2 changes: 1 addition & 1 deletion internal/codeintel/resolvers/codenav.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ type UsageResolver interface {
UsageRange(context.Context) (UsageRangeResolver, error)
SurroundingContent(_ context.Context, args *struct {
*SurroundingLines `json:"surroundingLines"`
}) (*string, error)
}) (string, error)
UsageKind() SymbolUsageKind
}

Expand Down
Loading