Skip to content

Commit 3bbe4f0

Browse files
feat: support for std.extVar variables (#37)
* config support inital pass * feat: support for extVars via lsp runtime configuration
1 parent 622645f commit 3bbe4f0

File tree

4 files changed

+184
-9
lines changed

4 files changed

+184
-9
lines changed

pkg/server/configuration.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package server
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/google/go-jsonnet"
8+
"github.com/jdbaldry/go-language-server-protocol/jsonrpc2"
9+
"github.com/jdbaldry/go-language-server-protocol/lsp/protocol"
10+
)
11+
12+
func (s *server) DidChangeConfiguration(ctx context.Context, params *protocol.DidChangeConfigurationParams) error {
13+
settingsMap, ok := params.Settings.(map[string]interface{})
14+
if !ok {
15+
return fmt.Errorf("%w: unsupported settings payload. expected json object, got: %T", jsonrpc2.ErrInvalidParams, params.Settings)
16+
}
17+
18+
for sk, sv := range settingsMap {
19+
switch sk {
20+
case "ext_vars":
21+
newVars, err := s.parseExtVars(sv)
22+
if err != nil {
23+
return fmt.Errorf("%w: ext_vars parsing failed: %v", jsonrpc2.ErrInvalidParams, err)
24+
}
25+
s.extVars = newVars
26+
27+
default:
28+
return fmt.Errorf("%w: unsupported settings key: %q", jsonrpc2.ErrInvalidParams, sk)
29+
}
30+
}
31+
return nil
32+
}
33+
34+
func (s *server) parseExtVars(unparsed interface{}) (map[string]string, error) {
35+
newVars, ok := unparsed.(map[string]interface{})
36+
if !ok {
37+
return nil, fmt.Errorf("unsupported settings value for ext_vars. expected json object. got: %T", unparsed)
38+
}
39+
40+
extVars := make(map[string]string, len(newVars))
41+
for varKey, varValue := range newVars {
42+
vv, ok := varValue.(string)
43+
if !ok {
44+
return nil, fmt.Errorf("unsupported settings value for ext_vars.%s. expected string. got: %T", varKey, varValue)
45+
}
46+
extVars[varKey] = vv
47+
}
48+
return extVars, nil
49+
}
50+
51+
func resetExtVars(vm *jsonnet.VM, vars map[string]string) {
52+
vm.ExtReset()
53+
for vk, vv := range vars {
54+
vm.ExtVar(vk, vv)
55+
}
56+
}

pkg/server/configuration_test.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package server
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
8+
"github.com/jdbaldry/go-language-server-protocol/lsp/protocol"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestConfiguration(t *testing.T) {
13+
type kase struct {
14+
name string
15+
settings interface{}
16+
fileContent string
17+
18+
expectedErr error
19+
expectedFileOutput string
20+
}
21+
22+
testCases := []kase{
23+
{
24+
name: "settings is not an object",
25+
settings: []string{""},
26+
fileContent: `[]`,
27+
expectedErr: errors.New("JSON RPC invalid params: unsupported settings payload. expected json object, got: []string"),
28+
},
29+
{
30+
name: "settings has unsupported key",
31+
settings: map[string]interface{}{
32+
"foo_bar": map[string]interface{}{},
33+
},
34+
fileContent: `[]`,
35+
expectedErr: errors.New("JSON RPC invalid params: unsupported settings key: \"foo_bar\""),
36+
},
37+
{
38+
name: "ext_var config is empty",
39+
settings: map[string]interface{}{
40+
"ext_vars": map[string]interface{}{},
41+
},
42+
fileContent: `[]`,
43+
expectedFileOutput: `[]`,
44+
},
45+
{
46+
name: "ext_var config is missing",
47+
settings: map[string]interface{}{},
48+
fileContent: `[]`,
49+
expectedFileOutput: `[]`,
50+
},
51+
{
52+
name: "ext_var config is not an object",
53+
settings: map[string]interface{}{
54+
"ext_vars": []string{},
55+
},
56+
fileContent: `[]`,
57+
expectedErr: errors.New("JSON RPC invalid params: ext_vars parsing failed: unsupported settings value for ext_vars. expected json object. got: []string"),
58+
},
59+
{
60+
name: "ext_var config value is not a string",
61+
settings: map[string]interface{}{
62+
"ext_vars": map[string]interface{}{
63+
"foo": true,
64+
},
65+
},
66+
fileContent: `[]`,
67+
expectedErr: errors.New("JSON RPC invalid params: ext_vars parsing failed: unsupported settings value for ext_vars.foo. expected string. got: bool"),
68+
},
69+
{
70+
name: "ext_var config is valid",
71+
settings: map[string]interface{}{
72+
"ext_vars": map[string]interface{}{
73+
"hello": "world",
74+
},
75+
},
76+
fileContent: `
77+
{
78+
hello: std.extVar("hello"),
79+
}
80+
`,
81+
expectedFileOutput: `
82+
{
83+
"hello": "world"
84+
}
85+
`,
86+
},
87+
}
88+
89+
for _, tc := range testCases {
90+
t.Run(tc.name, func(t *testing.T) {
91+
s, fileURI := testServerWithFile(t, nil, tc.fileContent)
92+
93+
err := s.DidChangeConfiguration(
94+
context.TODO(),
95+
&protocol.DidChangeConfigurationParams{
96+
Settings: tc.settings,
97+
},
98+
)
99+
if tc.expectedErr == nil && err != nil {
100+
t.Fatalf("DidChangeConfiguration produced unexpected error: %v", err)
101+
} else if tc.expectedErr != nil && err == nil {
102+
t.Fatalf("expected DidChangeConfiguration to produce error but it did not")
103+
} else if tc.expectedErr != nil && err != nil {
104+
assert.EqualError(t, err, tc.expectedErr.Error())
105+
return
106+
}
107+
108+
vm, err := s.getVM("any")
109+
assert.NoError(t, err)
110+
111+
doc, err := s.cache.get(fileURI)
112+
assert.NoError(t, err)
113+
114+
json, err := vm.Evaluate(doc.ast)
115+
assert.NoError(t, err)
116+
assert.JSONEq(t, tc.expectedFileOutput, json)
117+
})
118+
}
119+
}

pkg/server/server.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ func NewServer(name, version string, client protocol.ClientCloser) *server {
4949
type server struct {
5050
name, version string
5151

52-
stdlib []stdlib.Function
53-
cache *cache
54-
client protocol.ClientCloser
55-
getVM func(path string) (*jsonnet.VM, error)
52+
stdlib []stdlib.Function
53+
cache *cache
54+
client protocol.ClientCloser
55+
getVM func(path string) (*jsonnet.VM, error)
56+
extVars map[string]string
5657

5758
// Feature flags
5859
EvalDiags bool
@@ -64,6 +65,7 @@ func (s *server) WithStaticVM(jpaths []string) *server {
6465
s.getVM = func(path string) (*jsonnet.VM, error) {
6566
jpaths = append(jpaths, filepath.Dir(path))
6667
vm := jsonnet.MakeVM()
68+
resetExtVars(vm, s.extVars)
6769
importer := &jsonnet.FileImporter{JPaths: jpaths}
6870
vm.Importer(importer)
6971
return vm, nil
@@ -82,7 +84,9 @@ func (s *server) WithTankaVM(fallbackJPath []string) *server {
8284
opts := tankaJsonnet.Opts{
8385
ImportPaths: jpath,
8486
}
85-
return tankaJsonnet.MakeVM(opts), nil
87+
vm := tankaJsonnet.MakeVM(opts)
88+
resetExtVars(vm, s.extVars)
89+
return vm, nil
8690
}
8791
return s
8892
}

pkg/server/unused.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,6 @@ func (s *server) DiagnosticWorkspace(context.Context, *protocol.WorkspaceDiagnos
216216
return nil, notImplemented("DiagnosticWorkspace")
217217
}
218218

219-
func (s *server) DidChangeConfiguration(context.Context, *protocol.DidChangeConfigurationParams) error {
220-
return notImplemented("DidChangeConfiguration")
221-
}
222-
223219
func (s *server) DidChangeWatchedFiles(context.Context, *protocol.DidChangeWatchedFilesParams) error {
224220
return notImplemented("DidChangeWatchedFiles")
225221
}

0 commit comments

Comments
 (0)