ttsc 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -6
- package/cmd/ttsc/api_compile.go +101 -106
- package/cmd/ttsc/api_compile_branches_test.go +81 -0
- package/cmd/ttsc/api_transform.go +49 -54
- package/cmd/ttsc/api_transform_branches_test.go +41 -0
- package/cmd/ttsc/build.go +108 -109
- package/cmd/ttsc/build_branches_test.go +130 -0
- package/cmd/ttsc/helpers_test.go +47 -0
- package/cmd/ttsc/main.go +104 -103
- package/cmd/ttsc/main_alias_branches_test.go +29 -0
- package/cmd/ttscserver/main.go +104 -96
- package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +42 -0
- package/cmd/ttscserver/run_lsp_reports_server_error_test.go +23 -25
- package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +44 -0
- package/cmd/utility-host/main.go +42 -0
- package/driver/lsp.go +60 -0
- package/driver/plugins.go +122 -0
- package/driver/program.go +346 -356
- package/driver/rewrite.go +336 -339
- package/{driver → internal/lspserver}/lsp_envelope.go +70 -64
- package/{driver → internal/lspserver}/lsp_frame.go +62 -62
- package/{driver → internal/lspserver}/lsp_plugin.go +58 -58
- package/internal/lspserver/lsp_proxy.go +391 -0
- package/internal/lspserver/lsp_server.go +231 -0
- package/lib/TtscCompiler.d.ts +3 -3
- package/lib/TtscCompiler.js +20 -6
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.d.ts +1 -0
- package/lib/compiler/internal/resolveTsgo.js +15 -4
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.js +12 -4
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +12 -33
- package/lib/compiler/internal/sharedHostHelpers.js +27 -70
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +12 -5
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/index.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +9 -4
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.d.ts +1 -0
- package/lib/launcher/internal/runTtscserver.js +38 -1
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +3 -0
- package/lib/plugin/internal/buildSourcePlugin.js +444 -41
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +153 -34
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerContext.d.ts +2 -1
- package/lib/structures/ITtscPlugin.d.ts +8 -13
- package/lib/structures/ITtscPluginFactoryContext.d.ts +4 -5
- package/lib/structures/ITtscProjectPluginConfig.d.ts +2 -2
- package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +4 -2
- package/package.json +8 -8
- package/shim/lsp/shim.go +11 -17
- package/src/TtscCompiler.ts +20 -7
- package/src/compiler/internal/resolveTsgo.ts +18 -5
- package/src/compiler/internal/runBuild.ts +17 -4
- package/src/compiler/internal/sharedHostHelpers.ts +34 -67
- package/src/compiler/internal/transformProjectInMemory.ts +18 -5
- package/src/index.ts +0 -1
- package/src/launcher/internal/runTtsc.ts +9 -4
- package/src/launcher/internal/runTtscserver.ts +43 -1
- package/src/plugin/internal/buildSourcePlugin.ts +475 -41
- package/src/plugin/internal/loadProjectPlugins.ts +199 -39
- package/src/structures/ITtscCompilerContext.ts +2 -1
- package/src/structures/ITtscPlugin.ts +9 -13
- package/src/structures/ITtscPluginFactoryContext.ts +4 -5
- package/src/structures/ITtscProjectPluginConfig.ts +2 -2
- package/src/structures/internal/ITtscLoadedNativePlugin.ts +4 -2
- package/utility/host.go +267 -1296
- package/driver/lsp_proxy.go +0 -394
- package/driver/lsp_server.go +0 -197
- package/lib/defineTtscPlugin.d.ts +0 -18
- package/lib/defineTtscPlugin.js +0 -7
- package/lib/defineTtscPlugin.js.map +0 -1
- package/src/defineTtscPlugin.ts +0 -26
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
package
|
|
1
|
+
package lspserver
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"errors"
|
|
7
|
+
"fmt"
|
|
8
|
+
"strconv"
|
|
9
|
+
"strings"
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
// ErrInvalidJSONRPC is returned by ParseEnvelope when the body has a
|
|
@@ -19,12 +19,12 @@ var ErrInvalidJSONRPC = errors.New("lsp: jsonrpc field must be \"2.0\"")
|
|
|
19
19
|
// because LSP allows both numbers and strings and we must round-trip
|
|
20
20
|
// whichever shape the editor used in correlating responses.
|
|
21
21
|
type Envelope struct {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
JSONRPC string `json:"jsonrpc,omitempty"`
|
|
23
|
+
ID json.RawMessage `json:"id,omitempty"`
|
|
24
|
+
Method string `json:"method,omitempty"`
|
|
25
|
+
Params json.RawMessage `json:"params,omitempty"`
|
|
26
|
+
Result json.RawMessage `json:"result,omitempty"`
|
|
27
|
+
Error json.RawMessage `json:"error,omitempty"`
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// ParseEnvelope decodes the JSON-RPC envelope without consuming the
|
|
@@ -32,31 +32,31 @@ type Envelope struct {
|
|
|
32
32
|
// matches the LSP base protocol's forward-compatibility expectation. A
|
|
33
33
|
// non-empty `jsonrpc` value other than "2.0" is rejected.
|
|
34
34
|
func ParseEnvelope(body []byte) (Envelope, error) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
var env Envelope
|
|
36
|
+
if err := json.Unmarshal(body, &env); err != nil {
|
|
37
|
+
return Envelope{}, fmt.Errorf("lsp: envelope decode: %w", err)
|
|
38
|
+
}
|
|
39
|
+
if env.JSONRPC != "" && env.JSONRPC != "2.0" {
|
|
40
|
+
return Envelope{}, fmt.Errorf("%w (got %q)", ErrInvalidJSONRPC, env.JSONRPC)
|
|
41
|
+
}
|
|
42
|
+
return env, nil
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// IsRequest reports whether the envelope represents a request expecting
|
|
46
46
|
// a response (id present, method present).
|
|
47
47
|
func (e Envelope) IsRequest() bool {
|
|
48
|
-
|
|
48
|
+
return len(e.ID) > 0 && e.Method != ""
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// IsNotification reports whether the envelope is a one-way notification.
|
|
52
52
|
func (e Envelope) IsNotification() bool {
|
|
53
|
-
|
|
53
|
+
return len(e.ID) == 0 && e.Method != ""
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// IsResponse reports whether the envelope is a response (id present,
|
|
57
57
|
// method absent — either result or error will be set).
|
|
58
58
|
func (e Envelope) IsResponse() bool {
|
|
59
|
-
|
|
59
|
+
return len(e.ID) > 0 && e.Method == ""
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// IsErrorResponse reports whether a response envelope carries an error
|
|
@@ -64,7 +64,7 @@ func (e Envelope) IsResponse() bool {
|
|
|
64
64
|
// on the same response, so the proxy uses this to skip merging plugin
|
|
65
65
|
// contributions into upstream failures.
|
|
66
66
|
func (e Envelope) IsErrorResponse() bool {
|
|
67
|
-
|
|
67
|
+
return e.IsResponse() && len(e.Error) > 0 && !bytes.Equal(bytes.TrimSpace(e.Error), []byte("null"))
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// IDKey returns a stable string key for matching request and response
|
|
@@ -76,7 +76,13 @@ func (e Envelope) IsErrorResponse() bool {
|
|
|
76
76
|
// (LSP forbids these as request ids) produce the empty key, which
|
|
77
77
|
// callers treat as "no entry".
|
|
78
78
|
func (e Envelope) IDKey() string {
|
|
79
|
-
|
|
79
|
+
return idKeyFromRaw(e.ID)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// IDKeyFromRaw exposes the shared id-key normalizer to the public driver
|
|
83
|
+
// compatibility layer without exporting the helper from that package.
|
|
84
|
+
func IDKeyFromRaw(raw json.RawMessage) string {
|
|
85
|
+
return idKeyFromRaw(raw)
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
// idKeyFromRaw normalizes a raw id payload the same way IDKey does so
|
|
@@ -95,42 +101,42 @@ func (e Envelope) IDKey() string {
|
|
|
95
101
|
// (boolean, null, array, object — none of which LSP allows for ids)
|
|
96
102
|
// falls through to the empty key, which callers treat as "no entry".
|
|
97
103
|
func idKeyFromRaw(raw json.RawMessage) string {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
104
|
+
if len(raw) == 0 {
|
|
105
|
+
return ""
|
|
106
|
+
}
|
|
107
|
+
dec := json.NewDecoder(bytes.NewReader(raw))
|
|
108
|
+
dec.UseNumber()
|
|
109
|
+
var decoded any
|
|
110
|
+
if err := dec.Decode(&decoded); err != nil {
|
|
111
|
+
return ""
|
|
112
|
+
}
|
|
113
|
+
switch v := decoded.(type) {
|
|
114
|
+
case json.Number:
|
|
115
|
+
lit := v.String()
|
|
116
|
+
if !strings.ContainsAny(lit, ".eE") {
|
|
117
|
+
// Integer-shaped literal. Use int64 when it fits so leading
|
|
118
|
+
// zeros and minus signs canonicalize, but preserve the literal
|
|
119
|
+
// for past-int64 ids — Float64 normalization would silently
|
|
120
|
+
// round 9999999999999999998 and 9999999999999999999 to the
|
|
121
|
+
// same key.
|
|
122
|
+
if n, err := v.Int64(); err == nil {
|
|
123
|
+
return strconv.FormatInt(n, 10)
|
|
124
|
+
}
|
|
125
|
+
return lit
|
|
126
|
+
}
|
|
127
|
+
// Float-shaped literal (`1.0`, `1.5`, `1e2`). Collapse to integer
|
|
128
|
+
// form only when the value is exactly representable as int64;
|
|
129
|
+
// 2^53 bounds the safe range because float64 loses unit precision
|
|
130
|
+
// past that magnitude.
|
|
131
|
+
if f, err := v.Float64(); err == nil {
|
|
132
|
+
const maxSafeFloat = float64(int64(1) << 53)
|
|
133
|
+
if f >= -maxSafeFloat && f <= maxSafeFloat && f == float64(int64(f)) {
|
|
134
|
+
return strconv.FormatInt(int64(f), 10)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return lit
|
|
138
|
+
case string:
|
|
139
|
+
return strconv.Quote(v)
|
|
140
|
+
}
|
|
141
|
+
return ""
|
|
136
142
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// JSON-RPC Content-Length framing as used by LSP over stdio. ttscserver
|
|
2
|
-
//
|
|
2
|
+
// wraps tsgo's LSP server but sits between it and the editor so it can
|
|
3
3
|
// merge plugin diagnostics into outgoing publishDiagnostics and inject
|
|
4
4
|
// ttsc-owned code actions. Both legs of the proxy work on raw bytes.
|
|
5
5
|
//
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
// by WriteFrame include only Content-Length. That is intentional — the
|
|
9
9
|
// LSP base protocol marks every other header optional and editors in
|
|
10
10
|
// the wild do not require them on server output.
|
|
11
|
-
package
|
|
11
|
+
package lspserver
|
|
12
12
|
|
|
13
13
|
import (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
"bufio"
|
|
15
|
+
"errors"
|
|
16
|
+
"fmt"
|
|
17
|
+
"io"
|
|
18
|
+
"strconv"
|
|
19
|
+
"strings"
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
// ErrFrameClosed reports a clean EOF between frames. Callers use it to
|
|
@@ -40,69 +40,69 @@ const MaxFrameBytes = 64 << 20
|
|
|
40
40
|
// peer sent) and strict about the Content-Length value because tsgo's
|
|
41
41
|
// upstream writer always emits one.
|
|
42
42
|
type FrameReader struct {
|
|
43
|
-
|
|
43
|
+
br *bufio.Reader
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// NewFrameReader wraps r with an internal buffered reader. r is consumed
|
|
47
47
|
// lazily; the underlying reader is never closed by the FrameReader.
|
|
48
48
|
func NewFrameReader(r io.Reader) *FrameReader {
|
|
49
|
-
|
|
49
|
+
return &FrameReader{br: bufio.NewReader(r)}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Read returns the next message body together with the raw header block
|
|
53
53
|
// (without trailing CRLFCRLF). The header block is preserved so callers
|
|
54
54
|
// that proxy traffic verbatim do not lose Content-Type or vendor headers.
|
|
55
55
|
func (fr *FrameReader) Read() (headers string, body []byte, err error) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
56
|
+
var headerBuf strings.Builder
|
|
57
|
+
contentLength := -1
|
|
58
|
+
for {
|
|
59
|
+
line, lineErr := fr.br.ReadString('\n')
|
|
60
|
+
if lineErr != nil {
|
|
61
|
+
if lineErr == io.EOF && headerBuf.Len() == 0 && line == "" {
|
|
62
|
+
return "", nil, ErrFrameClosed
|
|
63
|
+
}
|
|
64
|
+
return "", nil, fmt.Errorf("lsp: header read: %w", lineErr)
|
|
65
|
+
}
|
|
66
|
+
trimmed := strings.TrimRight(line, "\r\n")
|
|
67
|
+
if trimmed == "" {
|
|
68
|
+
break
|
|
69
|
+
}
|
|
70
|
+
headerBuf.WriteString(line)
|
|
71
|
+
if value, ok := parseContentLength(trimmed); ok {
|
|
72
|
+
contentLength = value
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if contentLength < 0 {
|
|
76
|
+
return "", nil, errors.New("lsp: missing Content-Length header")
|
|
77
|
+
}
|
|
78
|
+
if contentLength > MaxFrameBytes {
|
|
79
|
+
return "", nil, fmt.Errorf("%w (got %d, max %d)", ErrFrameTooLarge, contentLength, MaxFrameBytes)
|
|
80
|
+
}
|
|
81
|
+
body = make([]byte, contentLength)
|
|
82
|
+
if _, err := io.ReadFull(fr.br, body); err != nil {
|
|
83
|
+
return "", nil, fmt.Errorf("lsp: body read: %w", err)
|
|
84
|
+
}
|
|
85
|
+
return headerBuf.String(), body, nil
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// parseContentLength extracts the integer value of a Content-Length header.
|
|
89
89
|
// Header names are case-insensitive per the LSP base protocol; tsgo emits
|
|
90
90
|
// "Content-Length" but some clients downcase, so we normalize here.
|
|
91
91
|
func parseContentLength(line string) (int, bool) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
92
|
+
colon := strings.IndexByte(line, ':')
|
|
93
|
+
if colon < 0 {
|
|
94
|
+
return 0, false
|
|
95
|
+
}
|
|
96
|
+
name := strings.TrimSpace(line[:colon])
|
|
97
|
+
if !strings.EqualFold(name, "Content-Length") {
|
|
98
|
+
return 0, false
|
|
99
|
+
}
|
|
100
|
+
value := strings.TrimSpace(line[colon+1:])
|
|
101
|
+
n, err := strconv.Atoi(value)
|
|
102
|
+
if err != nil || n < 0 {
|
|
103
|
+
return 0, false
|
|
104
|
+
}
|
|
105
|
+
return n, true
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
// WriteFrame serializes body with a Content-Length header to w. The header
|
|
@@ -110,12 +110,12 @@ func parseContentLength(line string) (int, bool) {
|
|
|
110
110
|
// that strict-parse the header block (notably VSCode's client) accept the
|
|
111
111
|
// message without warnings.
|
|
112
112
|
func WriteFrame(w io.Writer, body []byte) error {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
header := fmt.Sprintf("Content-Length: %d\r\n\r\n", len(body))
|
|
114
|
+
if _, err := io.WriteString(w, header); err != nil {
|
|
115
|
+
return fmt.Errorf("lsp: header write: %w", err)
|
|
116
|
+
}
|
|
117
|
+
if _, err := w.Write(body); err != nil {
|
|
118
|
+
return fmt.Errorf("lsp: body write: %w", err)
|
|
119
|
+
}
|
|
120
|
+
return nil
|
|
121
121
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package
|
|
1
|
+
package lspserver
|
|
2
2
|
|
|
3
3
|
import "encoding/json"
|
|
4
4
|
|
|
@@ -7,14 +7,14 @@ import "encoding/json"
|
|
|
7
7
|
// surface stays narrow; the merger marshals them straight into the array
|
|
8
8
|
// the editor already expects.
|
|
9
9
|
type LSPPosition struct {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Line int `json:"line"`
|
|
11
|
+
Character int `json:"character"`
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
// LSPRange is the closed-open [start, end) interval LSP uses for ranges.
|
|
15
15
|
type LSPRange struct {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Start LSPPosition `json:"start"`
|
|
17
|
+
End LSPPosition `json:"end"`
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// LSPDiagnosticSeverity values match the LSP enum exactly so editors
|
|
@@ -22,66 +22,66 @@ type LSPRange struct {
|
|
|
22
22
|
type LSPDiagnosticSeverity int
|
|
23
23
|
|
|
24
24
|
const (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
// LSPDiagnosticSeverityError is the most prominent severity; ttsc maps
|
|
26
|
+
// build-blocking findings (lint errors, parse errors) to it.
|
|
27
|
+
LSPDiagnosticSeverityError LSPDiagnosticSeverity = 1
|
|
28
|
+
// LSPDiagnosticSeverityWarning is rendered as a yellow squiggle in
|
|
29
|
+
// typical editor themes; ttsc maps lint warnings to it.
|
|
30
|
+
LSPDiagnosticSeverityWarning LSPDiagnosticSeverity = 2
|
|
31
|
+
// LSPDiagnosticSeverityInformation is rendered as a blue squiggle.
|
|
32
|
+
LSPDiagnosticSeverityInformation LSPDiagnosticSeverity = 3
|
|
33
|
+
// LSPDiagnosticSeverityHint is rendered as a faint underline or dots.
|
|
34
|
+
LSPDiagnosticSeverityHint LSPDiagnosticSeverity = 4
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
// LSPDiagnostic is the minimal subset of the LSP Diagnostic type
|
|
38
38
|
// ttscserver injects into outgoing publishDiagnostics. omitempty is set
|
|
39
39
|
// on optional fields so the merged JSON stays close to what tsgo emits.
|
|
40
40
|
type LSPDiagnostic struct {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
Range LSPRange `json:"range"`
|
|
42
|
+
Severity LSPDiagnosticSeverity `json:"severity,omitempty"`
|
|
43
|
+
Code any `json:"code,omitempty"`
|
|
44
|
+
Source string `json:"source,omitempty"`
|
|
45
|
+
Message string `json:"message"`
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// LSPCodeAction is the minimal Code Action shape ttscserver returns from
|
|
49
49
|
// the textDocument/codeAction handler. ttsc actions are always command-
|
|
50
50
|
// driven (the server side does the heavy lifting) so Edit stays nil.
|
|
51
51
|
type LSPCodeAction struct {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
Title string `json:"title"`
|
|
53
|
+
Kind string `json:"kind,omitempty"`
|
|
54
|
+
Command *LSPCommand `json:"command,omitempty"`
|
|
55
|
+
Edit json.RawMessage `json:"edit,omitempty"`
|
|
56
|
+
IsPreferred bool `json:"isPreferred,omitempty"`
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// LSPCommand is the wire shape of a workspace/executeCommand target.
|
|
60
60
|
type LSPCommand struct {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
Title string `json:"title"`
|
|
62
|
+
Command string `json:"command"`
|
|
63
|
+
Arguments []json.RawMessage `json:"arguments,omitempty"`
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// LSPCodeActionContext mirrors the context object the editor sends with
|
|
67
67
|
// textDocument/codeAction. Only the diagnostics slice is consumed by
|
|
68
68
|
// ttsc; the rest is opaque and forwarded by Proxy verbatim.
|
|
69
69
|
type LSPCodeActionContext struct {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
Diagnostics []json.RawMessage `json:"diagnostics,omitempty"`
|
|
71
|
+
Only []string `json:"only,omitempty"`
|
|
72
|
+
TriggerKind int `json:"triggerKind,omitempty"`
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// LSPWorkspaceEdit is the wire shape ttscserver returns from custom
|
|
76
76
|
// executeCommand handlers. It maps URIs to ordered text edits.
|
|
77
77
|
type LSPWorkspaceEdit struct {
|
|
78
|
-
|
|
78
|
+
Changes map[string][]LSPTextEdit `json:"changes,omitempty"`
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// LSPTextEdit is a single text edit in a workspace edit.
|
|
82
82
|
type LSPTextEdit struct {
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
Range LSPRange `json:"range"`
|
|
84
|
+
NewText string `json:"newText"`
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
// LSPDocumentVersion carries the LSP textDocument version associated
|
|
@@ -93,8 +93,8 @@ type LSPTextEdit struct {
|
|
|
93
93
|
// Version is nil when upstream omitted the field — that is legal in
|
|
94
94
|
// LSP and the plugin source should treat it as "version unknown".
|
|
95
95
|
type LSPDocumentVersion struct {
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
URI string
|
|
97
|
+
Version *int
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// PluginSource is the seam between the LSP proxy and ttsc's plugin
|
|
@@ -104,26 +104,26 @@ type LSPDocumentVersion struct {
|
|
|
104
104
|
// The proxy never holds a PluginSource lock across upstream traffic, so
|
|
105
105
|
// implementations must be safe to call from multiple goroutines.
|
|
106
106
|
type PluginSource interface {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
107
|
+
// Diagnostics returns ttsc plugin diagnostics for the document the
|
|
108
|
+
// proxy is about to publish. doc.Version is nil when upstream omitted
|
|
109
|
+
// the field. The proxy appends these to whatever upstream tsgo
|
|
110
|
+
// published, so duplicates between ttsc and tsgo must be deduplicated
|
|
111
|
+
// on the source side.
|
|
112
|
+
Diagnostics(doc LSPDocumentVersion) []LSPDiagnostic
|
|
113
|
+
|
|
114
|
+
// CodeActions contributes additional actions for the given range. The
|
|
115
|
+
// proxy appends them to the upstream tsgo response.
|
|
116
|
+
CodeActions(uri string, rng LSPRange, ctx LSPCodeActionContext) []LSPCodeAction
|
|
117
|
+
|
|
118
|
+
// ExecuteCommand handles workspace/executeCommand requests whose
|
|
119
|
+
// command id appears in CommandIDs. A nil edit
|
|
120
|
+
// means the command ran but produced no workspace changes; a non-nil
|
|
121
|
+
// error surfaces as an LSP error response.
|
|
122
|
+
ExecuteCommand(command string, args []json.RawMessage) (*LSPWorkspaceEdit, error)
|
|
123
|
+
|
|
124
|
+
// CommandIDs lists the workspace command ids ttsc handles locally so
|
|
125
|
+
// the proxy never forwards them to upstream tsgo.
|
|
126
|
+
CommandIDs() []string
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// NullPluginSource is the zero-contribution PluginSource used when the
|
|
@@ -137,12 +137,12 @@ func (NullPluginSource) Diagnostics(LSPDocumentVersion) []LSPDiagnostic { return
|
|
|
137
137
|
|
|
138
138
|
// CodeActions returns no plugin code actions.
|
|
139
139
|
func (NullPluginSource) CodeActions(string, LSPRange, LSPCodeActionContext) []LSPCodeAction {
|
|
140
|
-
|
|
140
|
+
return nil
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// ExecuteCommand reports that the command is not handled.
|
|
144
144
|
func (NullPluginSource) ExecuteCommand(string, []json.RawMessage) (*LSPWorkspaceEdit, error) {
|
|
145
|
-
|
|
145
|
+
return nil, ErrCommandNotHandled
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
// CommandIDs returns an empty slice so the proxy forwards every command
|