Commit d3318859 authored by Igor Drozdov's avatar Igor Drozdov

Accept string or int type for LSIF ids

Define Id type with custom marshalling which accepts either
int or string, because some languages (for example, Javascript)
define ids in LSIF as ints, but some languages (like Golang) use
string type for ids

It also changes int to uint32 for easier binary conversion
parent a87438cf
...@@ -12,19 +12,19 @@ type Line struct { ...@@ -12,19 +12,19 @@ type Line struct {
type Docs struct { type Docs struct {
Root string Root string
Entries map[string]string Entries map[Id]string
DocRanges map[string][]string DocRanges map[Id][]Id
Ranges *Ranges Ranges *Ranges
} }
type Document struct { type Document struct {
Id string `json:"id"` Id Id `json:"id"`
Uri string `json:"uri"` Uri string `json:"uri"`
} }
type DocumentRange struct { type DocumentRange struct {
OutV string `json:"outV"` OutV Id `json:"outV"`
RangeIds []string `json:"inVs"` RangeIds []Id `json:"inVs"`
} }
type Metadata struct { type Metadata struct {
...@@ -39,8 +39,8 @@ func NewDocs(tempDir string) (*Docs, error) { ...@@ -39,8 +39,8 @@ func NewDocs(tempDir string) (*Docs, error) {
return &Docs{ return &Docs{
Root: "file:///", Root: "file:///",
Entries: make(map[string]string), Entries: make(map[Id]string),
DocRanges: make(map[string][]string), DocRanges: make(map[Id][]Id),
Ranges: ranges, Ranges: ranges,
}, nil }, nil
} }
......
...@@ -23,9 +23,9 @@ func TestRead(t *testing.T) { ...@@ -23,9 +23,9 @@ func TestRead(t *testing.T) {
require.NoError(t, d.Read(createLine("3", "document", "file:///Users/nested/folder/file.rb"))) require.NoError(t, d.Read(createLine("3", "document", "file:///Users/nested/folder/file.rb")))
require.NoError(t, d.Read(createLine("4", "document", "file:///Users/wrong/file.rb"))) require.NoError(t, d.Read(createLine("4", "document", "file:///Users/wrong/file.rb")))
require.Equal(t, d.Entries["2"], "file.rb") require.Equal(t, d.Entries[2], "file.rb")
require.Equal(t, d.Entries["3"], "folder/file.rb") require.Equal(t, d.Entries[3], "folder/file.rb")
require.Equal(t, d.Entries["4"], "file:///Users/wrong/file.rb") require.Equal(t, d.Entries[4], "file:///Users/wrong/file.rb")
} }
func TestReadContainsLine(t *testing.T) { func TestReadContainsLine(t *testing.T) {
...@@ -37,5 +37,5 @@ func TestReadContainsLine(t *testing.T) { ...@@ -37,5 +37,5 @@ func TestReadContainsLine(t *testing.T) {
require.NoError(t, d.Read(line)) require.NoError(t, d.Read(line))
require.Equal(t, []string{"2", "3"}, d.DocRanges["1"]) require.Equal(t, []Id{2, 3}, d.DocRanges[1])
} }
...@@ -12,7 +12,7 @@ type Offset struct { ...@@ -12,7 +12,7 @@ type Offset struct {
} }
type Hovers struct { type Hovers struct {
Offsets map[string]*Offset Offsets map[Id]*Offset
File *os.File File *os.File
CurrentOffset int CurrentOffset int
} }
...@@ -22,18 +22,18 @@ type RawResult struct { ...@@ -22,18 +22,18 @@ type RawResult struct {
} }
type RawData struct { type RawData struct {
Id string `json:"id"` Id Id `json:"id"`
Result RawResult `json:"result"` Result RawResult `json:"result"`
} }
type HoverRef struct { type HoverRef struct {
ResultSetId string `json:"outV"` ResultSetId Id `json:"outV"`
HoverId string `json:"inV"` HoverId Id `json:"inV"`
} }
type ResultSetRef struct { type ResultSetRef struct {
ResultSetId string `json:"outV"` ResultSetId Id `json:"outV"`
RefId string `json:"inV"` RefId Id `json:"inV"`
} }
func NewHovers(tempDir string) (*Hovers, error) { func NewHovers(tempDir string) (*Hovers, error) {
...@@ -43,7 +43,7 @@ func NewHovers(tempDir string) (*Hovers, error) { ...@@ -43,7 +43,7 @@ func NewHovers(tempDir string) (*Hovers, error) {
} }
return &Hovers{ return &Hovers{
Offsets: make(map[string]*Offset), Offsets: make(map[Id]*Offset),
File: file, File: file,
CurrentOffset: 0, CurrentOffset: 0,
}, nil }, nil
...@@ -68,7 +68,7 @@ func (h *Hovers) Read(label string, line []byte) error { ...@@ -68,7 +68,7 @@ func (h *Hovers) Read(label string, line []byte) error {
return nil return nil
} }
func (h *Hovers) For(refId string) json.RawMessage { func (h *Hovers) For(refId Id) json.RawMessage {
offset, ok := h.Offsets[refId] offset, ok := h.Offsets[refId]
if !ok || offset == nil { if !ok || offset == nil {
return nil return nil
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
func TestHoversRead(t *testing.T) { func TestHoversRead(t *testing.T) {
h := setupHovers(t) h := setupHovers(t)
require.Equal(t, `[{"value":"hello"}]`, string(h.For("1"))) require.Equal(t, `[{"value":"hello"}]`, string(h.For(1)))
require.NoError(t, h.Close()) require.NoError(t, h.Close())
} }
...@@ -19,8 +19,8 @@ func setupHovers(t *testing.T) *Hovers { ...@@ -19,8 +19,8 @@ func setupHovers(t *testing.T) *Hovers {
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, h.Read("hoverResult", []byte(`{"id":"2","label":"hoverResult","result":{"contents": ["hello"]}}`))) require.NoError(t, h.Read("hoverResult", []byte(`{"id":"2","label":"hoverResult","result":{"contents": ["hello"]}}`)))
require.NoError(t, h.Read("textDocument/hover", []byte(`{"id":"4","label":"textDocument/hover","outV":"3","inV":"2"}`))) require.NoError(t, h.Read("textDocument/hover", []byte(`{"id":4,"label":"textDocument/hover","outV":"3","inV":2}`)))
require.NoError(t, h.Read("textDocument/references", []byte(`{"id":"3","label":"textDocument/references","outV":"3","inV":"1"}`))) require.NoError(t, h.Read("textDocument/references", []byte(`{"id":"3","label":"textDocument/references","outV":3,"inV":"1"}`)))
return h return h
} }
package parser
import (
"encoding/json"
"errors"
"strconv"
)
const (
minId = 1
maxId = 20 * 1000 * 1000
)
type Id int32
func (id *Id) UnmarshalJSON(b []byte) error {
if b[0] != '"' {
if err := id.unmarshalInt(b); err != nil {
return err
}
} else {
if err := id.unmarshalString(b); err != nil {
return err
}
}
if *id < minId || *id > maxId {
return errors.New("json: id is invalid")
}
return nil
}
func (id *Id) unmarshalInt(b []byte) error {
return json.Unmarshal(b, (*int32)(id))
}
func (id *Id) unmarshalString(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
i, err := strconv.Atoi(s)
if err != nil {
return err
}
*id = Id(i)
return nil
}
package parser
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
type jsonWithId struct {
Value Id `json:"value"`
}
func TestId(t *testing.T) {
var v jsonWithId
require.NoError(t, json.Unmarshal([]byte(`{ "value": 1230 }`), &v))
require.Equal(t, Id(1230), v.Value)
require.NoError(t, json.Unmarshal([]byte(`{ "value": "1230" }`), &v))
require.Equal(t, Id(1230), v.Value)
require.Error(t, json.Unmarshal([]byte(`{ "value": "1.5" }`), &v))
require.Error(t, json.Unmarshal([]byte(`{ "value": 1.5 }`), &v))
require.Error(t, json.Unmarshal([]byte(`{ "value": "-1" }`), &v))
require.Error(t, json.Unmarshal([]byte(`{ "value": -1 }`), &v))
require.Error(t, json.Unmarshal([]byte(`{ "value": 21000000 }`), &v))
require.Error(t, json.Unmarshal([]byte(`{ "value": "21000000" }`), &v))
}
...@@ -10,37 +10,37 @@ const Definitions = "definitions" ...@@ -10,37 +10,37 @@ const Definitions = "definitions"
const References = "references" const References = "references"
type Ranges struct { type Ranges struct {
Entries map[string]*Range Entries map[Id]*Range
DefRefs map[string]*DefRef DefRefs map[Id]*DefRef
Hovers *Hovers Hovers *Hovers
} }
type RawRange struct { type RawRange struct {
Id string `json:"id"` Id Id `json:"id"`
Data Range `json:"start"` Data Range `json:"start"`
} }
type Range struct { type Range struct {
Line int `json:"line"` Line int32 `json:"line"`
Character int `json:"character"` Character int32 `json:"character"`
RefId string RefId Id
} }
type RawDefRef struct { type RawDefRef struct {
Property string `json:"property"` Property string `json:"property"`
RefId string `json:"outV"` RefId Id `json:"outV"`
RangeIds []string `json:"inVs"` RangeIds []Id `json:"inVs"`
DocId string `json:"document"` DocId Id `json:"document"`
} }
type DefRef struct { type DefRef struct {
Line string Line string
DocId string DocId Id
} }
type SerializedRange struct { type SerializedRange struct {
StartLine int `json:"start_line"` StartLine int32 `json:"start_line"`
StartChar int `json:"start_char"` StartChar int32 `json:"start_char"`
DefinitionPath string `json:"definition_path,omitempty"` DefinitionPath string `json:"definition_path,omitempty"`
Hover json.RawMessage `json:"hover"` Hover json.RawMessage `json:"hover"`
} }
...@@ -52,8 +52,8 @@ func NewRanges(tempDir string) (*Ranges, error) { ...@@ -52,8 +52,8 @@ func NewRanges(tempDir string) (*Ranges, error) {
} }
return &Ranges{ return &Ranges{
Entries: make(map[string]*Range), Entries: make(map[Id]*Range),
DefRefs: make(map[string]*DefRef), DefRefs: make(map[Id]*DefRef),
Hovers: hovers, Hovers: hovers,
}, nil }, nil
} }
...@@ -75,7 +75,7 @@ func (r *Ranges) Read(label string, line []byte) error { ...@@ -75,7 +75,7 @@ func (r *Ranges) Read(label string, line []byte) error {
return nil return nil
} }
func (r *Ranges) Serialize(f io.Writer, rangeIds []string, docs map[string]string) error { func (r *Ranges) Serialize(f io.Writer, rangeIds []Id, docs map[Id]string) error {
encoder := json.NewEncoder(f) encoder := json.NewEncoder(f)
n := len(rangeIds) n := len(rangeIds)
...@@ -112,7 +112,7 @@ func (r *Ranges) Close() error { ...@@ -112,7 +112,7 @@ func (r *Ranges) Close() error {
return r.Hovers.Close() return r.Hovers.Close()
} }
func (r *Ranges) definitionPathFor(docs map[string]string, refId string) string { func (r *Ranges) definitionPathFor(docs map[Id]string, refId Id) string {
defRef, ok := r.DefRefs[refId] defRef, ok := r.DefRefs[refId]
if !ok { if !ok {
return "" return ""
...@@ -157,7 +157,7 @@ func (r *Ranges) addItem(line []byte) error { ...@@ -157,7 +157,7 @@ func (r *Ranges) addItem(line []byte) error {
defRange := r.Entries[defRef.RangeIds[0]] defRange := r.Entries[defRef.RangeIds[0]]
r.DefRefs[defRef.RefId] = &DefRef{ r.DefRefs[defRef.RefId] = &DefRef{
Line: strconv.Itoa(defRange.Line + 1), Line: strconv.Itoa(int(defRange.Line + 1)),
DocId: defRef.DocId, DocId: defRef.DocId,
} }
......
...@@ -11,21 +11,21 @@ func TestRangesRead(t *testing.T) { ...@@ -11,21 +11,21 @@ func TestRangesRead(t *testing.T) {
r, cleanup := setup(t) r, cleanup := setup(t)
defer cleanup() defer cleanup()
firstRange := Range{Line: 1, Character: 2, RefId: "3"} firstRange := Range{Line: 1, Character: 2, RefId: 3}
require.Equal(t, &firstRange, r.Entries["1"]) require.Equal(t, &firstRange, r.Entries[1])
secondRange := Range{Line: 5, Character: 4, RefId: "3"} secondRange := Range{Line: 5, Character: 4, RefId: 3}
require.Equal(t, &secondRange, r.Entries["2"]) require.Equal(t, &secondRange, r.Entries[2])
} }
func TestSerialize(t *testing.T) { func TestSerialize(t *testing.T) {
r, cleanup := setup(t) r, cleanup := setup(t)
defer cleanup() defer cleanup()
docs := map[string]string{"6": "def-path"} docs := map[Id]string{6: "def-path"}
var buf bytes.Buffer var buf bytes.Buffer
err := r.Serialize(&buf, []string{"1"}, docs) err := r.Serialize(&buf, []Id{1}, docs)
want := `[{"start_line":1,"start_char":2,"definition_path":"def-path#L2","hover":null}` + "\n]" want := `[{"start_line":1,"start_char":2,"definition_path":"def-path#L2","hover":null}` + "\n]"
require.NoError(t, err) require.NoError(t, err)
...@@ -36,11 +36,11 @@ func setup(t *testing.T) (*Ranges, func()) { ...@@ -36,11 +36,11 @@ func setup(t *testing.T) (*Ranges, func()) {
r, err := NewRanges("") r, err := NewRanges("")
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, r.Read("range", []byte(`{"id":"1","label":"range","start":{"line":1,"character":2}}`))) require.NoError(t, r.Read("range", []byte(`{"id":1,"label":"range","start":{"line":1,"character":2}}`)))
require.NoError(t, r.Read("range", []byte(`{"id":"2","label":"range","start":{"line":5,"character":4}}`))) require.NoError(t, r.Read("range", []byte(`{"id":"2","label":"range","start":{"line":5,"character":4}}`)))
require.NoError(t, r.Read("item", []byte(`{"id":"4","label":"item","property":"definitions","outV":"3","inVs":["1"],"document":"6"}`))) require.NoError(t, r.Read("item", []byte(`{"id":4,"label":"item","property":"definitions","outV":"3","inVs":[1],"document":"6"}`)))
require.NoError(t, r.Read("item", []byte(`{"id":"4","label":"item","property":"references","outV":"3","inVs":["2"]}`))) require.NoError(t, r.Read("item", []byte(`{"id":"5","label":"item","property":"references","outV":3,"inVs":["2"]}`)))
cleanup := func() { cleanup := func() {
require.NoError(t, r.Close()) require.NoError(t, r.Close())
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment