238 lines
5.4 KiB
Go
238 lines
5.4 KiB
Go
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package sourcegraph
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// GraphQL queries for Sourcegraph API
|
|
|
|
const hoverQuery = `
|
|
query Hover($repo: String!, $rev: String!, $path: String!, $line: Int!, $char: Int!) {
|
|
repository(name: $repo) {
|
|
commit(rev: $rev) {
|
|
blob(path: $path) {
|
|
lsif {
|
|
hover(line: $line, character: $char) {
|
|
markdown {
|
|
text
|
|
}
|
|
range {
|
|
start { line character }
|
|
end { line character }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
const definitionQuery = `
|
|
query Definition($repo: String!, $rev: String!, $path: String!, $line: Int!, $char: Int!) {
|
|
repository(name: $repo) {
|
|
commit(rev: $rev) {
|
|
blob(path: $path) {
|
|
lsif {
|
|
definitions(line: $line, character: $char) {
|
|
nodes {
|
|
resource {
|
|
path
|
|
repository {
|
|
name
|
|
}
|
|
commit {
|
|
oid
|
|
}
|
|
}
|
|
range {
|
|
start { line character }
|
|
end { line character }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
const referencesQuery = `
|
|
query References($repo: String!, $rev: String!, $path: String!, $line: Int!, $char: Int!) {
|
|
repository(name: $repo) {
|
|
commit(rev: $rev) {
|
|
blob(path: $path) {
|
|
lsif {
|
|
references(line: $line, character: $char, first: 100) {
|
|
nodes {
|
|
resource {
|
|
path
|
|
repository {
|
|
name
|
|
}
|
|
commit {
|
|
oid
|
|
}
|
|
}
|
|
range {
|
|
start { line character }
|
|
end { line character }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
const syncRepoMutation = `
|
|
mutation SyncRepo($repo: String!) {
|
|
scheduleRepositoryPermissionsSync(repository: $repo) {
|
|
alwaysNil
|
|
}
|
|
}
|
|
`
|
|
|
|
// Response parsing structures
|
|
|
|
type hoverResponse struct {
|
|
Repository *struct {
|
|
Commit *struct {
|
|
Blob *struct {
|
|
LSIF *struct {
|
|
Hover *struct {
|
|
Markdown *struct {
|
|
Text string `json:"text"`
|
|
} `json:"markdown"`
|
|
Range *struct {
|
|
Start struct {
|
|
Line int `json:"line"`
|
|
Character int `json:"character"`
|
|
} `json:"start"`
|
|
End struct {
|
|
Line int `json:"line"`
|
|
Character int `json:"character"`
|
|
} `json:"end"`
|
|
} `json:"range"`
|
|
} `json:"hover"`
|
|
} `json:"lsif"`
|
|
} `json:"blob"`
|
|
} `json:"commit"`
|
|
} `json:"repository"`
|
|
}
|
|
|
|
type locationsResponse struct {
|
|
Repository *struct {
|
|
Commit *struct {
|
|
Blob *struct {
|
|
LSIF *struct {
|
|
Definitions *locationNodes `json:"definitions,omitempty"`
|
|
References *locationNodes `json:"references,omitempty"`
|
|
} `json:"lsif"`
|
|
} `json:"blob"`
|
|
} `json:"commit"`
|
|
} `json:"repository"`
|
|
}
|
|
|
|
type locationNodes struct {
|
|
Nodes []struct {
|
|
Resource struct {
|
|
Path string `json:"path"`
|
|
Repository struct {
|
|
Name string `json:"name"`
|
|
} `json:"repository"`
|
|
Commit struct {
|
|
OID string `json:"oid"`
|
|
} `json:"commit"`
|
|
} `json:"resource"`
|
|
Range struct {
|
|
Start struct {
|
|
Line int `json:"line"`
|
|
Character int `json:"character"`
|
|
} `json:"start"`
|
|
End struct {
|
|
Line int `json:"line"`
|
|
Character int `json:"character"`
|
|
} `json:"end"`
|
|
} `json:"range"`
|
|
} `json:"nodes"`
|
|
}
|
|
|
|
func parseHoverResponse(data json.RawMessage) (*HoverResult, error) {
|
|
var resp hoverResponse
|
|
if err := json.Unmarshal(data, &resp); err != nil {
|
|
return nil, fmt.Errorf("failed to parse hover response: %w", err)
|
|
}
|
|
|
|
if resp.Repository == nil || resp.Repository.Commit == nil ||
|
|
resp.Repository.Commit.Blob == nil || resp.Repository.Commit.Blob.LSIF == nil ||
|
|
resp.Repository.Commit.Blob.LSIF.Hover == nil {
|
|
return nil, nil // No hover info available
|
|
}
|
|
|
|
hover := resp.Repository.Commit.Blob.LSIF.Hover
|
|
result := &HoverResult{}
|
|
|
|
if hover.Markdown != nil {
|
|
result.Contents = hover.Markdown.Text
|
|
}
|
|
|
|
if hover.Range != nil {
|
|
result.Range = &Range{
|
|
Start: Position{
|
|
Line: hover.Range.Start.Line,
|
|
Character: hover.Range.Start.Character,
|
|
},
|
|
End: Position{
|
|
Line: hover.Range.End.Line,
|
|
Character: hover.Range.End.Character,
|
|
},
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func parseLocationsResponse(data json.RawMessage, field string) ([]Location, error) {
|
|
var resp locationsResponse
|
|
if err := json.Unmarshal(data, &resp); err != nil {
|
|
return nil, fmt.Errorf("failed to parse locations response: %w", err)
|
|
}
|
|
|
|
if resp.Repository == nil || resp.Repository.Commit == nil ||
|
|
resp.Repository.Commit.Blob == nil || resp.Repository.Commit.Blob.LSIF == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var nodes *locationNodes
|
|
switch field {
|
|
case "definitions":
|
|
nodes = resp.Repository.Commit.Blob.LSIF.Definitions
|
|
case "references":
|
|
nodes = resp.Repository.Commit.Blob.LSIF.References
|
|
}
|
|
|
|
if nodes == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
locations := make([]Location, 0, len(nodes.Nodes))
|
|
for _, node := range nodes.Nodes {
|
|
locations = append(locations, Location{
|
|
Repo: node.Resource.Repository.Name,
|
|
Path: node.Resource.Path,
|
|
Line: node.Range.Start.Line,
|
|
Character: node.Range.Start.Character,
|
|
})
|
|
}
|
|
|
|
return locations, nil
|
|
}
|