diff --git a/cmd/frontend/graphqlbackend/codeintel.codenav.graphql b/cmd/frontend/graphqlbackend/codeintel.codenav.graphql index 1de7bbdd5976..223f7aebb091 100644 --- a/cmd/frontend/graphqlbackend/codeintel.codenav.graphql +++ b/cmd/frontend/graphqlbackend/codeintel.codenav.graphql @@ -47,6 +47,139 @@ extend type GitBlob { Experimental: This API is likely to change in the future. """ symbolInfo(line: Int!, character: Int!): SymbolInfo + + """ + Return the code graph data associated with this blob. + + If there are multiple tools (i.e. name and version pairs) which + have uploaded precise indexes for this blob, then this API will + return multiple results even if + filter == { provenance: { equals: Precise } }. + + Commit matching is done based on graph order. + For merge commits or their children, it is possible that the + same blob may have code graph data at different ancestors, + in which case this API will return multiple results. + + EXPERIMENTAL: This API may change in the future. + """ + codeGraphData(filter: CodeGraphDataFilter): [CodeGraphData!] +} + +""" +EXPERIMENTAL: This type may change in a backwards-incompatible way. +""" +input CodeGraphDataFilter { + """ + If this field is not set, then the codeGraphData API + will go through each provenance each provenance one by one + in the order Precise -> Syntactic -> SearchBased + and stop when some data is available. + """ + provenance: CodeGraphDataProvenanceComparator +} + +""" +EXPERIMENTAL: This type may change in a backwards-incompatible way. +""" +input CodeGraphDataProvenanceComparator { + equals: CodeGraphDataProvenance +} + +""" +EXPERIMENTAL: This type may change in a backwards-incompatible way. +""" +type CodeGraphData { + provenance: CodeGraphDataProvenance! + + """ + The commit associated with this code graph data. + + In general, this will be an ancestor of the commit at which code + graph data was requested, as code graph data may not be available + at the exact commit for the blob. + """ + commit: String! + + """ + Information about the tool which generated this code graph data + """ + toolInfo: CodeGraphToolInfo + + """ + Occurrences are guaranteed to be sorted by range. + """ + occurrences(first: Int, after: String): SCIPOccurrenceConnection +} + +type CodeGraphToolInfo { + name: String + version: String +} + +type SCIPOccurrenceConnection { + """ + A list of locations within a file. + """ + nodes: [SCIPOccurrence!]! + + """ + Pagination information. + """ + pageInfo: PageInfo! +} + +""" +EXPERIMENTAL: This type may change in a backwards-incompatible way. +""" +enum CodeGraphDataProvenance { + """ + Based on a compiler, a type-checker or a similar data source + which doesn't have false positives. + Generally, the results are specific to a particular build configuration, + such as for a specific OS or CPU, which can matter for + codebases having a large amount of platform-specific code. + """ + Precise, + """ + Based on a data source that uses an abstract or concrete syntax + tree, but without access to reliable type information. + """ + Syntactic, + """ + Based on a data source that only does textual analysis, say + using regular expressions. + """ + SearchBased, +} + +""" +EXPERIMENTAL: This type may change in a backwards-incompatible way. +""" +type SCIPOccurrence { + """ + Symbol name using syntax specified by the SCIP schema. + https://github.com/sourcegraph/scip/blob/main/scip.proto#L147-L188 + This value will generally be used in conjunction with + the `usagesBySymbol` API. + """ + symbol: String + range: Range! + roles: [SymbolRole!] + # We can add diagnostics etc. here in the future if needed. +} + +""" +EXPERIMENTAL: This type may change in a backwards-compatible way. +""" +enum SymbolRole { + Definition, + Reference, + """ + Applicable for forward declarations in languages with header files (C, C++ etc.) + as well as standalone signatures in languages with separate interface files (OCaml etc.). + """ + ForwardDefinition, } """ diff --git a/cmd/frontend/graphqlbackend/git_tree_entry.go b/cmd/frontend/graphqlbackend/git_tree_entry.go index 5a69eb1cd765..409e311a1471 100644 --- a/cmd/frontend/graphqlbackend/git_tree_entry.go +++ b/cmd/frontend/graphqlbackend/git_tree_entry.go @@ -459,6 +459,19 @@ func (r *GitTreeEntryResolver) LSIF(ctx context.Context, args *struct{ ToolName }) } +func (r *GitTreeEntryResolver) CodeGraphData(ctx context.Context, args *resolverstubs.CodeGraphDataArgs) (*[]resolverstubs.CodeGraphDataResolver, error) { + repo, err := r.commit.repoResolver.getRepo(ctx) + if err != nil { + return nil, err + } + return EnterpriseResolvers.codeIntelResolver.CodeGraphData(ctx, &resolverstubs.CodeGraphDataOpts{ + Args: args, + Repo: repo, + Commit: api.CommitID(r.Commit().OID()), + Path: r.Path(), + }) +} + func (r *GitTreeEntryResolver) LocalCodeIntel(ctx context.Context) (*JSONValue, error) { repo, err := r.commit.repoResolver.getRepo(ctx) if err != nil { diff --git a/internal/codeintel/codenav/service.go b/internal/codeintel/codenav/service.go index 4d14ddc870c7..c6f34a375b51 100644 --- a/internal/codeintel/codenav/service.go +++ b/internal/codeintel/codenav/service.go @@ -945,3 +945,7 @@ func (s *Service) SnapshotForDocument(ctx context.Context, repositoryID int, com return } + +func (s *Service) SCIPDocument(ctx context.Context, uploadID int, path string) (*scip.Document, error) { + return s.lsifstore.SCIPDocument(ctx, uploadID, path) +} diff --git a/internal/codeintel/codenav/transport/graphql/BUILD.bazel b/internal/codeintel/codenav/transport/graphql/BUILD.bazel index 90a1b6b3f42c..88da5fea9e1a 100644 --- a/internal/codeintel/codenav/transport/graphql/BUILD.bazel +++ b/internal/codeintel/codenav/transport/graphql/BUILD.bazel @@ -22,6 +22,7 @@ go_library( importpath = "github.com/sourcegraph/sourcegraph/internal/codeintel/codenav/transport/graphql", visibility = ["//:__subpackages__"], deps = [ + "//cmd/frontend/graphqlbackend/graphqlutil", "//internal/api", "//internal/authz", "//internal/codeintel/codenav", @@ -41,6 +42,8 @@ go_library( "@com_github_graph_gophers_graphql_go//:graphql-go", "@com_github_sourcegraph_go_lsp//:go-lsp", "@com_github_sourcegraph_log//:log", + "@com_github_sourcegraph_scip//bindings/go/scip", + "@com_github_wk8_go_ordered_map_v2//:go-ordered-map", "@io_opentelemetry_go_otel//attribute", ], ) diff --git a/internal/codeintel/codenav/transport/graphql/iface.go b/internal/codeintel/codenav/transport/graphql/iface.go index 6f2b08cb2743..df2734420c7f 100644 --- a/internal/codeintel/codenav/transport/graphql/iface.go +++ b/internal/codeintel/codenav/transport/graphql/iface.go @@ -3,6 +3,8 @@ package graphql import ( "context" + "github.com/sourcegraph/scip/bindings/go/scip" + "github.com/sourcegraph/sourcegraph/internal/codeintel/codenav" "github.com/sourcegraph/sourcegraph/internal/codeintel/codenav/shared" uploadsshared "github.com/sourcegraph/sourcegraph/internal/codeintel/uploads/shared" @@ -22,6 +24,7 @@ type CodeNavService interface { GetClosestCompletedUploadsForBlob(context.Context, uploadsshared.UploadMatchingOptions) (_ []uploadsshared.CompletedUpload, err error) VisibleUploadsForPath(ctx context.Context, requestState codenav.RequestState) ([]uploadsshared.CompletedUpload, error) SnapshotForDocument(ctx context.Context, repositoryID int, commit, path string, uploadID int) (data []shared.SnapshotData, err error) + SCIPDocument(ctx context.Context, uploadID int, path string) (*scip.Document, error) } type AutoIndexingService interface { diff --git a/internal/codeintel/codenav/transport/graphql/observability.go b/internal/codeintel/codenav/transport/graphql/observability.go index 80800e3320f3..3e9971cd0b0d 100644 --- a/internal/codeintel/codenav/transport/graphql/observability.go +++ b/internal/codeintel/codenav/transport/graphql/observability.go @@ -15,6 +15,8 @@ import ( type operations struct { gitBlobLsifData *observation.Operation + codeGraphData *observation.Operation + occurrences *observation.Operation hover *observation.Operation definitions *observation.Operation references *observation.Operation @@ -45,6 +47,8 @@ func newOperations(observationCtx *observation.Context) *operations { return &operations{ gitBlobLsifData: op("GitBlobLsifData"), + codeGraphData: op("CodeGraphData"), + occurrences: op("Occurrences"), hover: op("Hover"), definitions: op("Definitions"), references: op("References"), diff --git a/internal/codeintel/codenav/transport/graphql/root_resolver.go b/internal/codeintel/codenav/transport/graphql/root_resolver.go index 4d8f360d4713..89d82064467a 100644 --- a/internal/codeintel/codenav/transport/graphql/root_resolver.go +++ b/internal/codeintel/codenav/transport/graphql/root_resolver.go @@ -2,20 +2,31 @@ package graphql import ( "context" + "encoding/base64" + "encoding/json" + "fmt" "strings" + "sync" + orderedmap "github.com/wk8/go-ordered-map/v2" "go.opentelemetry.io/otel/attribute" + "github.com/sourcegraph/go-lsp" + "github.com/sourcegraph/scip/bindings/go/scip" + + "github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend/graphqlutil" "github.com/sourcegraph/sourcegraph/internal/authz" "github.com/sourcegraph/sourcegraph/internal/codeintel/codenav" resolverstubs "github.com/sourcegraph/sourcegraph/internal/codeintel/resolvers" sharedresolvers "github.com/sourcegraph/sourcegraph/internal/codeintel/shared/resolvers" "github.com/sourcegraph/sourcegraph/internal/codeintel/shared/resolvers/gitresolvers" + "github.com/sourcegraph/sourcegraph/internal/codeintel/uploads/shared" uploadsgraphql "github.com/sourcegraph/sourcegraph/internal/codeintel/uploads/transport/graphql" "github.com/sourcegraph/sourcegraph/internal/database" "github.com/sourcegraph/sourcegraph/internal/dotcom" "github.com/sourcegraph/sourcegraph/internal/gitserver" "github.com/sourcegraph/sourcegraph/internal/observation" + "github.com/sourcegraph/sourcegraph/lib/pointers" ) type rootResolver struct { @@ -111,6 +122,68 @@ func (r *rootResolver) GitBlobLSIFData(ctx context.Context, args *resolverstubs. ), nil } +func (r *rootResolver) CodeGraphData(ctx context.Context, opts *resolverstubs.CodeGraphDataOpts) (_ *[]resolverstubs.CodeGraphDataResolver, err error) { + ctx, _, endObservation := r.operations.codeGraphData.WithErrors(ctx, &err, observation.Args{Attrs: opts.Attrs()}) + endObservation.OnCancel(ctx, 1, observation.Args{}) + + makeResolvers := func(prov resolverstubs.CodeGraphDataProvenance) ([]resolverstubs.CodeGraphDataResolver, error) { + indexer := "" + if prov == resolverstubs.ProvenanceSyntactic { + indexer = shared.SyntacticIndexer + } + uploads, err := r.svc.GetClosestCompletedUploadsForBlob(ctx, shared.UploadMatchingOptions{ + RepositoryID: int(opts.Repo.ID), + Commit: string(opts.Commit), + Path: opts.Path, + RootToPathMatching: shared.RootMustEnclosePath, + Indexer: indexer, + }) + if err != nil || len(uploads) == 0 { + return nil, err + } + resolvers := []resolverstubs.CodeGraphDataResolver{} + for _, upload := range preferUploadsWithLongestRoots(uploads) { + resolvers = append(resolvers, newCodeGraphDataResolver(r.svc, upload, opts, prov, r.operations)) + } + return resolvers, nil + } + + provs := opts.Args.ProvenancesForSCIPData() + if provs.Precise { + preciseResolvers, err := makeResolvers(resolverstubs.ProvenancePrecise) + if len(preciseResolvers) != 0 || err != nil { + return &preciseResolvers, err + } + } + + if provs.Syntactic { + syntacticResolvers, err := makeResolvers(resolverstubs.ProvenanceSyntactic) + if len(syntacticResolvers) != 0 || err != nil { + return &syntacticResolvers, err + } + } + return &[]resolverstubs.CodeGraphDataResolver{}, nil +} + +func preferUploadsWithLongestRoots(uploads []shared.CompletedUpload) []shared.CompletedUpload { + sortedMap := orderedmap.New[string, shared.CompletedUpload]() + for _, upload := range uploads { + key := fmt.Sprintf("%s:%s", upload.Indexer, upload.Commit) + if val, found := sortedMap.Get(key); found { + if len(val.Root) < len(upload.Root) { + sortedMap.Set(key, upload) + } + } else { + sortedMap.Set(key, upload) + } + } + out := make([]shared.CompletedUpload, 0, sortedMap.Len()) + for pair := sortedMap.Oldest(); pair != nil; pair = pair.Next() { + out = append(out, pair.Value) + } + return out +} + // gitBlobLSIFDataResolver is the main interface to bundle-related operations exposed to the GraphQL API. This // resolver concerns itself with GraphQL/API-specific behaviors (auth, validation, marshaling, etc.). // All code intel-specific behavior is delegated to the underlying resolver instance, which is defined @@ -189,3 +262,193 @@ func (r *gitBlobLSIFDataResolver) VisibleIndexes(ctx context.Context) (_ *[]reso return &resolvers, nil } + +type codeGraphDataResolver struct { + // Retrieved data/state + retrievedDocument sync.Once + document *scip.Document + documentRetrievalError error + + // Arguments + svc CodeNavService + upload shared.CompletedUpload + opts *resolverstubs.CodeGraphDataOpts + provenance resolverstubs.CodeGraphDataProvenance + + // O11y + operations *operations +} + +func newCodeGraphDataResolver( + svc CodeNavService, + upload shared.CompletedUpload, + opts *resolverstubs.CodeGraphDataOpts, + provenance resolverstubs.CodeGraphDataProvenance, + operations *operations, +) resolverstubs.CodeGraphDataResolver { + return &codeGraphDataResolver{ + sync.Once{}, + /*document*/ nil, + /*documentRetrievalError*/ nil, + svc, + upload, + opts, + provenance, + operations, + } +} + +func (c *codeGraphDataResolver) tryRetrieveDocument(ctx context.Context) (*scip.Document, error) { + // NOTE(id: scip-doc-optimization): In the case of pagination, if we retrieve the document ID + // from the database, we can avoid performing a JOIN between codeintel_scip_document_lookup + // and codeintel_scip_documents + c.retrievedDocument.Do(func() { + c.document, c.documentRetrievalError = c.svc.SCIPDocument(ctx, c.upload.ID, c.opts.Path) + }) + return c.document, c.documentRetrievalError +} + +func (c *codeGraphDataResolver) Provenance(_ context.Context) (resolverstubs.CodeGraphDataProvenance, error) { + return c.provenance, nil +} + +func (c *codeGraphDataResolver) Commit(_ context.Context) (string, error) { + return c.upload.Commit, nil +} + +func (c *codeGraphDataResolver) ToolInfo(_ context.Context) (*resolverstubs.CodeGraphToolInfo, error) { + return &resolverstubs.CodeGraphToolInfo{Name_: &c.upload.Indexer, Version_: &c.upload.IndexerVersion}, nil +} + +func (c *codeGraphDataResolver) Occurrences(ctx context.Context, args *resolverstubs.OccurrencesArgs) (_ resolverstubs.SCIPOccurrenceConnectionResolver, err error) { + ctx, _, endObservation := c.operations.occurrences.WithErrors(ctx, &err, observation.Args{Attrs: c.opts.Attrs()}) + + defer endObservation(1, observation.Args{}) + + numOccurrences := args.First + const maxPageSize = 100000 + if numOccurrences == nil { + numOccurrences = pointers.Ptr(int32(maxPageSize)) + } + + impl, err := graphqlutil.NewConnectionResolver[resolverstubs.SCIPOccurrenceResolver]( + &occurrenceConnectionStore{c}, + &graphqlutil.ConnectionResolverArgs{First: numOccurrences, After: args.After}, + &graphqlutil.ConnectionResolverOptions{MaxPageSize: maxPageSize, Reverse: pointers.Ptr(false)}) + if err != nil { + return nil, err + } + return &occurrenceConnectionResolver{impl, c, args}, nil +} + +type occurrenceConnectionResolver struct { + impl *graphqlutil.ConnectionResolver[resolverstubs.SCIPOccurrenceResolver] + + // Arguments + graphData *codeGraphDataResolver + args *resolverstubs.OccurrencesArgs +} + +var _ resolverstubs.SCIPOccurrenceConnectionResolver = &occurrenceConnectionResolver{} + +func (o *occurrenceConnectionResolver) Nodes(ctx context.Context) ([]resolverstubs.SCIPOccurrenceResolver, error) { + return o.impl.Nodes(ctx) +} + +func (o *occurrenceConnectionResolver) PageInfo(ctx context.Context) (*graphqlutil.ConnectionPageInfo[resolverstubs.SCIPOccurrenceResolver], error) { + return o.impl.PageInfo(ctx) +} + +var _ graphqlutil.ConnectionResolverStore[resolverstubs.SCIPOccurrenceResolver] = &occurrenceConnectionStore{} + +type scipOccurrence struct { + impl *scip.Occurrence + + // For cursor state, because a single value is passed to MarshalCursor + cursor +} + +func (s scipOccurrence) Roles() (*[]resolverstubs.SymbolRole, error) { + roles := s.impl.GetSymbolRoles() + out := []resolverstubs.SymbolRole{} + if roles&int32(scip.SymbolRole_Definition) != 0 { + out = append(out, resolverstubs.SymbolRoleDefinition) + } else { + out = append(out, resolverstubs.SymbolRoleReference) + } + if roles&int32(scip.SymbolRole_ForwardDefinition) != 0 { + out = append(out, resolverstubs.SymbolRoleForwardDefinition) + } + return &out, nil +} + +var _ resolverstubs.SCIPOccurrenceResolver = scipOccurrence{} + +type occurrenceConnectionStore struct { + graphData *codeGraphDataResolver +} + +var _ graphqlutil.ConnectionResolverStore[resolverstubs.SCIPOccurrenceResolver] = &occurrenceConnectionStore{} + +func (o *occurrenceConnectionStore) ComputeTotal(ctx context.Context) (int32, error) { + doc, err := o.graphData.tryRetrieveDocument(ctx) + if doc == nil || err != nil { + return 0, err + } + return int32(len(doc.Occurrences)), nil +} + +func (o *occurrenceConnectionStore) ComputeNodes(ctx context.Context, paginationArgs *database.PaginationArgs) ([]resolverstubs.SCIPOccurrenceResolver, error) { + doc, err := o.graphData.tryRetrieveDocument(ctx) + if err != nil { + return nil, err + } + occs, _, err2 := database.OffsetBasedCursorSlice(doc.Occurrences, paginationArgs) + if err2 != nil { + return nil, err2 + } + + out := make([]resolverstubs.SCIPOccurrenceResolver, 0, len(occs)) + for idx, occ := range occs { + out = append(out, scipOccurrence{occ, cursor{idx}}) + } + return out, nil +} + +func (o *occurrenceConnectionStore) MarshalCursor(n resolverstubs.SCIPOccurrenceResolver, _ database.OrderBy) (*string, error) { + buf, err := json.Marshal(n.(scipOccurrence).cursor) + if err != nil { + return nil, err + } + return pointers.Ptr(base64.StdEncoding.EncodeToString(buf)), nil +} + +func (o *occurrenceConnectionStore) UnmarshalCursor(s string, _ database.OrderBy) ([]any, error) { + buf, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return nil, err + } + var c cursor + if err = json.Unmarshal(buf, &c); err != nil { + return nil, err + } + return []any{c}, nil +} + +type cursor struct { + // Index inside occurrences array in document + Index int +} + +func (s scipOccurrence) Symbol() (*string, error) { + return pointers.Ptr(s.impl.Symbol), nil +} + +func (s scipOccurrence) Range() (resolverstubs.RangeResolver, error) { + // FIXME(issue: GRAPH-571): Below code is correct iff the indexer uses UTF-16 offsets + r := scip.NewRange(s.impl.Range) + return newRangeResolver(lsp.Range{ + Start: lsp.Position{Line: int(r.Start.Line), Character: int(r.Start.Character)}, + End: lsp.Position{Line: int(r.End.Line), Character: int(r.End.Character)}, + }), nil +} diff --git a/internal/codeintel/resolvers/BUILD.bazel b/internal/codeintel/resolvers/BUILD.bazel index ca165e1a6561..13a800b89ee9 100644 --- a/internal/codeintel/resolvers/BUILD.bazel +++ b/internal/codeintel/resolvers/BUILD.bazel @@ -15,6 +15,7 @@ go_library( importpath = "github.com/sourcegraph/sourcegraph/internal/codeintel/resolvers", visibility = ["//:__subpackages__"], deps = [ + "//cmd/frontend/graphqlbackend/graphqlutil", "//internal/api", "//internal/codeintel/uploads/shared", "//internal/gitserver/gitdomain", @@ -25,5 +26,6 @@ go_library( "//lib/pointers", "@com_github_graph_gophers_graphql_go//:graphql-go", "@com_github_graph_gophers_graphql_go//relay", + "@io_opentelemetry_go_otel//attribute", ], ) diff --git a/internal/codeintel/resolvers/codenav.go b/internal/codeintel/resolvers/codenav.go index 0e33e8a08dff..420168a220b2 100644 --- a/internal/codeintel/resolvers/codenav.go +++ b/internal/codeintel/resolvers/codenav.go @@ -2,9 +2,12 @@ package resolvers import ( "context" + "fmt" "github.com/graph-gophers/graphql-go" + "go.opentelemetry.io/otel/attribute" + "github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend/graphqlutil" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/codeintel/uploads/shared" "github.com/sourcegraph/sourcegraph/internal/markdown" @@ -13,6 +16,11 @@ import ( type CodeNavServiceResolver interface { GitBlobLSIFData(ctx context.Context, args *GitBlobLSIFDataArgs) (GitBlobLSIFDataResolver, error) + // CodeGraphData is a newer API that is more SCIP-oriented. + // The second parameter is called 'opts' and not 'args' to reflect + // that it is not what is exactly provided as input from the GraphQL + // client. + CodeGraphData(ctx context.Context, opts *CodeGraphDataOpts) (*[]CodeGraphDataResolver, error) } type GitBlobLSIFDataArgs struct { @@ -140,3 +148,117 @@ type DiagnosticResolver interface { Message() (*string, error) Location(ctx context.Context) (LocationResolver, error) } + +type CodeGraphDataResolver interface { + Provenance(ctx context.Context) (CodeGraphDataProvenance, error) + Commit(ctx context.Context) (string, error) + ToolInfo(ctx context.Context) (*CodeGraphToolInfo, error) + Occurrences(ctx context.Context, args *OccurrencesArgs) (SCIPOccurrenceConnectionResolver, error) +} + +type CodeGraphDataProvenance string + +const ( + ProvenancePrecise CodeGraphDataProvenance = "Precise" + ProvenanceSyntactic CodeGraphDataProvenance = "Syntactic" + ProvenanceSearchBased CodeGraphDataProvenance = "Search-based" +) + +type CodeGraphDataProvenanceComparator struct { + Equals *CodeGraphDataProvenance +} + +type CodeGraphDataFilter struct { + Provenance *CodeGraphDataProvenanceComparator +} + +// String is meant as a debugging-only representation without round-trippability +func (f *CodeGraphDataFilter) String() string { + if f != nil && f.Provenance != nil && f.Provenance.Equals != nil { + return fmt.Sprintf("provenance == %s", string(*f.Provenance.Equals)) + } + return "" +} + +type CodeGraphDataArgs struct { + Filter *CodeGraphDataFilter +} + +func (args *CodeGraphDataArgs) Attrs() []attribute.KeyValue { + return []attribute.KeyValue{attribute.String("args.filter", args.Filter.String())} +} + +type ForEachProvenance[T any] struct { + SearchBased T + Syntactic T + Precise T +} + +func (a *CodeGraphDataArgs) ProvenancesForSCIPData() ForEachProvenance[bool] { + var out ForEachProvenance[bool] + if a == nil || a.Filter == nil || a.Filter.Provenance == nil || a.Filter.Provenance.Equals == nil { + out.Syntactic = true + out.Precise = true + } else { + p := *a.Filter.Provenance.Equals + switch p { + case ProvenancePrecise: + out.Precise = true + case ProvenanceSyntactic: + out.Syntactic = true + case ProvenanceSearchBased: + } + } + return out +} + +type CodeGraphDataOpts struct { + Args *CodeGraphDataArgs + Repo *types.Repo + Commit api.CommitID + Path string +} + +func (opts *CodeGraphDataOpts) Attrs() []attribute.KeyValue { + return append([]attribute.KeyValue{attribute.String("repo", opts.Repo.String()), + opts.Commit.Attr(), + attribute.String("path", opts.Path)}, opts.Args.Attrs()...) +} + +type CodeGraphToolInfo struct { + Name_ *string + Version_ *string +} + +func (ti *CodeGraphToolInfo) Name() *string { + return ti.Name_ +} + +func (ti *CodeGraphToolInfo) Version() *string { + return ti.Version_ +} + +type OccurrencesArgs struct { + First *int32 + After *string +} + +type SCIPOccurrenceConnectionResolver interface { + ConnectionResolver[SCIPOccurrenceResolver] + PageInfo(ctx context.Context) (*graphqlutil.ConnectionPageInfo[SCIPOccurrenceResolver], error) +} + +type SCIPOccurrenceResolver interface { + Symbol() (*string, error) + Range() (RangeResolver, error) + Roles() (*[]SymbolRole, error) +} + +type SymbolRole string + +// ⚠️ CAUTION: These constants are part of the public GraphQL API +const ( + SymbolRoleDefinition SymbolRole = "Definition" + SymbolRoleReference SymbolRole = "Reference" + SymbolRoleForwardDefinition SymbolRole = "ForwardDefinition" +) diff --git a/internal/codeintel/resolvers/root_resolver.go b/internal/codeintel/resolvers/root_resolver.go index a4d1a0375310..f1255efca8e8 100644 --- a/internal/codeintel/resolvers/root_resolver.go +++ b/internal/codeintel/resolvers/root_resolver.go @@ -125,6 +125,10 @@ func (r *Resolver) GitBlobLSIFData(ctx context.Context, args *GitBlobLSIFDataArg return r.codenavResolver.GitBlobLSIFData(ctx, args) } +func (r *Resolver) CodeGraphData(ctx context.Context, opts *CodeGraphDataOpts) (*[]CodeGraphDataResolver, error) { + return r.codenavResolver.CodeGraphData(ctx, opts) +} + func (r *Resolver) ConfigurationPolicyByID(ctx context.Context, id graphql.ID) (_ CodeIntelligenceConfigurationPolicyResolver, err error) { return r.policiesRootResolver.ConfigurationPolicyByID(ctx, id) }