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.
Files changed (79) hide show
  1. package/README.md +25 -6
  2. package/cmd/ttsc/api_compile.go +101 -106
  3. package/cmd/ttsc/api_compile_branches_test.go +81 -0
  4. package/cmd/ttsc/api_transform.go +49 -54
  5. package/cmd/ttsc/api_transform_branches_test.go +41 -0
  6. package/cmd/ttsc/build.go +108 -109
  7. package/cmd/ttsc/build_branches_test.go +130 -0
  8. package/cmd/ttsc/helpers_test.go +47 -0
  9. package/cmd/ttsc/main.go +104 -103
  10. package/cmd/ttsc/main_alias_branches_test.go +29 -0
  11. package/cmd/ttscserver/main.go +104 -96
  12. package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +42 -0
  13. package/cmd/ttscserver/run_lsp_reports_server_error_test.go +23 -25
  14. package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +44 -0
  15. package/cmd/utility-host/main.go +42 -0
  16. package/driver/lsp.go +60 -0
  17. package/driver/plugins.go +122 -0
  18. package/driver/program.go +346 -356
  19. package/driver/rewrite.go +336 -339
  20. package/{driver → internal/lspserver}/lsp_envelope.go +70 -64
  21. package/{driver → internal/lspserver}/lsp_frame.go +62 -62
  22. package/{driver → internal/lspserver}/lsp_plugin.go +58 -58
  23. package/internal/lspserver/lsp_proxy.go +391 -0
  24. package/internal/lspserver/lsp_server.go +231 -0
  25. package/lib/TtscCompiler.d.ts +3 -3
  26. package/lib/TtscCompiler.js +20 -6
  27. package/lib/TtscCompiler.js.map +1 -1
  28. package/lib/compiler/internal/resolveTsgo.d.ts +1 -0
  29. package/lib/compiler/internal/resolveTsgo.js +15 -4
  30. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  31. package/lib/compiler/internal/runBuild.js +12 -4
  32. package/lib/compiler/internal/runBuild.js.map +1 -1
  33. package/lib/compiler/internal/sharedHostHelpers.d.ts +12 -33
  34. package/lib/compiler/internal/sharedHostHelpers.js +27 -70
  35. package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
  36. package/lib/compiler/internal/transformProjectInMemory.js +12 -5
  37. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  38. package/lib/index.d.ts +0 -1
  39. package/lib/index.js +0 -1
  40. package/lib/index.js.map +1 -1
  41. package/lib/launcher/internal/runTtsc.js +9 -4
  42. package/lib/launcher/internal/runTtsc.js.map +1 -1
  43. package/lib/launcher/internal/runTtscserver.d.ts +1 -0
  44. package/lib/launcher/internal/runTtscserver.js +38 -1
  45. package/lib/launcher/internal/runTtscserver.js.map +1 -1
  46. package/lib/plugin/internal/buildSourcePlugin.d.ts +3 -0
  47. package/lib/plugin/internal/buildSourcePlugin.js +444 -41
  48. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  49. package/lib/plugin/internal/loadProjectPlugins.js +153 -34
  50. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  51. package/lib/structures/ITtscCompilerContext.d.ts +2 -1
  52. package/lib/structures/ITtscPlugin.d.ts +8 -13
  53. package/lib/structures/ITtscPluginFactoryContext.d.ts +4 -5
  54. package/lib/structures/ITtscProjectPluginConfig.d.ts +2 -2
  55. package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +4 -2
  56. package/package.json +8 -8
  57. package/shim/lsp/shim.go +11 -17
  58. package/src/TtscCompiler.ts +20 -7
  59. package/src/compiler/internal/resolveTsgo.ts +18 -5
  60. package/src/compiler/internal/runBuild.ts +17 -4
  61. package/src/compiler/internal/sharedHostHelpers.ts +34 -67
  62. package/src/compiler/internal/transformProjectInMemory.ts +18 -5
  63. package/src/index.ts +0 -1
  64. package/src/launcher/internal/runTtsc.ts +9 -4
  65. package/src/launcher/internal/runTtscserver.ts +43 -1
  66. package/src/plugin/internal/buildSourcePlugin.ts +475 -41
  67. package/src/plugin/internal/loadProjectPlugins.ts +199 -39
  68. package/src/structures/ITtscCompilerContext.ts +2 -1
  69. package/src/structures/ITtscPlugin.ts +9 -13
  70. package/src/structures/ITtscPluginFactoryContext.ts +4 -5
  71. package/src/structures/ITtscProjectPluginConfig.ts +2 -2
  72. package/src/structures/internal/ITtscLoadedNativePlugin.ts +4 -2
  73. package/utility/host.go +267 -1296
  74. package/driver/lsp_proxy.go +0 -394
  75. package/driver/lsp_server.go +0 -197
  76. package/lib/defineTtscPlugin.d.ts +0 -18
  77. package/lib/defineTtscPlugin.js +0 -7
  78. package/lib/defineTtscPlugin.js.map +0 -1
  79. package/src/defineTtscPlugin.ts +0 -26
@@ -1,12 +1,12 @@
1
- package driver
1
+ package lspserver
2
2
 
3
3
  import (
4
- "bytes"
5
- "encoding/json"
6
- "errors"
7
- "fmt"
8
- "strconv"
9
- "strings"
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
- 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"`
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
- 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
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
- return len(e.ID) > 0 && e.Method != ""
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
- return len(e.ID) == 0 && e.Method != ""
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
- return len(e.ID) > 0 && e.Method == ""
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
- return e.IsResponse() && len(e.Error) > 0 && !bytes.Equal(bytes.TrimSpace(e.Error), []byte("null"))
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
- return idKeyFromRaw(e.ID)
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
- if len(raw) == 0 {
99
- return ""
100
- }
101
- dec := json.NewDecoder(bytes.NewReader(raw))
102
- dec.UseNumber()
103
- var decoded any
104
- if err := dec.Decode(&decoded); err != nil {
105
- return ""
106
- }
107
- switch v := decoded.(type) {
108
- case json.Number:
109
- lit := v.String()
110
- if !strings.ContainsAny(lit, ".eE") {
111
- // Integer-shaped literal. Use int64 when it fits so leading
112
- // zeros and minus signs canonicalize, but preserve the literal
113
- // for past-int64 ids — Float64 normalization would silently
114
- // round 9999999999999999998 and 9999999999999999999 to the
115
- // same key.
116
- if n, err := v.Int64(); err == nil {
117
- return strconv.FormatInt(n, 10)
118
- }
119
- return lit
120
- }
121
- // Float-shaped literal (`1.0`, `1.5`, `1e2`). Collapse to integer
122
- // form only when the value is exactly representable as int64;
123
- // 2^53 bounds the safe range because float64 loses unit precision
124
- // past that magnitude.
125
- if f, err := v.Float64(); err == nil {
126
- const maxSafeFloat = float64(int64(1) << 53)
127
- if f >= -maxSafeFloat && f <= maxSafeFloat && f == float64(int64(f)) {
128
- return strconv.FormatInt(int64(f), 10)
129
- }
130
- }
131
- return lit
132
- case string:
133
- return strconv.Quote(v)
134
- }
135
- return ""
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
- // embeds tsgo's LSP server but sits between it and the editor so it can
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 driver
11
+ package lspserver
12
12
 
13
13
  import (
14
- "bufio"
15
- "errors"
16
- "fmt"
17
- "io"
18
- "strconv"
19
- "strings"
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
- br *bufio.Reader
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
- return &FrameReader{br: bufio.NewReader(r)}
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
- 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
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
- 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
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
- 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
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 driver
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
- Line int `json:"line"`
11
- Character int `json:"character"`
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
- Start LSPPosition `json:"start"`
17
- End LSPPosition `json:"end"`
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
- // 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
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
- 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"`
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
- 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"`
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
- Title string `json:"title"`
62
- Command string `json:"command"`
63
- Arguments []json.RawMessage `json:"arguments,omitempty"`
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
- Diagnostics []json.RawMessage `json:"diagnostics,omitempty"`
71
- Only []string `json:"only,omitempty"`
72
- TriggerKind int `json:"triggerKind,omitempty"`
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
- Changes map[string][]LSPTextEdit `json:"changes,omitempty"`
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
- Range LSPRange `json:"range"`
84
- NewText string `json:"newText"`
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
- URI string
97
- Version *int
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
- // 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 ttsc owns (CommandIDs reports the prefix). 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
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
- return nil
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
- return nil, ErrCommandNotHandled
145
+ return nil, ErrCommandNotHandled
146
146
  }
147
147
 
148
148
  // CommandIDs returns an empty slice so the proxy forwards every command