Skip to content

Commit c96d24f

Browse files
committed
feat: Implement text buffers and textdocument notifications
1 parent f7933ae commit c96d24f

File tree

18 files changed

+1365
-104
lines changed

18 files changed

+1365
-104
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
/tmp/
2+
*.test

.golangci.yaml

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,85 @@
1-
version: '2'
1+
version: "2"
22
linters:
3+
enable:
4+
- depguard
5+
- errcheck
6+
- ginkgolinter
7+
- gocritic
8+
- godot
9+
- gosec
10+
- govet
11+
- ineffassign
12+
- misspell
13+
- musttag
14+
- nolintlint
15+
- revive
16+
- sloglint
17+
- staticcheck
18+
- unused
319
settings:
4-
govet:
5-
disable:
6-
- structtag # participle uses nonconventional tags
20+
depguard:
21+
rules:
22+
lsp:
23+
files: ["${base-path}/internal/lsp/*"]
24+
deny:
25+
- pkg: github.com/armsnyder/gdshader-language-server/internal/ast
26+
desc: lsp and ast packages should not depend on each other
27+
- pkg: github.com/armsnyder/gdshader-language-server/internal/app
28+
desc: Library packages should not depend on app package
29+
ast:
30+
files: ["${base-path}/internal/ast/*"]
31+
deny:
32+
- pkg: github.com/armsnyder/gdshader-language-server/internal/lsp
33+
desc: lsp and ast packages should not depend on each other
34+
- pkg: github.com/armsnyder/gdshader-language-server/internal/app
35+
desc: Library packages should not depend on app package
36+
production:
37+
files: ["!**/*_test.go"]
38+
deny:
39+
- pkg: github.com/armsnyder/gdshader-language-server/internal/testutil
40+
desc: Test utilities should not be used in production code
41+
gocritic:
42+
enable-all: true
43+
disabled-checks:
44+
- whyNoLint
45+
revive:
46+
enable-all-rules: true
47+
rules:
48+
- name: add-constant
49+
disabled: true # Too strict
50+
- name: cognitive-complexity
51+
arguments: [15] # Default 7
52+
- name: cyclomatic
53+
arguments: [15] # Default 10
54+
- name: line-length-limit
55+
disabled: true # Too strict
56+
- name: max-public-structs
57+
disabled: true # Too strict
58+
- name: nested-structs
59+
disabled: true # Too strict
60+
- name: package-comments
61+
disabled: true # Not a lib
62+
- name: unused-receiver
63+
disabled: true # Too common
64+
sloglint:
65+
msg-style: capitalized
66+
key-naming-case: snake
67+
exclusions:
68+
rules:
69+
- path: internal/lsp/types.go
70+
text: comment .+ should be of the form # Types are documented with reference URLs
71+
- path: internal/ast/types.go
72+
text: malformed tag|bad syntax for struct tag # participle uses weird tags
73+
- path: _test.go
74+
text: dot-imports # gomega
75+
- path: _test.go
76+
text: function-length
77+
- path: _test.go
78+
linters: [gosec]
79+
- linters: [gocritic]
80+
text: contains a path separator # Not a valid check
781
formatters:
8-
enable:
9-
- gofumpt
82+
enable: [gofumpt]
1083
settings:
1184
gofumpt:
1285
module-path: github.com/armsnyder/gdshader-language-server

Makefile

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
default: install
2+
3+
all: reviewable install
4+
5+
install:
6+
go install
7+
8+
COV_UNIT := $(PWD)/tmp/cover/unit
9+
COV_E2E := $(PWD)/tmp/cover/e2e
10+
COV_MERGED := $(PWD)/tmp/cover/merged
11+
12+
reviewable: tidy fmt lint build test
13+
14+
tidy:
15+
go mod tidy
16+
17+
fmt:
18+
golangci-lint fmt
19+
20+
lint:
21+
golangci-lint run
22+
23+
build:
24+
go build ./...
25+
26+
test:
27+
rm -rf tmp/cover
28+
mkdir -p $(COV_UNIT) $(COV_MERGED)
29+
go test -cover ./... -args -test.gocoverdir=$(COV_UNIT)
30+
go tool covdata merge -i $(COV_UNIT),$(COV_E2E) -o $(COV_MERGED)
31+
$(call render_coverage, $(COV_UNIT))
32+
$(call render_coverage, $(COV_E2E))
33+
$(call render_coverage, $(COV_MERGED))
34+
35+
define render_coverage
36+
go tool covdata textfmt -i $(1) -o $(1)/cover.out
37+
go tool cover -html $(1)/cover.out -o $(1)/cover.html
38+
go tool covdata percent -i $(1)
39+
endef

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,12 @@ existing code conventions.
6868

6969
```graphql
7070
.
71-
├── main.go # Entry point
71+
├── main.go # Entry point
7272
└── internal
73-
├── ast # .gdshader file parser library (application agnostic)
74-
├── handler # Main application logic
75-
└── lsp # LSP server library (application agnostic)
73+
├── app # Main application logic
74+
├── ast # .gdshader file parser library (application agnostic)
75+
├── lsp # LSP server library (application agnostic)
76+
└── testutil # Test utilities for all packages
7677
```
7778

7879
## License

e2e_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func TestE2E(t *testing.T) {
6767
expected += "Content-Length: " + strconv.Itoa(len(s)) + "\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n" + s
6868
}
6969

70-
expect(`{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"textDocumentSync":{"openClose":true,"change":1}},"serverInfo":{"name":"gdshader-language-server","version":"development"}}}`)
70+
expect(`{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"textDocumentSync":{"openClose":true,"change":2},"completionProvider":{}},"serverInfo":{"name":"gdshader-language-server","version":"development"}}}`)
7171
expect(`{"jsonrpc":"2.0","id":2,"result":null}`)
7272

7373
g.Expect(stdout.String()).To(BeComparableTo(string(expected)), "Output does not match expected")

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/google/go-cmp v0.7.0
88
github.com/onsi/gomega v1.37.0
99
github.com/samber/lo v1.51.0
10+
github.com/zyedidia/rope v0.0.0-20210616205215-37fbf22eab3a
1011
)
1112

1213
require (

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
2020
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
2121
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
2222
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
23+
github.com/zyedidia/rope v0.0.0-20210616205215-37fbf22eab3a h1:+VbuFCNAjzVffErUlm0TIAZClFxFZwJ1il6qtWrnIg8=
24+
github.com/zyedidia/rope v0.0.0-20210616205215-37fbf22eab3a/go.mod h1:IKo1js3O2gdESAUH9F3B33wlrRBnpJMJR/gXyY0a3ho=
2325
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
2426
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
2527
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=

internal/app/handler.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/armsnyder/gdshader-language-server/internal/lsp"
8+
)
9+
10+
// Handler encapsulates the logic of the Godot shader language server.
11+
type Handler struct {
12+
documents map[string]*lsp.Document
13+
}
14+
15+
// Initialize implements lsp.Handler.
16+
func (h *Handler) Initialize(context.Context, lsp.ClientCapabilities) (*lsp.ServerCapabilities, error) {
17+
return &lsp.ServerCapabilities{
18+
TextDocumentSync: &lsp.TextDocumentSyncOptions{
19+
OpenClose: true,
20+
Change: lsp.SyncIncremental,
21+
},
22+
CompletionProvider: &lsp.CompletionOptions{},
23+
}, nil
24+
}
25+
26+
// DidOpenTextDocument implements lsp.Handler.
27+
func (h *Handler) DidOpenTextDocument(_ context.Context, params lsp.DidOpenTextDocumentParams) error {
28+
doc := lsp.NewDocument([]byte(params.TextDocument.Text), nil)
29+
if h.documents == nil {
30+
h.documents = make(map[string]*lsp.Document)
31+
}
32+
h.documents[params.TextDocument.URI] = doc
33+
return nil
34+
}
35+
36+
// DidCloseTextDocument implements lsp.Handler.
37+
func (h *Handler) DidCloseTextDocument(_ context.Context, params lsp.DidCloseTextDocumentParams) error {
38+
delete(h.documents, params.TextDocument.URI)
39+
return nil
40+
}
41+
42+
// DidChangeTextDocument implements lsp.Handler.
43+
func (h *Handler) DidChangeTextDocument(_ context.Context, params lsp.DidChangeTextDocumentParams) error {
44+
doc, ok := h.documents[params.TextDocument.URI]
45+
if !ok {
46+
return fmt.Errorf("document not found: %s", params.TextDocument.URI)
47+
}
48+
49+
for _, change := range params.ContentChanges {
50+
if err := doc.ApplyChange(change); err != nil {
51+
return err
52+
}
53+
}
54+
55+
return nil
56+
}
57+
58+
var _ lsp.Handler = &Handler{}

internal/ast/parser.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ func init() {
1212
parser = participle.MustBuild[File]()
1313
}
1414

15+
// Parse parses a .gdshader file into a tree of AST nodes.
1516
func Parse(filename string, reader io.Reader) (*File, error) {
1617
return parser.Parse(filename, reader, participle.AllowTrailing(true))
1718
}

internal/ast/types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,55 @@ package ast
22

33
import "github.com/alecthomas/participle/v2/lexer"
44

5+
// File is the root of a parsed .gdshader file.
56
type File struct {
67
Pos lexer.Position
78
Declarations []*Declaration `@@*`
89
}
910

11+
// Declaration is a top-level declaration.
1012
type Declaration struct {
1113
UniformDecl *UniformDecl `@@`
1214
FunctionDecl *FunctionDecl `| @@`
1315
}
1416

17+
// UniformDecl is a uniform variable declaration.
1518
type UniformDecl struct {
1619
Type string `"uniform" @Ident`
1720
Name string `@Ident ";"`
1821
}
1922

23+
// FunctionDecl is a function declaration.
2024
type FunctionDecl struct {
2125
ReturnType string `@Ident`
2226
Name string `@Ident "(" ")"`
2327
Body *BlockStmt `@@`
2428
}
2529

30+
// BlockStmt is a block of statements enclosed in braces.
2631
type BlockStmt struct {
2732
Stmts []*Stmt `"{" @@* "}"`
2833
}
2934

35+
// Stmt is a code statement.
3036
type Stmt struct {
3137
Assignment *Assignment `@@`
3238
}
3339

40+
// Assignment is an assignment statement.
3441
type Assignment struct {
3542
Left string `@Ident`
3643
Equals string `@"="`
3744
Expr *Expr `@@`
3845
Semi string `@";"`
3946
}
4047

48+
// Expr is an expression.
4149
type Expr struct {
4250
FuncCall *FuncCall `@@`
4351
}
4452

53+
// FuncCall is a function call exporession.
4554
type FuncCall struct {
4655
FuncName string `@Ident`
4756
LParen string `@"("`

0 commit comments

Comments
 (0)