1package parser
2
3import (
4	"encoding/json"
5	"io/ioutil"
6	"os"
7)
8
9type Offset struct {
10	At  int32
11	Len int32
12}
13
14type Hovers struct {
15	File          *os.File
16	Offsets       *cache
17	CurrentOffset int
18}
19
20type RawResult struct {
21	Contents json.RawMessage `json:"contents"`
22}
23
24type RawData struct {
25	Id     Id        `json:"id"`
26	Result RawResult `json:"result"`
27}
28
29type HoverRef struct {
30	ResultSetId Id `json:"outV"`
31	HoverId     Id `json:"inV"`
32}
33
34type ResultSetRef struct {
35	ResultSetId Id `json:"outV"`
36	RefId       Id `json:"inV"`
37}
38
39func NewHovers(config Config) (*Hovers, error) {
40	tempPath := config.TempPath
41
42	file, err := ioutil.TempFile(tempPath, "hovers")
43	if err != nil {
44		return nil, err
45	}
46
47	if err := os.Remove(file.Name()); err != nil {
48		return nil, err
49	}
50
51	offsets, err := newCache(tempPath, "hovers-indexes", Offset{})
52	if err != nil {
53		return nil, err
54	}
55
56	return &Hovers{
57		File:          file,
58		Offsets:       offsets,
59		CurrentOffset: 0,
60	}, nil
61}
62
63func (h *Hovers) Read(label string, line []byte) error {
64	switch label {
65	case "hoverResult":
66		if err := h.addData(line); err != nil {
67			return err
68		}
69	case "textDocument/hover":
70		if err := h.addHoverRef(line); err != nil {
71			return err
72		}
73	case "textDocument/references":
74		if err := h.addResultSetRef(line); err != nil {
75			return err
76		}
77	}
78
79	return nil
80}
81
82func (h *Hovers) For(refId Id) json.RawMessage {
83	var offset Offset
84	if err := h.Offsets.Entry(refId, &offset); err != nil || offset.Len == 0 {
85		return nil
86	}
87
88	hover := make([]byte, offset.Len)
89	_, err := h.File.ReadAt(hover, int64(offset.At))
90	if err != nil {
91		return nil
92	}
93
94	return json.RawMessage(hover)
95}
96
97func (h *Hovers) Close() error {
98	return combineErrors(
99		h.File.Close(),
100		h.Offsets.Close(),
101	)
102}
103
104func (h *Hovers) addData(line []byte) error {
105	var rawData RawData
106	if err := json.Unmarshal(line, &rawData); err != nil {
107		return err
108	}
109
110	codeHovers, err := newCodeHovers(rawData.Result.Contents)
111	if err != nil {
112		return err
113	}
114
115	codeHoversData, err := json.Marshal(codeHovers)
116	if err != nil {
117		return err
118	}
119
120	n, err := h.File.Write(codeHoversData)
121	if err != nil {
122		return err
123	}
124
125	offset := Offset{At: int32(h.CurrentOffset), Len: int32(n)}
126	h.CurrentOffset += n
127
128	return h.Offsets.SetEntry(rawData.Id, &offset)
129}
130
131func (h *Hovers) addHoverRef(line []byte) error {
132	var hoverRef HoverRef
133	if err := json.Unmarshal(line, &hoverRef); err != nil {
134		return err
135	}
136
137	var offset Offset
138	if err := h.Offsets.Entry(hoverRef.HoverId, &offset); err != nil {
139		return err
140	}
141
142	return h.Offsets.SetEntry(hoverRef.ResultSetId, &offset)
143}
144
145func (h *Hovers) addResultSetRef(line []byte) error {
146	var ref ResultSetRef
147	if err := json.Unmarshal(line, &ref); err != nil {
148		return err
149	}
150
151	var offset Offset
152	if err := h.Offsets.Entry(ref.ResultSetId, &offset); err != nil {
153		return nil
154	}
155
156	return h.Offsets.SetEntry(ref.RefId, &offset)
157}
158