Skip to content

Commit b1a9763

Browse files
Fix symbols crashing (#183)
Closes #166 Symbols are now always strings. If field names are complex nodes, then we use the actual jsonnet
1 parent 446a98f commit b1a9763

File tree

7 files changed

+157
-23
lines changed

7 files changed

+157
-23
lines changed

Diff for: pkg/ast/processing/find_field.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func (p *Processor) extractObjectRangesFromDesugaredObjs(desugaredObjs []*ast.De
9696
}
9797
if len(indexList) == 0 {
9898
for _, found := range foundFields {
99-
ranges = append(ranges, FieldToRange(*found))
99+
ranges = append(ranges, p.FieldToRange(*found))
100100

101101
// If the field is not PlusSuper (field+: value), we stop there. Other previous values are not relevant
102102
// If partialMatchCurrentField is true, we can continue to look for other fields

Diff for: pkg/ast/processing/object_range.go

+29-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"strings"
66

77
"github.com/google/go-jsonnet/ast"
8+
position "github.com/grafana/jsonnet-language-server/pkg/position_conversion"
9+
"github.com/jdbaldry/go-language-server-protocol/lsp/protocol"
810
)
911

1012
type ObjectRange struct {
@@ -15,41 +17,56 @@ type ObjectRange struct {
1517
Node ast.Node
1618
}
1719

18-
func FieldToRange(field ast.DesugaredObjectField) ObjectRange {
20+
func (p *Processor) FieldToRange(field ast.DesugaredObjectField) ObjectRange {
1921
selectionRange := ast.LocationRange{
2022
Begin: ast.Location{
2123
Line: field.LocRange.Begin.Line,
2224
Column: field.LocRange.Begin.Column,
2325
},
2426
End: ast.Location{
2527
Line: field.LocRange.Begin.Line,
26-
Column: field.LocRange.Begin.Column + len(FieldNameToString(field.Name)),
28+
Column: field.LocRange.Begin.Column + len(p.FieldNameToString(field.Name)),
2729
},
2830
}
2931
return ObjectRange{
3032
Filename: field.LocRange.FileName,
3133
SelectionRange: selectionRange,
3234
FullRange: field.LocRange,
33-
FieldName: FieldNameToString(field.Name),
35+
FieldName: p.FieldNameToString(field.Name),
3436
Node: field.Body,
3537
}
3638
}
3739

38-
func FieldNameToString(fieldName ast.Node) string {
39-
if fieldName, ok := fieldName.(*ast.LiteralString); ok {
40+
func (p *Processor) FieldNameToString(fieldName ast.Node) string {
41+
const unknown = "<unknown>"
42+
43+
switch fieldName := fieldName.(type) {
44+
case *ast.LiteralString:
4045
return fieldName.Value
41-
}
42-
if fieldName, ok := fieldName.(*ast.Index); ok {
46+
case *ast.Index:
4347
// We only want to wrap in brackets at the top level, so we trim at all step and then rewrap
4448
return fmt.Sprintf("[%s.%s]",
45-
strings.Trim(FieldNameToString(fieldName.Target), "[]"),
46-
strings.Trim(FieldNameToString(fieldName.Index), "[]"),
49+
strings.Trim(p.FieldNameToString(fieldName.Target), "[]"),
50+
strings.Trim(p.FieldNameToString(fieldName.Index), "[]"),
4751
)
48-
}
49-
if fieldName, ok := fieldName.(*ast.Var); ok {
52+
case *ast.Var:
5053
return string(fieldName.Id)
54+
default:
55+
loc := fieldName.Loc()
56+
if loc == nil {
57+
return unknown
58+
}
59+
fname := loc.FileName
60+
if fname == "" {
61+
return unknown
62+
}
63+
64+
content, err := p.cache.GetContents(protocol.URIFromPath(fname), position.RangeASTToProtocol(*loc))
65+
if err != nil {
66+
return unknown
67+
}
68+
return content
5169
}
52-
return ""
5370
}
5471

5572
func LocalBindToRange(bind ast.LocalBind) ObjectRange {

Diff for: pkg/cache/cache.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ func (c *Cache) GetContents(uri protocol.DocumentURI, position protocol.Range) (
107107
for i := position.Start.Line; i <= position.End.Line; i++ {
108108
switch i {
109109
case position.Start.Line:
110-
contentBuilder.WriteString(lines[i][position.Start.Character:])
110+
if i == position.End.Line {
111+
contentBuilder.WriteString(lines[i][position.Start.Character:position.End.Character])
112+
} else {
113+
contentBuilder.WriteString(lines[i][position.Start.Character:])
114+
}
111115
case position.End.Line:
112116
contentBuilder.WriteString(lines[i][:position.End.Character])
113117
default:

Diff for: pkg/server/hover_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ func TestHover(t *testing.T) {
259259
expectedContent: protocol.Hover{
260260
Contents: protocol.MarkupContent{
261261
Kind: protocol.Markdown,
262-
Value: "```jsonnet\nbar: 'innerfoo',\n```\n",
262+
Value: "```jsonnet\nbar: 'innerfoo'\n```\n",
263263
},
264264
Range: protocol.Range{
265265
Start: protocol.Position{Line: 9, Character: 5},

Diff for: pkg/server/symbols.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ func (s *Server) DocumentSymbol(_ context.Context, params *protocol.DocumentSymb
2727
return nil, nil
2828
}
2929

30-
symbols := buildDocumentSymbols(doc.AST)
30+
processor := processing.NewProcessor(s.cache, nil)
31+
symbols := s.buildDocumentSymbols(processor, doc.AST)
3132

3233
result := make([]interface{}, len(symbols))
3334
for i, symbol := range symbols {
@@ -37,13 +38,13 @@ func (s *Server) DocumentSymbol(_ context.Context, params *protocol.DocumentSymb
3738
return result, nil
3839
}
3940

40-
func buildDocumentSymbols(node ast.Node) []protocol.DocumentSymbol {
41+
func (s *Server) buildDocumentSymbols(processor *processing.Processor, node ast.Node) []protocol.DocumentSymbol {
4142
var symbols []protocol.DocumentSymbol
4243

4344
switch node := node.(type) {
4445
case *ast.Binary:
45-
symbols = append(symbols, buildDocumentSymbols(node.Left)...)
46-
symbols = append(symbols, buildDocumentSymbols(node.Right)...)
46+
symbols = append(symbols, s.buildDocumentSymbols(processor, node.Left)...)
47+
symbols = append(symbols, s.buildDocumentSymbols(processor, node.Right)...)
4748
case *ast.Local:
4849
for _, bind := range node.Binds {
4950
objectRange := processing.LocalBindToRange(bind)
@@ -55,21 +56,21 @@ func buildDocumentSymbols(node ast.Node) []protocol.DocumentSymbol {
5556
Detail: symbolDetails(bind.Body),
5657
})
5758
}
58-
symbols = append(symbols, buildDocumentSymbols(node.Body)...)
59+
symbols = append(symbols, s.buildDocumentSymbols(processor, node.Body)...)
5960
case *ast.DesugaredObject:
6061
for _, field := range node.Fields {
6162
kind := protocol.Field
6263
if field.Hide == ast.ObjectFieldHidden {
6364
kind = protocol.Property
6465
}
65-
fieldRange := processing.FieldToRange(field)
66+
fieldRange := processor.FieldToRange(field)
6667
symbols = append(symbols, protocol.DocumentSymbol{
67-
Name: processing.FieldNameToString(field.Name),
68+
Name: processor.FieldNameToString(field.Name),
6869
Kind: kind,
6970
Range: position.RangeASTToProtocol(fieldRange.FullRange),
7071
SelectionRange: position.RangeASTToProtocol(fieldRange.SelectionRange),
7172
Detail: symbolDetails(field.Body),
72-
Children: buildDocumentSymbols(field.Body),
73+
Children: s.buildDocumentSymbols(processor, field.Body),
7374
})
7475
}
7576
}

Diff for: pkg/server/symbols_test.go

+106
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,112 @@ func TestSymbols(t *testing.T) {
266266
},
267267
},
268268
},
269+
{
270+
name: "Conditional fields",
271+
filename: "testdata/conditional-fields.jsonnet",
272+
expectSymbols: []interface{}{
273+
protocol.DocumentSymbol{
274+
Name: "flag",
275+
Detail: "Boolean",
276+
Kind: protocol.Variable,
277+
Range: protocol.Range{
278+
Start: protocol.Position{
279+
Line: 0,
280+
Character: 6,
281+
},
282+
End: protocol.Position{
283+
Line: 0,
284+
Character: 17,
285+
},
286+
},
287+
SelectionRange: protocol.Range{
288+
Start: protocol.Position{
289+
Line: 0,
290+
Character: 6,
291+
},
292+
End: protocol.Position{
293+
Line: 0,
294+
Character: 10,
295+
},
296+
},
297+
},
298+
protocol.DocumentSymbol{
299+
Name: "if flag then 'hello'",
300+
Detail: "String",
301+
Kind: protocol.Field,
302+
Range: protocol.Range{
303+
Start: protocol.Position{
304+
Line: 2,
305+
Character: 2,
306+
},
307+
End: protocol.Position{
308+
Line: 2,
309+
Character: 34,
310+
},
311+
},
312+
SelectionRange: protocol.Range{
313+
Start: protocol.Position{
314+
Line: 2,
315+
Character: 2,
316+
},
317+
End: protocol.Position{
318+
Line: 2,
319+
Character: 22,
320+
},
321+
},
322+
},
323+
protocol.DocumentSymbol{
324+
Name: "if flag then 'hello1' else 'hello2'",
325+
Detail: "String",
326+
Kind: protocol.Field,
327+
Range: protocol.Range{
328+
Start: protocol.Position{
329+
Line: 3,
330+
Character: 2,
331+
},
332+
End: protocol.Position{
333+
Line: 3,
334+
Character: 49,
335+
},
336+
},
337+
SelectionRange: protocol.Range{
338+
Start: protocol.Position{
339+
Line: 3,
340+
Character: 2,
341+
},
342+
End: protocol.Position{
343+
Line: 3,
344+
Character: 37,
345+
},
346+
},
347+
},
348+
protocol.DocumentSymbol{
349+
Name: "if false == flag then 'hello3' else (function() 'test')()",
350+
Detail: "String",
351+
Kind: protocol.Field,
352+
Range: protocol.Range{
353+
Start: protocol.Position{
354+
Line: 4,
355+
Character: 2,
356+
},
357+
End: protocol.Position{
358+
Line: 4,
359+
Character: 71,
360+
},
361+
},
362+
SelectionRange: protocol.Range{
363+
Start: protocol.Position{
364+
Line: 4,
365+
Character: 2,
366+
},
367+
End: protocol.Position{
368+
Line: 4,
369+
Character: 59,
370+
},
371+
},
372+
},
373+
},
374+
},
269375
} {
270376
t.Run(tc.name, func(t *testing.T) {
271377
params := &protocol.DocumentSymbolParams{

Diff for: pkg/server/testdata/conditional-fields.jsonnet

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
local flag = true;
2+
{
3+
[if flag then 'hello']: 'world!',
4+
[if flag then 'hello1' else 'hello2']: 'world!',
5+
[if false == flag then 'hello3' else (function() 'test')()]: 'world!',
6+
}

0 commit comments

Comments
 (0)