Skip to content

Commit 118bdcd

Browse files
committed
add folding provider
1 parent 8b7fd98 commit 118bdcd

File tree

12 files changed

+323
-14
lines changed

12 files changed

+323
-14
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## v0.1.3 - 2020-12-09
8+
9+
### Added
10+
- Event folding
11+
12+
### Fixed
13+
- Fixed bug where a completion request won't throw an error even if it found one
14+
715
## v0.1.2 - 2020-11-28
816

917
### Fixed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ stdio.
1313
Please note that this language server is currently not 100% stable, so bugs may
1414
appear here and there.
1515

16+
## Features
17+
18+
- Hover information
19+
- Diagnostics
20+
- Event duplication checking
21+
- Command usage validation
22+
- Message box text overflow checking
23+
- Completions
24+
- Event folding
25+
1626
## Getting Started
1727

1828
Language clients should usually install the language server for you

langserver/handlers/completion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func TextDocumentCompletion(ctx context.Context, _ *jrpc2.Request) ([]lsp.Comple
1515
conf, err := lsctx.Config(ctx)
1616

1717
if err != nil {
18-
return []lsp.CompletionItem{}, nil
18+
return []lsp.CompletionItem{}, err
1919
}
2020

2121
completions := tsc.GetCompletions(conf)

langserver/handlers/folding.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package handlers
2+
3+
import (
4+
"context"
5+
6+
"github.com/creachadair/jrpc2"
7+
"github.com/sourcegraph/go-lsp"
8+
lsctx "pkg.nimblebun.works/tsc-language-server/langserver/context"
9+
"pkg.nimblebun.works/tsc-language-server/langserver/filesystem/filehandler"
10+
"pkg.nimblebun.works/tsc-language-server/tsc"
11+
)
12+
13+
// FoldingRangeParams is a (loosely reproduced) structure that contains the
14+
// fields sent on a textDocument/foldingRange request.
15+
type FoldingRangeParams struct {
16+
TextDocument lsp.TextDocumentIdentifier `json:"textDocument"`
17+
}
18+
19+
// TextDocumentFoldingRange is the callback that runs on the
20+
// "textDocument/foldingRange" method.
21+
func (mh *MethodHandler) TextDocumentFoldingRange(ctx context.Context, req *jrpc2.Request) ([]tsc.FoldingRange, error) {
22+
var params FoldingRangeParams
23+
err := req.UnmarshalParams(jrpc2.NonStrict(&params))
24+
if err != nil {
25+
return []tsc.FoldingRange{}, err
26+
}
27+
28+
fs, err := lsctx.FileSystem(ctx)
29+
if err != nil {
30+
return []tsc.FoldingRange{}, err
31+
}
32+
33+
handler := filehandler.FromDocumentURI(params.TextDocument.URI)
34+
path, err := handler.FullPath()
35+
if err != nil {
36+
return []tsc.FoldingRange{}, err
37+
}
38+
39+
contents, err := fs.ReadFile(path)
40+
if err != nil {
41+
return []tsc.FoldingRange{}, err
42+
}
43+
44+
doc := lsp.TextDocumentItem{
45+
URI: params.TextDocument.URI,
46+
LanguageID: "tsc",
47+
Text: string(contents),
48+
}
49+
50+
ranges := tsc.GetFoldingRanges(doc)
51+
return ranges, nil
52+
}

langserver/handlers/initialize.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,48 @@ import (
77
"github.com/sourcegraph/go-lsp"
88
)
99

10+
// ServerCapabilities is a temporary workaround for the missing folding range
11+
// provider field in Sourcegraph's go-lsp package. We will work on an in-house
12+
// version of the LSP implementation in Go in the future.
13+
type ServerCapabilities struct {
14+
TextDocumentSync *lsp.TextDocumentSyncOptionsOrKind `json:"textDocumentSync,omitempty"`
15+
HoverProvider bool `json:"hoverProvider,omitempty"`
16+
FoldingRangeProvider bool `json:"foldingRangeProvider,omitempty"`
17+
CompletionProvider *lsp.CompletionOptions `json:"completionProvider,omitempty"`
18+
SignatureHelpProvider *lsp.SignatureHelpOptions `json:"signatureHelpProvider,omitempty"`
19+
DefinitionProvider bool `json:"definitionProvider,omitempty"`
20+
TypeDefinitionProvider bool `json:"typeDefinitionProvider,omitempty"`
21+
ReferencesProvider bool `json:"referencesProvider,omitempty"`
22+
DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"`
23+
DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"`
24+
WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"`
25+
ImplementationProvider bool `json:"implementationProvider,omitempty"`
26+
CodeActionProvider bool `json:"codeActionProvider,omitempty"`
27+
CodeLensProvider *lsp.CodeLensOptions `json:"codeLensProvider,omitempty"`
28+
DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"`
29+
DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"`
30+
DocumentOnTypeFormattingProvider *lsp.DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"`
31+
RenameProvider bool `json:"renameProvider,omitempty"`
32+
ExecuteCommandProvider *lsp.ExecuteCommandOptions `json:"executeCommandProvider,omitempty"`
33+
SemanticHighlighting *lsp.SemanticHighlightingOptions `json:"semanticHighlighting,omitempty"`
34+
XWorkspaceReferencesProvider bool `json:"xworkspaceReferencesProvider,omitempty"`
35+
XDefinitionProvider bool `json:"xdefinitionProvider,omitempty"`
36+
XWorkspaceSymbolByProperties bool `json:"xworkspaceSymbolByProperties,omitempty"`
37+
38+
Experimental interface{} `json:"experimental,omitempty"`
39+
}
40+
41+
// InitializeResult is a temporary workaround for the missing folding range
42+
// provider field in Sourcegraph's go-lsp package. We will work on an in-house
43+
// version of the LSP implementation in Go in the future.
44+
type InitializeResult struct {
45+
Capabilities ServerCapabilities `json:"capabilities"`
46+
}
47+
1048
// Initialize is the callback that runs on the "initialize" method
11-
func (mh *MethodHandler) Initialize(ctx context.Context, _ *jrpc2.Request) (lsp.InitializeResult, error) {
12-
result := lsp.InitializeResult{
13-
Capabilities: lsp.ServerCapabilities{
49+
func (mh *MethodHandler) Initialize(ctx context.Context, _ *jrpc2.Request) (InitializeResult, error) {
50+
result := InitializeResult{
51+
Capabilities: ServerCapabilities{
1452
TextDocumentSync: &lsp.TextDocumentSyncOptionsOrKind{
1553
Options: &lsp.TextDocumentSyncOptions{
1654
OpenClose: true,
@@ -20,7 +58,8 @@ func (mh *MethodHandler) Initialize(ctx context.Context, _ *jrpc2.Request) (lsp.
2058
CompletionProvider: &lsp.CompletionOptions{
2159
ResolveProvider: false,
2260
},
23-
HoverProvider: true,
61+
HoverProvider: true,
62+
FoldingRangeProvider: true,
2463
},
2564
}
2665

langserver/handlers/service.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,17 @@ func (service *Service) Assigner() (jrpc2.Assigner, error) {
156156
return handle(ctx, req, mh.TextDocumentHover)
157157
},
158158

159+
"textDocument/foldingRange": func(ctx context.Context, req *jrpc2.Request) (interface{}, error) {
160+
err := sess.EnsureInitialized()
161+
if err != nil {
162+
return nil, err
163+
}
164+
165+
ctx = lsctx.WithFileSystem(ctx, fs)
166+
167+
return handle(ctx, req, mh.TextDocumentFoldingRange)
168+
},
169+
159170
"tsc/setConfig": func(ctx context.Context, req *jrpc2.Request) (interface{}, error) {
160171
err := sess.EnsureInitialized()
161172
if err != nil {

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ func main() {
1212
app := cli.NewApp()
1313
app.Name = "tsc-ls"
1414
app.Usage = "language Server for the TSC scripting language"
15-
app.Version = "0.1.2"
15+
app.Version = "0.1.3"
1616

1717
app.Commands = []*cli.Command{
1818
{

tsc/completions_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
package tsc
1+
package tsc_test
22

33
import (
44
"testing"
55

66
"github.com/sourcegraph/go-lsp"
77
"pkg.nimblebun.works/tsc-language-server/config"
8+
"pkg.nimblebun.works/tsc-language-server/tsc"
89
)
910

1011
func TestGetCompletions(t *testing.T) {
1112
conf := config.New()
12-
completions := GetCompletions(&conf)
13+
completions := tsc.GetCompletions(&conf)
1314

1415
definitionsLength := len(conf.GetTSCDefinitions())
1516
completionsLength := len(completions)

tsc/folding.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package tsc
2+
3+
import (
4+
"github.com/sourcegraph/go-lsp"
5+
"pkg.nimblebun.works/tsc-language-server/langserver/textdocument"
6+
"pkg.nimblebun.works/tsc-language-server/utils"
7+
)
8+
9+
const (
10+
// FRKComment resolves to the "comment" folding range kind.
11+
FRKComment = "comment"
12+
13+
// FRKImports resolves to the "imports" folding range kind.
14+
FRKImports = "imports"
15+
16+
// FRKRegion resolves to the "region" folding range kind.
17+
FRKRegion = "region"
18+
)
19+
20+
// FoldingRange defines a LSP-compatible folding range.
21+
type FoldingRange struct {
22+
StartLine int `json:"startLine"`
23+
StartCharacter int `json:"startCharacter,omitempty"`
24+
EndLine int `json:"endLine"`
25+
EndCharacter int `json:"endCharacter"`
26+
27+
Kind string `json:"kind,omitempty"`
28+
}
29+
30+
func createFoldingRange(start int, end int, document textdocument.TextDocument) FoldingRange {
31+
startPos := document.PositionAt(start)
32+
endPos := document.PositionAt(end)
33+
34+
return FoldingRange{
35+
StartLine: startPos.Line,
36+
StartCharacter: startPos.Character,
37+
38+
EndLine: endPos.Line,
39+
EndCharacter: endPos.Character,
40+
41+
Kind: FRKRegion,
42+
}
43+
}
44+
45+
// GetFoldingRanges will return all foldable ranges from a given document.
46+
func GetFoldingRanges(textDocumentItem lsp.TextDocumentItem) []FoldingRange {
47+
text := textDocumentItem.Text
48+
49+
document := textdocument.From(textDocumentItem)
50+
ranges := make([]FoldingRange, 0)
51+
52+
start := -1
53+
end := -1
54+
55+
for idx, letter := range text {
56+
if letter == '#' && IsEvent(utils.Substring(text, idx, 5)) {
57+
if end-start > 4 {
58+
foldingRange := createFoldingRange(start, end, document)
59+
ranges = append(ranges, foldingRange)
60+
}
61+
62+
start = idx
63+
}
64+
65+
if letter != '\n' && letter != '\r' {
66+
end = idx
67+
}
68+
}
69+
70+
if end-start > 4 {
71+
foldingRange := createFoldingRange(start, end, document)
72+
ranges = append(ranges, foldingRange)
73+
}
74+
75+
return ranges
76+
}

tsc/folding_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package tsc_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/sourcegraph/go-lsp"
7+
"pkg.nimblebun.works/tsc-language-server/tsc"
8+
)
9+
10+
func TestGetFoldingRanges(t *testing.T) {
11+
dummyData := `#0098
12+
<CNP0306:0117:0000<ANP0306:0032:0002<END
13+
14+
#0100
15+
<KEY<FLJ0839:0101
16+
<SOU0011<ANP0100:0000:0002
17+
<FAO0000<TRA0046:0090:0017:0009
18+
19+
#0101
20+
<KEY<MSGIt won't open...<NOD<END
21+
22+
#0200
23+
<KEY
24+
<FLJ0832:0204
25+
<FLJ0824:0203
26+
<FLJ0823:0202
27+
<FLJ0821:0201<MSG
28+
OPEN SHUTTER?<YNJ0000<CLR
29+
OPENING SHUTTER<NOD<CLO<MYD0000
30+
<WAI0030<ANP0250:0010:0001<WAI0010<ANP0300:0001:0002
31+
<WAI0022<ANP0251:0010:0001<ANP0300:0003:0002
32+
<WAI0032<ANP0252:0010:0001
33+
<WAI0032<ANP0253:0010:0001
34+
<WAI0032<ANP0254:0010:0001<DNP0250
35+
<WAI0032<DNP0251
36+
<WAI0032<DNP0252
37+
<ANP0253:0001:0000<WAI0032<DNP0300
38+
<CNP0301:0117:0000
39+
<ANP0301:0021:0002
40+
<FL-0820<FL+0821<FL+0822<MSGABNORMALITY DETECTED IN
41+
SHUTTER NO. 4<NOD<END`
42+
43+
dummyDocument := lsp.TextDocumentItem{
44+
Text: dummyData,
45+
}
46+
47+
t.Run("should return correct number of folding ranges", func(t *testing.T) {
48+
ranges := tsc.GetFoldingRanges(dummyDocument)
49+
actual := len(ranges)
50+
expected := 4
51+
52+
if expected != actual {
53+
t.Errorf("GetFoldingRanges(document) length, got %d, want %d", actual, expected)
54+
}
55+
})
56+
57+
t.Run("should return correct ranges", func(t *testing.T) {
58+
ranges := tsc.GetFoldingRanges(dummyDocument)
59+
expectedRanges := []tsc.FoldingRange{
60+
{
61+
StartLine: 0,
62+
StartCharacter: 0,
63+
EndLine: 1,
64+
EndCharacter: 39,
65+
},
66+
{
67+
StartLine: 3,
68+
StartCharacter: 0,
69+
EndLine: 6,
70+
EndCharacter: 30,
71+
},
72+
{
73+
StartLine: 8,
74+
StartCharacter: 0,
75+
EndLine: 9,
76+
EndCharacter: 31,
77+
},
78+
{
79+
StartLine: 11,
80+
StartCharacter: 0,
81+
EndLine: 30,
82+
EndCharacter: 20,
83+
},
84+
}
85+
86+
for idx := range ranges {
87+
actual := ranges[idx]
88+
expected := expectedRanges[idx]
89+
90+
if actual.StartLine != expected.StartLine {
91+
t.Errorf("GetFoldingRanges(doc) @ %d -> StartLine, got %v, want %v", idx, actual.StartLine, expected.StartLine)
92+
}
93+
94+
if actual.StartCharacter != expected.StartCharacter {
95+
t.Errorf("GetFoldingRanges(doc) @ %d -> StartCharacter, got %v, want %v", idx, actual.StartCharacter, expected.StartCharacter)
96+
}
97+
98+
if actual.EndLine != expected.EndLine {
99+
t.Errorf("GetFoldingRanges(doc) @ %d -> EndLine, got %v, want %v", idx, actual.EndLine, expected.EndLine)
100+
}
101+
102+
if actual.EndCharacter != expected.EndCharacter {
103+
t.Errorf("GetFoldingRanges(doc) @ %d -> EndCharacter, got %v, want %v", idx, actual.EndCharacter, expected.EndCharacter)
104+
}
105+
}
106+
})
107+
}

0 commit comments

Comments
 (0)