ttsc 0.12.3 → 0.12.4
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/cmd/platform/main.go +9 -0
- package/cmd/ttsc/api_compile.go +109 -101
- package/cmd/ttsc/api_compile_branches_test.go +55 -57
- package/cmd/ttsc/api_transform.go +54 -49
- package/cmd/ttsc/api_transform_branches_test.go +30 -32
- package/cmd/ttsc/build.go +111 -108
- package/cmd/ttsc/build_branches_test.go +95 -97
- package/cmd/ttsc/helpers_test.go +39 -28
- package/cmd/ttsc/main.go +111 -104
- package/cmd/ttsc/main_alias_branches_test.go +21 -23
- package/cmd/ttscserver/main.go +96 -90
- package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +22 -22
- package/cmd/ttscserver/run_lsp_reports_server_error_test.go +22 -22
- package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +23 -23
- package/cmd/utility-host/main.go +30 -23
- package/driver/lsp.go +53 -7
- package/driver/plugins.go +88 -72
- package/driver/program.go +362 -339
- package/driver/rewrite.go +442 -380
- package/internal/lspserver/lsp_envelope.go +64 -64
- package/internal/lspserver/lsp_frame.go +60 -60
- package/internal/lspserver/lsp_plugin.go +57 -57
- package/internal/lspserver/lsp_proxy.go +265 -251
- package/internal/lspserver/lsp_server.go +155 -144
- package/lib/TtscCompiler.d.ts +7 -0
- package/lib/TtscCompiler.js +7 -0
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/buildNativeCompiler.d.ts +11 -1
- package/lib/compiler/internal/buildNativeCompiler.js +23 -3
- package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.d.ts +9 -0
- package/lib/compiler/internal/compileProjectInMemory.js +36 -21
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/paths.d.ts +27 -0
- package/lib/compiler/internal/paths.js +70 -0
- package/lib/compiler/internal/paths.js.map +1 -0
- package/lib/compiler/internal/project/readProjectConfig.d.ts +9 -1
- package/lib/compiler/internal/project/readProjectConfig.js +80 -1
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/project/resolveProjectConfig.d.ts +13 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js +24 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
- package/lib/compiler/internal/resolveBinary.d.ts +9 -1
- package/lib/compiler/internal/resolveBinary.js +17 -1
- package/lib/compiler/internal/resolveBinary.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +16 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.js +42 -10
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.d.ts +21 -1
- package/lib/compiler/internal/resolveTsgo.js +23 -1
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +19 -0
- package/lib/compiler/internal/runBuild.js +112 -46
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.d.ts +10 -1
- package/lib/compiler/internal/runSingleFileEmit.js +14 -1
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +5 -0
- package/lib/compiler/internal/sharedHostHelpers.js +6 -2
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/spawnNative.d.ts +21 -0
- package/lib/compiler/internal/spawnNative.js +59 -0
- package/lib/compiler/internal/spawnNative.js.map +1 -0
- package/lib/compiler/internal/transformProjectInMemory.d.ts +15 -1
- package/lib/compiler/internal/transformProjectInMemory.js +57 -61
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/launcher/internal/getCompilerVersionText.js +7 -0
- package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +32 -0
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/resolveCacheDir.d.ts +9 -0
- package/lib/launcher/internal/resolveCacheDir.js +22 -0
- package/lib/launcher/internal/resolveCacheDir.js.map +1 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js +2 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js.map +1 -1
- package/lib/launcher/internal/runTtsc.d.ts +10 -0
- package/lib/launcher/internal/runTtsc.js +22 -9
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +10 -35
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.d.ts +8 -0
- package/lib/launcher/internal/runTtsx.js +33 -7
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +38 -0
- package/lib/plugin/internal/buildSourcePlugin.js +40 -17
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +26 -0
- package/lib/plugin/internal/loadProjectPlugins.js +32 -18
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +15 -2
- package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +0 -2
- package/package.json +9 -9
- package/shim/ast/shim.go +24 -0
- package/shim/checker/shim.go +35 -0
- package/shim/core/shim.go +17 -0
- package/shim/diagnosticwriter/shim.go +4 -0
- package/shim/lsp/shim.go +3 -3
- package/shim/printer/shim.go +20 -0
- package/src/TtscCompiler.ts +7 -0
- package/src/compiler/internal/buildNativeCompiler.ts +23 -3
- package/src/compiler/internal/compileProjectInMemory.ts +34 -25
- package/src/compiler/internal/paths.ts +68 -0
- package/src/compiler/internal/project/readProjectConfig.ts +86 -1
- package/src/compiler/internal/project/resolveProjectConfig.ts +24 -1
- package/src/compiler/internal/resolveBinary.ts +17 -1
- package/src/compiler/internal/resolveEmittedJavaScript.ts +43 -12
- package/src/compiler/internal/resolveTsgo.ts +29 -1
- package/src/compiler/internal/runBuild.ts +110 -55
- package/src/compiler/internal/runSingleFileEmit.ts +14 -1
- package/src/compiler/internal/sharedHostHelpers.ts +7 -7
- package/src/compiler/internal/spawnNative.ts +64 -0
- package/src/compiler/internal/transformProjectInMemory.ts +47 -67
- package/src/launcher/internal/getCompilerVersionText.ts +7 -0
- package/src/launcher/internal/prepareExecution.ts +32 -0
- package/src/launcher/internal/resolveCacheDir.ts +19 -0
- package/src/launcher/internal/resolveTtscserverBinary.ts +2 -0
- package/src/launcher/internal/runTtsc.ts +22 -10
- package/src/launcher/internal/runTtscserver.ts +9 -1
- package/src/launcher/internal/runTtsx.ts +33 -8
- package/src/plugin/internal/buildSourcePlugin.ts +40 -14
- package/src/plugin/internal/loadProjectPlugins.ts +42 -18
- package/src/structures/ITtscCompilerTransformation.ts +15 -2
- package/src/structures/ITtscPlugin.ts +0 -1
- package/src/structures/internal/TtscSingleFileEmitOptions.ts +0 -2
- package/tools/gen_shims/main.go +43 -8
- package/utility/host.go +294 -245
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
package lspserver
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
"bytes"
|
|
5
|
+
"context"
|
|
6
|
+
"encoding/json"
|
|
7
|
+
"errors"
|
|
8
|
+
"fmt"
|
|
9
|
+
"io"
|
|
10
|
+
"sync"
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
// ErrCommandNotHandled is returned by PluginSource.ExecuteCommand for
|
|
@@ -15,60 +15,60 @@ import (
|
|
|
15
15
|
var ErrCommandNotHandled = errors.New("lsp: command not handled by ttsc")
|
|
16
16
|
|
|
17
17
|
const (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
methodPublishDiagnostics = "textDocument/publishDiagnostics"
|
|
19
|
+
methodCodeAction = "textDocument/codeAction"
|
|
20
|
+
methodExecuteCommand = "workspace/executeCommand"
|
|
21
|
+
methodCancelRequest = "$/cancelRequest"
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
// ProxyOptions wires the byte-level proxy together. ttscserver creates
|
|
25
25
|
// the upstream pipes around `tsgo --lsp --stdio` and hands the proxy
|
|
26
26
|
// editor stdio plus those pipe ends.
|
|
27
27
|
type ProxyOptions struct {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
EditorIn io.Reader
|
|
29
|
+
EditorOut io.Writer
|
|
30
|
+
UpstreamIn io.Writer // we write here; the tsgo LSP process reads
|
|
31
|
+
UpstreamOut io.Reader // the tsgo LSP process writes here; we read
|
|
32
|
+
Source PluginSource
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// Proxy bridges the editor and an upstream tsgo LSP process, intercepting
|
|
36
36
|
// the message types ttsc cares about (publishDiagnostics merge, code
|
|
37
37
|
// action augmentation, executeCommand for ttsc-owned commands).
|
|
38
38
|
type Proxy struct {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
editorIn io.Reader
|
|
40
|
+
editorOut io.Writer
|
|
41
|
+
upstreamIn io.Writer
|
|
42
|
+
upstreamOut io.Reader
|
|
43
|
+
source PluginSource
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
writeMu sync.Mutex // serializes WriteFrame calls to editorOut
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
pendingMu sync.Mutex
|
|
48
|
+
pendingActions map[string]pendingCodeActionRequest
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
type pendingCodeActionRequest struct {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
uri string
|
|
53
|
+
rng LSPRange
|
|
54
|
+
ctx LSPCodeActionContext
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// NewProxy returns a Proxy ready to Run. The PluginSource is required;
|
|
58
58
|
// pass NullPluginSource{} for a no-contribution setup.
|
|
59
59
|
func NewProxy(opts ProxyOptions) *Proxy {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
source := opts.Source
|
|
61
|
+
if source == nil {
|
|
62
|
+
source = NullPluginSource{}
|
|
63
|
+
}
|
|
64
|
+
return &Proxy{
|
|
65
|
+
editorIn: opts.EditorIn,
|
|
66
|
+
editorOut: opts.EditorOut,
|
|
67
|
+
upstreamIn: opts.UpstreamIn,
|
|
68
|
+
upstreamOut: opts.UpstreamOut,
|
|
69
|
+
source: source,
|
|
70
|
+
pendingActions: make(map[string]pendingCodeActionRequest),
|
|
71
|
+
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// Run drives both pump goroutines until they return. Pumps return when
|
|
@@ -77,18 +77,18 @@ func NewProxy(opts ProxyOptions) *Proxy {
|
|
|
77
77
|
// pipe write fails. ErrFrameClosed and context.Canceled are folded into
|
|
78
78
|
// a nil result so editor shutdown does not look like a crash.
|
|
79
79
|
func (p *Proxy) Run(ctx context.Context) error {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
errCh := make(chan error, 2)
|
|
81
|
+
go func() { errCh <- p.pumpEditorToUpstream(ctx) }()
|
|
82
|
+
go func() { errCh <- p.pumpUpstreamToEditor(ctx) }()
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
var first error
|
|
85
|
+
for i := 0; i < 2; i++ {
|
|
86
|
+
err := <-errCh
|
|
87
|
+
if first == nil && err != nil && !errors.Is(err, ErrFrameClosed) {
|
|
88
|
+
first = err
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return first
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// pumpEditorToUpstream reads frames from the editor, decides whether to
|
|
@@ -98,33 +98,33 @@ func (p *Proxy) Run(ctx context.Context) error {
|
|
|
98
98
|
// writer so tsgo's server-side Read returns EOF and its Run loop drains;
|
|
99
99
|
// without that nudge tsgo would wait forever for more input.
|
|
100
100
|
func (p *Proxy) pumpEditorToUpstream(_ context.Context) error {
|
|
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
|
-
|
|
101
|
+
fr := NewFrameReader(p.editorIn)
|
|
102
|
+
for {
|
|
103
|
+
_, body, err := fr.Read()
|
|
104
|
+
if err != nil {
|
|
105
|
+
if errors.Is(err, ErrFrameClosed) {
|
|
106
|
+
p.closeUpstreamInput()
|
|
107
|
+
}
|
|
108
|
+
return err
|
|
109
|
+
}
|
|
110
|
+
env, parseErr := ParseEnvelope(body)
|
|
111
|
+
if parseErr != nil {
|
|
112
|
+
if forwardErr := WriteFrame(p.upstreamIn, body); forwardErr != nil {
|
|
113
|
+
return forwardErr
|
|
114
|
+
}
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
handled, handleErr := p.handleEditorEnvelope(env)
|
|
118
|
+
if handleErr != nil {
|
|
119
|
+
return handleErr
|
|
120
|
+
}
|
|
121
|
+
if handled {
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
if err := WriteFrame(p.upstreamIn, body); err != nil {
|
|
125
|
+
return err
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
// closeUpstreamInput closes the upstream writer (if it is also an
|
|
@@ -132,31 +132,31 @@ func (p *Proxy) pumpEditorToUpstream(_ context.Context) error {
|
|
|
132
132
|
// We type-assert because the public ProxyOptions surface promises only
|
|
133
133
|
// io.Writer; RunLSPServer always passes an *io.PipeWriter in practice.
|
|
134
134
|
func (p *Proxy) closeUpstreamInput() {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
if closer, ok := p.upstreamIn.(io.Closer); ok {
|
|
136
|
+
_ = closer.Close()
|
|
137
|
+
}
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
// handleEditorEnvelope returns true if the envelope was fully processed
|
|
141
141
|
// locally (responded to without forwarding upstream).
|
|
142
142
|
func (p *Proxy) handleEditorEnvelope(env Envelope) (bool, error) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
143
|
+
switch env.Method {
|
|
144
|
+
case methodExecuteCommand:
|
|
145
|
+
if env.IsRequest() {
|
|
146
|
+
return p.tryExecuteCommand(env)
|
|
147
|
+
}
|
|
148
|
+
case methodCodeAction:
|
|
149
|
+
if env.IsRequest() {
|
|
150
|
+
p.rememberCodeActionRequest(env)
|
|
151
|
+
}
|
|
152
|
+
case methodCancelRequest:
|
|
153
|
+
// $/cancelRequest names an in-flight id the editor has given up on.
|
|
154
|
+
// The proxy drops any pending codeAction entry for that id so the
|
|
155
|
+
// map cannot grow unbounded across long editor sessions, then lets
|
|
156
|
+
// the notification continue to upstream.
|
|
157
|
+
p.forgetCancelledRequest(env)
|
|
158
|
+
}
|
|
159
|
+
return false, nil
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
// forgetCancelledRequest removes any pending codeAction entry whose id
|
|
@@ -165,42 +165,42 @@ func (p *Proxy) handleEditorEnvelope(env Envelope) (bool, error) {
|
|
|
165
165
|
// through the shared normalizer so a cancel for `1.0` deletes an entry
|
|
166
166
|
// stored under `1` (and vice versa for string-vs-numeric encodings).
|
|
167
167
|
func (p *Proxy) forgetCancelledRequest(env Envelope) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
168
|
+
var params struct {
|
|
169
|
+
ID json.RawMessage `json:"id"`
|
|
170
|
+
}
|
|
171
|
+
if err := json.Unmarshal(env.Params, ¶ms); err != nil {
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
key := idKeyFromRaw(params.ID)
|
|
175
|
+
if key == "" {
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
p.pendingMu.Lock()
|
|
179
|
+
defer p.pendingMu.Unlock()
|
|
180
|
+
delete(p.pendingActions, key)
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
// rememberCodeActionRequest stores the request payload so the matching
|
|
184
184
|
// response from upstream can be augmented with ttsc-owned code actions
|
|
185
185
|
// for the same range.
|
|
186
186
|
func (p *Proxy) rememberCodeActionRequest(env Envelope) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
187
|
+
var params struct {
|
|
188
|
+
TextDocument struct {
|
|
189
|
+
URI string `json:"uri"`
|
|
190
|
+
} `json:"textDocument"`
|
|
191
|
+
Range LSPRange `json:"range"`
|
|
192
|
+
Context LSPCodeActionContext `json:"context"`
|
|
193
|
+
}
|
|
194
|
+
if err := json.Unmarshal(env.Params, ¶ms); err != nil {
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
p.pendingMu.Lock()
|
|
198
|
+
defer p.pendingMu.Unlock()
|
|
199
|
+
p.pendingActions[env.IDKey()] = pendingCodeActionRequest{
|
|
200
|
+
uri: params.TextDocument.URI,
|
|
201
|
+
rng: params.Range,
|
|
202
|
+
ctx: params.Context,
|
|
203
|
+
}
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
// tryExecuteCommand handles workspace/executeCommand requests whose
|
|
@@ -208,41 +208,43 @@ func (p *Proxy) rememberCodeActionRequest(env Envelope) {
|
|
|
208
208
|
// successful local response; false when the command should fall through
|
|
209
209
|
// to upstream tsgo.
|
|
210
210
|
func (p *Proxy) tryExecuteCommand(env Envelope) (bool, error) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
211
|
+
var params struct {
|
|
212
|
+
Command string `json:"command"`
|
|
213
|
+
Arguments []json.RawMessage `json:"arguments,omitempty"`
|
|
214
|
+
}
|
|
215
|
+
if err := json.Unmarshal(env.Params, ¶ms); err != nil {
|
|
216
|
+
return false, nil
|
|
217
|
+
}
|
|
218
|
+
if !p.ownsCommand(params.Command) {
|
|
219
|
+
return false, nil
|
|
220
|
+
}
|
|
221
|
+
edit, err := p.source.ExecuteCommand(params.Command, params.Arguments)
|
|
222
|
+
if errors.Is(err, ErrCommandNotHandled) {
|
|
223
|
+
return false, nil
|
|
224
|
+
}
|
|
225
|
+
if err != nil {
|
|
226
|
+
return true, p.writeError(env.ID, fmt.Sprintf("ttsc command %q failed: %v", params.Command, err))
|
|
227
|
+
}
|
|
228
|
+
// Cycle 1 returns the WorkspaceEdit inside the executeCommand response
|
|
229
|
+
// instead of sending workspace/applyEdit as a server→client request.
|
|
230
|
+
// ttsc owns both ends (its VSCode extension), so the extension applies
|
|
231
|
+
// the edit on its side. Sticking to one direction avoids tracking our
|
|
232
|
+
// own outgoing request ids in the proxy.
|
|
233
|
+
if edit == nil {
|
|
234
|
+
return true, p.writeResult(env.ID, nil)
|
|
235
|
+
}
|
|
236
|
+
return true, p.writeResult(env.ID, edit)
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
// ownsCommand reports whether command is registered with the PluginSource
|
|
240
|
+
// and should be handled locally rather than forwarded to upstream tsgo.
|
|
239
241
|
func (p *Proxy) ownsCommand(command string) bool {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
for _, id := range p.source.CommandIDs() {
|
|
243
|
+
if id == command {
|
|
244
|
+
return true
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return false
|
|
246
248
|
}
|
|
247
249
|
|
|
248
250
|
// pumpUpstreamToEditor reads frames from the upstream tsgo server,
|
|
@@ -251,24 +253,24 @@ func (p *Proxy) ownsCommand(command string) bool {
|
|
|
251
253
|
// propagation happens through pipe closure by RunLSPServer's deferred
|
|
252
254
|
// cleanup goroutines.
|
|
253
255
|
func (p *Proxy) pumpUpstreamToEditor(_ context.Context) error {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
256
|
+
fr := NewFrameReader(p.upstreamOut)
|
|
257
|
+
for {
|
|
258
|
+
_, body, err := fr.Read()
|
|
259
|
+
if err != nil {
|
|
260
|
+
return err
|
|
261
|
+
}
|
|
262
|
+
env, parseErr := ParseEnvelope(body)
|
|
263
|
+
if parseErr != nil {
|
|
264
|
+
if forwardErr := p.writeEditorFrame(body); forwardErr != nil {
|
|
265
|
+
return forwardErr
|
|
266
|
+
}
|
|
267
|
+
continue
|
|
268
|
+
}
|
|
269
|
+
augmented := p.augmentUpstream(env, body)
|
|
270
|
+
if err := p.writeEditorFrame(augmented); err != nil {
|
|
271
|
+
return err
|
|
272
|
+
}
|
|
273
|
+
}
|
|
272
274
|
}
|
|
273
275
|
|
|
274
276
|
// augmentUpstream returns the (possibly rewritten) body to forward. For
|
|
@@ -284,25 +286,25 @@ func (p *Proxy) pumpUpstreamToEditor(_ context.Context) error {
|
|
|
284
286
|
// semantics. When the cancel wins, the pending entry is gone and
|
|
285
287
|
// augmentation skips cleanly.
|
|
286
288
|
func (p *Proxy) augmentUpstream(env Envelope, body []byte) []byte {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
289
|
+
if env.IsNotification() && env.Method == methodPublishDiagnostics {
|
|
290
|
+
if merged, ok := p.mergePublishDiagnostics(env); ok {
|
|
291
|
+
return merged
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if env.IsResponse() {
|
|
295
|
+
p.pendingMu.Lock()
|
|
296
|
+
pending, ok := p.pendingActions[env.IDKey()]
|
|
297
|
+
if ok {
|
|
298
|
+
delete(p.pendingActions, env.IDKey())
|
|
299
|
+
}
|
|
300
|
+
p.pendingMu.Unlock()
|
|
301
|
+
if ok {
|
|
302
|
+
if augmented, augOk := p.appendCodeActions(env, pending); augOk {
|
|
303
|
+
return augmented
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return body
|
|
306
308
|
}
|
|
307
309
|
|
|
308
310
|
// mergePublishDiagnostics decodes the publishDiagnostics params, asks
|
|
@@ -312,25 +314,25 @@ func (p *Proxy) augmentUpstream(env Envelope, body []byte) []byte {
|
|
|
312
314
|
// verbatim." Re-encoding our own types is guaranteed by their
|
|
313
315
|
// definitions in lsp_plugin.go, so the marshal errors are not checked.
|
|
314
316
|
func (p *Proxy) mergePublishDiagnostics(env Envelope) ([]byte, bool) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
317
|
+
var params struct {
|
|
318
|
+
URI string `json:"uri"`
|
|
319
|
+
Version *int `json:"version,omitempty"`
|
|
320
|
+
Diagnostics []json.RawMessage `json:"diagnostics"`
|
|
321
|
+
}
|
|
322
|
+
if err := json.Unmarshal(env.Params, ¶ms); err != nil {
|
|
323
|
+
return nil, false
|
|
324
|
+
}
|
|
325
|
+
extras := p.source.Diagnostics(LSPDocumentVersion{URI: params.URI, Version: params.Version})
|
|
326
|
+
if len(extras) == 0 {
|
|
327
|
+
return nil, false
|
|
328
|
+
}
|
|
329
|
+
for _, extra := range extras {
|
|
330
|
+
raw, _ := json.Marshal(extra)
|
|
331
|
+
params.Diagnostics = append(params.Diagnostics, raw)
|
|
332
|
+
}
|
|
333
|
+
env.Params, _ = json.Marshal(params)
|
|
334
|
+
body, _ := json.Marshal(env)
|
|
335
|
+
return body, true
|
|
334
336
|
}
|
|
335
337
|
|
|
336
338
|
// appendCodeActions adds ttsc-owned code actions to a forwarded
|
|
@@ -343,49 +345,61 @@ func (p *Proxy) mergePublishDiagnostics(env Envelope) ([]byte, bool) {
|
|
|
343
345
|
// shape ttsc cannot splice into, so the proxy forwards verbatim
|
|
344
346
|
// rather than corrupting it).
|
|
345
347
|
func (p *Proxy) appendCodeActions(env Envelope, pending pendingCodeActionRequest) ([]byte, bool) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
348
|
+
if env.IsErrorResponse() {
|
|
349
|
+
return nil, false
|
|
350
|
+
}
|
|
351
|
+
trimmed := bytes.TrimSpace(env.Result)
|
|
352
|
+
if len(trimmed) > 0 && trimmed[0] != '[' && !bytes.Equal(trimmed, []byte("null")) {
|
|
353
|
+
return nil, false
|
|
354
|
+
}
|
|
355
|
+
actions := p.source.CodeActions(pending.uri, pending.rng, pending.ctx)
|
|
356
|
+
if len(actions) == 0 {
|
|
357
|
+
return nil, false
|
|
358
|
+
}
|
|
359
|
+
var existing []json.RawMessage
|
|
360
|
+
if len(trimmed) > 0 && !bytes.Equal(trimmed, []byte("null")) {
|
|
361
|
+
_ = json.Unmarshal(trimmed, &existing)
|
|
362
|
+
}
|
|
363
|
+
for _, action := range actions {
|
|
364
|
+
raw, _ := json.Marshal(action)
|
|
365
|
+
existing = append(existing, raw)
|
|
366
|
+
}
|
|
367
|
+
env.Result, _ = json.Marshal(existing)
|
|
368
|
+
body, _ := json.Marshal(env)
|
|
369
|
+
return body, true
|
|
368
370
|
}
|
|
369
371
|
|
|
372
|
+
// writeEditorFrame serializes body under writeMu so concurrent calls from
|
|
373
|
+
// pumpEditorToUpstream (local command responses) and pumpUpstreamToEditor
|
|
374
|
+
// (forwarded upstream frames) do not interleave partial writes.
|
|
370
375
|
func (p *Proxy) writeEditorFrame(body []byte) error {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
376
|
+
p.writeMu.Lock()
|
|
377
|
+
defer p.writeMu.Unlock()
|
|
378
|
+
return WriteFrame(p.editorOut, body)
|
|
374
379
|
}
|
|
375
380
|
|
|
381
|
+
// writeResult sends a JSON-RPC success response with the given result to
|
|
382
|
+
// the editor. A nil result is marshalled as JSON null.
|
|
376
383
|
func (p *Proxy) writeResult(id json.RawMessage, result any) error {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
384
|
+
rawResult, _ := json.Marshal(result)
|
|
385
|
+
env := Envelope{JSONRPC: "2.0", ID: id, Result: rawResult}
|
|
386
|
+
body, _ := json.Marshal(env)
|
|
387
|
+
return p.writeEditorFrame(body)
|
|
381
388
|
}
|
|
382
389
|
|
|
390
|
+
// jsonRPCInternalError is the JSON-RPC 2.0 reserved error code for
|
|
391
|
+
// "Internal error" (spec §5.1).
|
|
392
|
+
const jsonRPCInternalError = -32603
|
|
393
|
+
|
|
394
|
+
// writeError sends a JSON-RPC error response to the editor. message is
|
|
395
|
+
// embedded verbatim in the error object; callers are responsible for
|
|
396
|
+
// keeping it concise and safe to surface in editor UI.
|
|
383
397
|
func (p *Proxy) writeError(id json.RawMessage, message string) error {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
398
|
+
errPayload, _ := json.Marshal(struct {
|
|
399
|
+
Code int `json:"code"`
|
|
400
|
+
Message string `json:"message"`
|
|
401
|
+
}{Code: jsonRPCInternalError, Message: message})
|
|
402
|
+
env := Envelope{JSONRPC: "2.0", ID: id, Error: errPayload}
|
|
403
|
+
body, _ := json.Marshal(env)
|
|
404
|
+
return p.writeEditorFrame(body)
|
|
391
405
|
}
|