diff --git a/cmd/frontend/graphqlbackend/codeintel.codenav.graphql b/cmd/frontend/graphqlbackend/codeintel.codenav.graphql index 1de7bbdd5976..38440807d4b3 100644 --- a/cmd/frontend/graphqlbackend/codeintel.codenav.graphql +++ b/cmd/frontend/graphqlbackend/codeintel.codenav.graphql @@ -47,6 +47,161 @@ 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 } }. + """ + codeGraphData(filter: CodeGraphDataFilter): [CodeGraphData!] +} + +input CodeGraphDataFilter { + """ + If provenance is not provided, then the API will go through + each provenance one by one in the order Precise -> Syntactic -> SearchBased + and stop when some data is available. + """ + provenance: CodeGraphDataProvenanceComparator + # EXTENSION: We can add filters for tool name (if there are overlay-style + # indexers) and build identifier (e.g. Linux vs Windows) here. +} + +""" +Invariants: +1. forall sym in symbols. + (sym.provenance, sym.dataSource) == (scipDocument.provenance, scipDocument.dataSource) +""" +# Implementation notes: +# 1. For precise code nav, we already have a code for inferring +# 'visible uploads' using the commit graph machinery -> infer best upload +# -> get document for that using the given path. +# 2. Syntactic will be similar to precise +# 3. Search-based -> return empty document/lists. +# +# Ref panel logic: +# 1. Request code graph data, specifying 'filter' if needed. +# Either the scipDocument or occurrences is fine. +# 2. When a hover is triggered, attempt to look up occurrence in document or list +# of occurrences (or some other lookup structure constructed from those) +# based on the source range. +# 3a. If one matching occurrence is found, use SCIPOccurrence.symbol and range to call +# usagesForSymbol. +# 3b. If zero matching occurrences are found, use only range to call usagesForSymbol. +# 3c. If more than one matching occurrence is found, use some more complex logic +# to offer a symbol picker. +# - Problem: For a symbol picker, ideally you'd use SymbolInformation.display_name +# in SCIP. However, display_name is not supported today by most SCIP indexers. +# And we don't have any API for getting the detailed SCIP SymbolInformation +# for a bunch of occurrences. +type CodeGraphData { + scipDocument: AssociatedSCIPDocument + + """ + 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 + + """ + List of symbols with definitions in this document. + + Invariant: + forall sym in symbols. + exists occ in occurrences such that + sym.name == occ.symbol && occ.roles.contains(Definition) + + These map 1-1 with the symbols in scipDocument.data + """ + symbols: SymbolInformationConnection + + """ + Occurrences are guaranteed to be sorted by range. + """ + occurrences(filter: SCIPOccurrencesFilter): SCIPOccurrenceConnection + + # EXTENSION: We can add a build identifier here to return different + # Documents for (say) Linux vs Windows. + # EXTENSION: We can add a fileSymbolInfo field here to allow 'Find usages' for the document. +} + +type CodeGraphToolInfo { + name: String + version: String +} + +type SymbolInformationConnection { + nodes: [SymbolInformation!] + pageInfo: PageInfo! +} + +type SCIPOccurrenceConnection { + """ + A list of occurrences within a Document + """ + nodes: [SCIPOccurrence!] + + """ + Pagination information. + """ + pageInfo: PageInfo! +} + +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!] + # EXTENSION: We can add diagnostics etc. here in the future if needed. +} + +input SCIPOccurrencesFilter { + rangeFilter: RangeFilter + rolesFilter: RolesFilter + # EXTENSION: We can add a DiagnosticsFilter etc. here in the future if needed. +} + +input RangeFilter { + """ + Zero-based line number, inclusive. + """ + startLine: Int + """ + Zero-based line number, inclusive. + """ + endLine: Int +} + +input RolesFilter { + or: [RolesFilter!] + equals: SymbolRole +} + +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, } """ @@ -109,7 +264,7 @@ type GitBlobLSIFData implements TreeEntryLSIFData { When specified, it filters references by filename. """ filter: String - ): LocationConnection! + ): LocationConnection! @deprecated(reason: "Superseded by usagesForSymbol; use the Definition filter") """ A list of references of the symbol under the given document position. @@ -118,12 +273,12 @@ type GitBlobLSIFData implements TreeEntryLSIFData { """ The line on which the symbol occurs (zero-based, inclusive). """ - line: Int! + line: Int """ The character (not byte) of the start line on which the symbol occurs (zero-based, inclusive). """ - character: Int! + character: Int """ When specified, indicates that this request should be paginated and @@ -144,7 +299,7 @@ type GitBlobLSIFData implements TreeEntryLSIFData { When specified, it filters references by filename. """ filter: String - ): LocationConnection! + ): LocationConnection! @deprecated(reason: "Superseded by usagesForSymbol; use the Reference filter") """ A list of implementations of the symbol under the given document position. @@ -179,7 +334,7 @@ type GitBlobLSIFData implements TreeEntryLSIFData { When specified, it filters implementation by filename. """ filter: String - ): LocationConnection! + ): LocationConnection! @deprecated(reason: "Superseded by usagesForSymbol; use the Implemention filter") """ A list of prototypes of the symbol under the given document position. @@ -214,7 +369,7 @@ type GitBlobLSIFData implements TreeEntryLSIFData { When specified, it filters prototypes by filename. """ filter: String - ): LocationConnection! + ): LocationConnection! @deprecated(reason: "Superseded by usagesForSymbol; use the Super filter") """ The hover result of the symbol under the given document position. @@ -247,6 +402,282 @@ type GitBlobLSIFData implements TreeEntryLSIFData { snapshot(indexID: ID!): [SnapshotData!] } +extend type Query { + """ + Identify usages for either a semantic symbol, or the symbol(s) implied + by a source range. + + Ordering and uniqueness guarantees: + 1. The usages returned will already be de-duplicated. + 2. Results for a single file are contiguous. + 3. Results for a single repository are contiguous. + + Related: See `codeGraphData` on GitBlob. + """ + usagesForSymbol( + """ + Symbol to perform the lookup for. + + If provided, then the start and end position in `range` are optional. + + If not provided, and if multiple symbols are detected at the same range, + the combined results for all symbols will be returned. + In that case, `Usage.symbol.name` can be used to group results. + """ + symbol: SymbolComparator, + + """ + Information about an existing source range for a usage of the symbol. + + TODO: Specify behavior for various cases of overlap between LookupRange + and actual occurrence range. + """ + range: RangeInput!, + + filter: UsagesFilter, + + """ + When specified, indicates that this request should be paginated and + the first N results (relative to the cursor) should be returned. i.e. + how many results to return per page. + """ + first: Int + + """ + When specified, indicates that this request should be paginated and + to fetch results starting at this cursor. + A future request can be made for more results by passing in the + 'UsageConnection.pageInfo.endCursor' that is returned. + """ + after: String + ): UsageConnection! +} + +input SymbolComparator { + name: SymbolNameComparator! + provenance: CodeGraphDataProvenanceComparator! +} + +# Implementation note: In the future, we may want to extend this +# to allow passing in a suffix or a "symbol pattern". +# See https://github.com/sourcegraph/sourcegraph/issues/59957 +input SymbolNameComparator { + equals: String +} + +input CodeGraphDataProvenanceComparator { + equals: CodeGraphDataProvenance +} + +""" +Type representing useful information about a symbol in code, +based on 'SymbolInformation' in SCIP. +""" +type SymbolInformation { + """ + 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. + """ + name: String! + + """ + Hover documentation for the symbol, in Markdown format. + + The caller must take care to escape any particular strings, + such as raw HTML, as necessary. + + The value is null when the hover documentation is not found + by 'dataSource'. + + The value is empty when `dataSource` is confident that there + is no appropriate hover documentation to display. + """ + documentation: [String!] + + """ + Coarse-grained information about the data source. + """ + provenance: CodeGraphDataProvenance! + + """ + Opaque fine-grained information describing the data source. + + Provided only for debugging. + + This field should be ignored when checking structural equality. + """ + dataSource: String +} + +input RangeInput { + """ + Defaults to instance-wide search if the repo is not specified. + + This field is necessary if SymbolComparator is a repository-local or + file-local symbol. It is mandatory for cross-repository symbols + due to implementation limitations. + """ + repository: String! + + """ + Defaults to HEAD of the default branch if not specified. + """ + revision: String + + """ + Defaults to repo-wide search if the path is not specified. + + This field is necessary if SymbolComparator is a file-local symbol. + It is mandatory for repository-local and cross-repo symbols + due to implementation limitations. + """ + path: String! + + start: PositionInput + + end: PositionInput +} + +input PositionInput { + """ + Zero-based count of newline (\n or \r\n) characters before this position. + """ + line: Int! + + """ + Zero-based UTF-16 code unit offset from preceding newline (\n or \r\n) character. + """ + character: Int! +} + +# NOTE(id: filter-vs-comparator-terminology) +# +# 'Filter' is used for complex objects whereas 'Comparator' is used +# for primitive objects. + +""" +An empty filter allows all kinds of usages for all paths in all repositories. + +However, if the symbol used for lookup is a file-local symbol or a +repository-local symbol, then usages will automatically be limited to the +same file or same repository respectively. + +Filters are combined monoidally, i.e. all filters are applied. For example, + +``` +{and: [u1, u2], or: [u3, u4], not: u5} +``` + +would be equivalent to: + +``` +{and: [{and: [u1, u2]}, {or: [u3, u4]}, {not: u5}]} +``` +""" +# Design Rationale: +# +# 1. The ref panel did separate queries for 'this repo' vs 'all other repos' +# https://sourcegraph.com/github.com/sourcegraph/sourcegraph/-/blob/client/web/src/enterprise/codeintel/searchBased.ts?L105:13-105:23#tab=references +# That requires `NOT` (at least for a `repo`). +# 2. The ref panel has a way to specify paths for filtering. This requires AND. +# AND will not be part of the first implementation pass. However, +# it would allow fetching less data instead of post-filtering client-side. +# 3. Currently, the ref panel has a setting for only showing precise results. +# Once syntactic is added, if we want to allow the user to specify either +# syntactic or precise (but not search-based), we'd need OR. +# OR will also not be needed for the first implementation pass. +input UsagesFilter { + and: [UsagesFilter!] + or: [UsagesFilter!] + not: UsagesFilter + repository: RepositoryFilter + path: PathFilter + kind: SymbolUsageKindComparator + provenance: CodeGraphDataProvenanceComparator +} + +input RepositoryFilter { + name: StringComparator! +} + +input PathFilter { + name: StringComparator! +} + +input StringComparator { + equals: String +} + +input SymbolUsageKindComparator { + equals: SymbolUsageKind +} + +enum SymbolUsageKind { + Definition, + + Reference, + + """ + If a type T implements an interface X or inherits from another type X, + then searching for Implementations of X will return T as a result + if the data source supports it. + """ + Implementation, + + """ + If a type T implements an interface X or inherits from another type X, + then searching for Supers of T will return X as a result + if the data source supports it. + """ + Super, +} + +type UsageConnection { + nodes: [Usage!]! + pageInfo: PageInfo! +} + +type UsageRange { + repository: String! + revision: String! + path: String! + range: Range! +} + +type Usage { + symbol: SymbolInformation! + + """ + Invariant: `range.path` == `blob.path`. The path is made available + as part of this type for convenience. + """ + range: UsageRange + + """ + Instead of requesting `blob.content`, it may be more useful + to ask for `surroundingBlobContent`. + """ + # Left as optional in case we support generated files in the future, + # which may not be represented by GitBlob. + blob: GitBlob + + """ + Instead of blob { content }, allows accessing a sub-span of the content + using relative coordinates from the range of this usage. If linesBefore + or linesAfter is negative 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 +} + +input SurroundingLines { + linesBefore: Int + linesAfter: Int +} + """ The SCIP snapshot decoration for a single SCIP Occurrence. """ diff --git a/cmd/frontend/graphqlbackend/schema.graphql b/cmd/frontend/graphqlbackend/schema.graphql index f66a0f758c9b..865a51787426 100755 --- a/cmd/frontend/graphqlbackend/schema.graphql +++ b/cmd/frontend/graphqlbackend/schema.graphql @@ -6172,10 +6172,15 @@ type HighlightedFile { """ html: String! """ - Base64 encoded JSON payload of LSIF Typed with syntax highlighting data. + DEPRECATED: Base64 encoded JSON payload of SCIP with syntax highlighting data. + Use document instead. """ lsif: String! """ + The highlighted file with ranges represented using occurrences. + """ + document: AssociatedSCIPDocument! + """ A list of the desired line ranges. Each list is a list of lines, where each element is an HTML table row '...' string. This is useful if you only need to display specific subsets of the file. @@ -6183,6 +6188,68 @@ type HighlightedFile { lineRanges(ranges: [HighlightLineRange!]!): [[String!]!]! } +enum CodeGraphDataProvenance { + """ + Based on a compiler, a type-checker or a similar data source + which doesn't have false positives. + + The results are specific to a specific 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, +} + +type AssociatedSCIPDocument { + """ + Base64 encoded SCIP payload. + + After decoding, the resulting byte buffer can be parsed with + pre-generated bindings in the sourcegraph/scip repository or + manually generating bindings using + https://github.com/sourcegraph/scip/blob/main/scip.proto + + 'first' limits the number of bytes to be fetched. As a consequence, + a partial result may not be a well-formed Index. So users of this API + must either: + 1. Use a resilient, streaming parser, that parses as many tag-length-value + triplets as possible before requesting more data. + An example of streaming parsing can be found here: + https://sourcegraph.com/github.com/sourcegraph/scip/-/blob/bindings/go/scip/parse.go + OR + 2. Buffer all the strings, concatenate them, and parse the final result + with the provided generated bindings. + """ + data(first: Int, after: String): BytesConnection! + """ + Coarse-grained information about the data source. + """ + provenance: CodeGraphDataProvenance! + """ + Opaque fine-grained information describing the data source. + + Provided only for debugging. + + This field should be ignored when checking structural equality. + """ + dataSource: String +} + +type BytesConnection { + nodes: [String!] + hasNextPage: PageInfo! +} + """ A file match. """