gitlab-org--gitlab-foss/workhorse/internal/lsif_transformer/parser/ranges.go

220 lines
4.0 KiB
Go

package parser
import (
"encoding/json"
"errors"
"io"
"strconv"
)
const (
definitions = "definitions"
references = "references"
)
type Ranges struct {
DefRefs map[Id]Item
References *References
Hovers *Hovers
Cache *cache
}
type RawRange struct {
Id Id `json:"id"`
Data Range `json:"start"`
}
type Range struct {
Line int32 `json:"line"`
Character int32 `json:"character"`
RefId Id
}
type RawItem struct {
Property string `json:"property"`
RefId Id `json:"outV"`
RangeIds []Id `json:"inVs"`
DocId Id `json:"document"`
}
type Item struct {
Line int32
DocId Id
}
type SerializedRange struct {
StartLine int32 `json:"start_line"`
StartChar int32 `json:"start_char"`
DefinitionPath string `json:"definition_path,omitempty"`
Hover json.RawMessage `json:"hover"`
References []SerializedReference `json:"references,omitempty"`
}
func NewRanges(config Config) (*Ranges, error) {
hovers, err := NewHovers(config)
if err != nil {
return nil, err
}
references, err := NewReferences(config)
if err != nil {
return nil, err
}
cache, err := newCache(config.TempPath, "ranges", Range{})
if err != nil {
return nil, err
}
return &Ranges{
DefRefs: make(map[Id]Item),
References: references,
Hovers: hovers,
Cache: cache,
}, nil
}
func (r *Ranges) Read(label string, line []byte) error {
switch label {
case "range":
if err := r.addRange(line); err != nil {
return err
}
case "item":
if err := r.addItem(line); err != nil {
return err
}
default:
return r.Hovers.Read(label, line)
}
return nil
}
func (r *Ranges) Serialize(f io.Writer, rangeIds []Id, docs map[Id]string) error {
encoder := json.NewEncoder(f)
n := len(rangeIds)
if _, err := f.Write([]byte("[")); err != nil {
return err
}
for i, rangeId := range rangeIds {
entry, err := r.getRange(rangeId)
if err != nil {
continue
}
serializedRange := SerializedRange{
StartLine: entry.Line,
StartChar: entry.Character,
DefinitionPath: r.definitionPathFor(docs, entry.RefId),
Hover: r.Hovers.For(entry.RefId),
References: r.References.For(docs, entry.RefId),
}
if err := encoder.Encode(serializedRange); err != nil {
return err
}
if i+1 < n {
if _, err := f.Write([]byte(",")); err != nil {
return err
}
}
}
if _, err := f.Write([]byte("]")); err != nil {
return err
}
return nil
}
func (r *Ranges) Close() error {
for _, err := range []error{
r.Cache.Close(),
r.References.Close(),
r.Hovers.Close(),
} {
if err != nil {
return err
}
}
return nil
}
func (r *Ranges) definitionPathFor(docs map[Id]string, refId Id) string {
defRef, ok := r.DefRefs[refId]
if !ok {
return ""
}
defPath := docs[defRef.DocId] + "#L" + strconv.Itoa(int(defRef.Line))
return defPath
}
func (r *Ranges) addRange(line []byte) error {
var rg RawRange
if err := json.Unmarshal(line, &rg); err != nil {
return err
}
return r.Cache.SetEntry(rg.Id, &rg.Data)
}
func (r *Ranges) addItem(line []byte) error {
var rawItem RawItem
if err := json.Unmarshal(line, &rawItem); err != nil {
return err
}
if rawItem.Property != definitions && rawItem.Property != references {
return nil
}
if len(rawItem.RangeIds) == 0 {
return errors.New("no range IDs")
}
var references []Item
for _, rangeId := range rawItem.RangeIds {
rg, err := r.getRange(rangeId)
if err != nil {
return err
}
rg.RefId = rawItem.RefId
if err := r.Cache.SetEntry(rangeId, rg); err != nil {
return err
}
item := Item{
Line: rg.Line + 1,
DocId: rawItem.DocId,
}
if rawItem.Property == definitions {
r.DefRefs[rawItem.RefId] = item
} else {
references = append(references, item)
}
}
if err := r.References.Store(rawItem.RefId, references); err != nil {
return err
}
return nil
}
func (r *Ranges) getRange(rangeId Id) (*Range, error) {
var rg Range
if err := r.Cache.Entry(rangeId, &rg); err != nil {
return nil, err
}
return &rg, nil
}