ttsc 0.31.5 → 0.32.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 (123) hide show
  1. package/cmd/ttsc/compiler_argument_cwd_test.go +38 -38
  2. package/cmd/ttscserver/main.go +2 -12
  3. package/cmd/ttscserver/resolve_launch_uses_native_flag_authority_test.go +7 -7
  4. package/driver/lsp.go +4 -43
  5. package/driver/rewrite.go +1 -1
  6. package/internal/e2etrace/completion_hints.go +3 -3
  7. package/internal/e2etrace/completion_hints_test.go +26 -12
  8. package/internal/graph/build.go +5 -6
  9. package/internal/graph/graph.go +3 -3
  10. package/internal/graph/line_model.go +2 -35
  11. package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +13 -36
  12. package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +9 -3
  13. package/internal/lspserver/lsp_plugin.go +19 -0
  14. package/internal/lspserver/lsp_proxy.go +9 -129
  15. package/internal/lspserver/lsp_server.go +0 -8
  16. package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +6 -36
  17. package/internal/sourceprocess/windows_target_owns_suspended_admission_test.go +5 -5
  18. package/lib/compiler/internal/build/BuildExecution.js +1 -1
  19. package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
  20. package/lib/compiler/internal/build/TsgoArguments.js.map +1 -1
  21. package/lib/compiler/internal/project/CompilerProjectSelection.d.ts +9 -8
  22. package/lib/compiler/internal/project/CompilerProjectSelection.js +11 -9
  23. package/lib/compiler/internal/project/CompilerProjectSelection.js.map +1 -1
  24. package/lib/compiler/internal/project/resolveOwningProjectConfig.d.ts +20 -8
  25. package/lib/compiler/internal/project/resolveOwningProjectConfig.js +21 -12
  26. package/lib/compiler/internal/project/resolveOwningProjectConfig.js.map +1 -1
  27. package/lib/compiler/internal/runSingleFileEmit.d.ts +5 -4
  28. package/lib/compiler/internal/runSingleFileEmit.js +5 -4
  29. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  30. package/lib/flags/ParseOptions.d.ts +2 -2
  31. package/lib/internal/E2ETrace.js.map +1 -1
  32. package/lib/internal/OwnedNativeProcess.d.ts +2 -2
  33. package/lib/internal/OwnedNativeProcess.js +2 -2
  34. package/lib/internal/SourceNativeRetirement.d.ts +8 -7
  35. package/lib/internal/SourceNativeRetirement.js +19 -10
  36. package/lib/internal/SourceNativeRetirement.js.map +1 -1
  37. package/lib/internal/TtsxNodeOptions.d.ts +79 -0
  38. package/lib/internal/TtsxNodeOptions.js +121 -0
  39. package/lib/internal/TtsxNodeOptions.js.map +1 -0
  40. package/lib/internal/javascriptRuntimeCapabilities.js +4 -1
  41. package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
  42. package/lib/internal/parseWindowsDirectoryCaseSensitivity.d.ts +5 -5
  43. package/lib/internal/parseWindowsDirectoryCaseSensitivity.js +5 -5
  44. package/lib/internal/pathIdentity/createCaseSensitivityObservations.d.ts +35 -0
  45. package/lib/internal/pathIdentity/createCaseSensitivityObservations.js +209 -0
  46. package/lib/internal/pathIdentity/createCaseSensitivityObservations.js.map +1 -0
  47. package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js +2 -143
  48. package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js.map +1 -1
  49. package/lib/internal/projectInputPathIdentity.d.ts +1 -0
  50. package/lib/internal/projectInputPathIdentity.js +1 -0
  51. package/lib/internal/projectInputPathIdentity.js.map +1 -1
  52. package/lib/launcher/internal/parseTtsxCLI.d.ts +5 -4
  53. package/lib/launcher/internal/parseTtsxCLI.js +5 -4
  54. package/lib/launcher/internal/parseTtsxCLI.js.map +1 -1
  55. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  56. package/lib/launcher/internal/resolveSingleFileOutput.d.ts +8 -9
  57. package/lib/launcher/internal/resolveSingleFileOutput.js +8 -9
  58. package/lib/launcher/internal/resolveSingleFileOutput.js.map +1 -1
  59. package/lib/launcher/internal/runTtsc.js +5 -5
  60. package/lib/launcher/internal/runTtsx.js +5 -2
  61. package/lib/launcher/internal/runTtsx.js.map +1 -1
  62. package/lib/launcher/internal/runtime/RuntimeEntrySources.d.ts +40 -0
  63. package/lib/launcher/internal/runtime/RuntimeEntrySources.js +57 -0
  64. package/lib/launcher/internal/runtime/RuntimeEntrySources.js.map +1 -0
  65. package/lib/launcher/internal/runtime/RuntimeProjectOwnership.d.ts +5 -4
  66. package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js +8 -6
  67. package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js.map +1 -1
  68. package/lib/launcher/internal/runtime/commonJsExportNames.d.ts +2 -1
  69. package/lib/launcher/internal/runtime/commonJsExportNames.js +2 -1
  70. package/lib/launcher/internal/runtime/commonJsExportNames.js.map +1 -1
  71. package/lib/launcher/internal/runtime/installRuntimeHooks.js +35 -9
  72. package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
  73. package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js +3 -1
  74. package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js.map +1 -1
  75. package/lib/launcher/internal/watch/WatchTopology.d.ts +74 -12
  76. package/lib/launcher/internal/watch/WatchTopology.js +301 -92
  77. package/lib/launcher/internal/watch/WatchTopology.js.map +1 -1
  78. package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.d.ts +4 -1
  79. package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js +3 -2
  80. package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js.map +1 -1
  81. package/lib/plugin/internal/load/loadProjectPlugins.js +13 -7
  82. package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
  83. package/lib/plugin/internal/load/resolveNativeSource.js +2 -3
  84. package/lib/plugin/internal/load/resolveNativeSource.js.map +1 -1
  85. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.d.ts +4 -1
  86. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js +9 -2
  87. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js.map +1 -1
  88. package/lib/structures/internal/TtscCommonOptions.d.ts +2 -2
  89. package/package.json +9 -9
  90. package/src/compiler/internal/build/BuildExecution.ts +5 -2
  91. package/src/compiler/internal/build/TsgoArguments.ts +4 -1
  92. package/src/compiler/internal/project/CompilerProjectSelection.ts +14 -14
  93. package/src/compiler/internal/project/resolveOwningProjectConfig.ts +29 -14
  94. package/src/compiler/internal/runSingleFileEmit.ts +11 -7
  95. package/src/flags/ParseOptions.ts +2 -2
  96. package/src/internal/E2ETrace.ts +3 -1
  97. package/src/internal/OwnedNativeProcess.ts +2 -2
  98. package/src/internal/SourceNativeRetirement.ts +42 -19
  99. package/src/internal/TtsxNodeOptions.ts +124 -0
  100. package/src/internal/javascriptRuntimeCapabilities.ts +6 -1
  101. package/src/internal/parseWindowsDirectoryCaseSensitivity.ts +5 -5
  102. package/src/internal/pathIdentity/createCaseSensitivityObservations.ts +242 -0
  103. package/src/internal/pathIdentity/createFilesystemPathIdentityContext.ts +2 -169
  104. package/src/internal/projectInputPathIdentity.ts +1 -0
  105. package/src/launcher/internal/parseTtsxCLI.ts +5 -4
  106. package/src/launcher/internal/prepareExecution.ts +4 -1
  107. package/src/launcher/internal/resolveSingleFileOutput.ts +8 -9
  108. package/src/launcher/internal/runTtsc.ts +5 -5
  109. package/src/launcher/internal/runTtsx.ts +17 -8
  110. package/src/launcher/internal/runtime/RuntimeEntrySources.ts +57 -0
  111. package/src/launcher/internal/runtime/RuntimeProjectOwnership.ts +23 -10
  112. package/src/launcher/internal/runtime/commonJsExportNames.ts +11 -4
  113. package/src/launcher/internal/runtime/installRuntimeHooks.ts +55 -13
  114. package/src/launcher/internal/runtime/resolveRuntimeCleanTargets.ts +17 -5
  115. package/src/launcher/internal/watch/WatchTopology.ts +387 -99
  116. package/src/launcher/internal/watch/projectInputReloadEventShouldNotify.ts +14 -9
  117. package/src/plugin/internal/load/loadProjectPlugins.ts +13 -1
  118. package/src/plugin/internal/load/resolveNativeSource.ts +10 -4
  119. package/src/plugin/internal/source/pluginModuleReplaceDirectories.ts +10 -1
  120. package/src/structures/internal/TtscCommonOptions.ts +2 -2
  121. package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +0 -78
  122. package/internal/graphsymbols/provider.go +0 -650
  123. package/internal/lspserver/lsp_symbols.go +0 -212
@@ -1,11 +1,11 @@
1
1
  package main
2
2
 
3
3
  import (
4
- "path/filepath"
5
- "reflect"
6
- "testing"
4
+ "path/filepath"
5
+ "reflect"
6
+ "testing"
7
7
 
8
- "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ "github.com/samchon/ttsc/packages/ttsc/driver"
9
9
  )
10
10
 
11
11
  // TestCompilerArgumentCwdPreparation verifies decoded argv origin at all
@@ -20,38 +20,38 @@ import (
20
20
  // @evidence contracts/testing.md#distinguishing-cases All three frontdoors cover inherited, explicit nonempty, explicit [] and absent payload, keeping explicit empty distinct from nil inherited argv.
21
21
  // @evidence contracts/testing.md#execution-ownership Pure command preparation executes in this Go unit process with temporary cwd and t.Setenv restoration, without Program acquisition, native host launch or installation.
22
22
  func TestCompilerArgumentCwdPreparation(t *testing.T) {
23
- root := t.TempDir()
24
- a := filepath.Join(root, "A")
25
- t.Setenv(driver.TsgoArgsCwdEnv, a)
26
- for _, c := range []struct {
27
- name, inherited, explicit, cwd string
28
- argv []string
29
- }{
30
- {"inherited", `["--strict"]`, "", "", nil},
31
- {"explicit", `["--strict"]`, `["--target","es2022"]`, "", []string{"--target", "es2022"}},
32
- {"explicit empty", `["--strict"]`, `[]`, "", []string{}},
33
- {"absent payload", "", "", "", nil},
34
- } {
35
- t.Run(c.name, func(t *testing.T) {
36
- t.Setenv(driver.TsgoArgsEnv, c.inherited)
37
- args := []string{"--cwd", root}
38
- if c.explicit != "" {
39
- args = append(args, "--tsgo-args", c.explicit)
40
- }
41
- build, buildStatus := prepareBuildInvocation(args)
42
- compile, compileStatus := prepareAPICompileInvocation(args)
43
- transform, transformStatus := prepareAPITransformInvocation(args)
44
- if buildStatus != 0 || compileStatus != 0 || transformStatus != 0 {
45
- t.Fatalf("preparation statuses: %d/%d/%d", buildStatus, compileStatus, transformStatus)
46
- }
47
- if build.options.ForceEmit || build.options.ForceNoEmit || !compile.options.ForceEmit || compile.options.ForceNoEmit || transform.options.ForceEmit || !transform.options.ForceNoEmit {
48
- t.Fatal("command preparation changed its original emit policy")
49
- }
50
- for _, options := range []driver.LoadProgramOptions{build.options, compile.options, transform.options} {
51
- if options.TsgoArgsCwd != c.cwd || !reflect.DeepEqual(options.TsgoArgs, c.argv) {
52
- t.Fatalf("wrong origin: %#v", options)
53
- }
54
- }
55
- })
56
- }
23
+ root := t.TempDir()
24
+ a := filepath.Join(root, "A")
25
+ t.Setenv(driver.TsgoArgsCwdEnv, a)
26
+ for _, c := range []struct {
27
+ name, inherited, explicit, cwd string
28
+ argv []string
29
+ }{
30
+ {"inherited", `["--strict"]`, "", "", nil},
31
+ {"explicit", `["--strict"]`, `["--target","es2022"]`, "", []string{"--target", "es2022"}},
32
+ {"explicit empty", `["--strict"]`, `[]`, "", []string{}},
33
+ {"absent payload", "", "", "", nil},
34
+ } {
35
+ t.Run(c.name, func(t *testing.T) {
36
+ t.Setenv(driver.TsgoArgsEnv, c.inherited)
37
+ args := []string{"--cwd", root}
38
+ if c.explicit != "" {
39
+ args = append(args, "--tsgo-args", c.explicit)
40
+ }
41
+ build, buildStatus := prepareBuildInvocation(args)
42
+ compile, compileStatus := prepareAPICompileInvocation(args)
43
+ transform, transformStatus := prepareAPITransformInvocation(args)
44
+ if buildStatus != 0 || compileStatus != 0 || transformStatus != 0 {
45
+ t.Fatalf("preparation statuses: %d/%d/%d", buildStatus, compileStatus, transformStatus)
46
+ }
47
+ if build.options.ForceEmit || build.options.ForceNoEmit || !compile.options.ForceEmit || compile.options.ForceNoEmit || transform.options.ForceEmit || !transform.options.ForceNoEmit {
48
+ t.Fatal("command preparation changed its original emit policy")
49
+ }
50
+ for _, options := range []driver.LoadProgramOptions{build.options, compile.options, transform.options} {
51
+ if options.TsgoArgsCwd != c.cwd || !reflect.DeepEqual(options.TsgoArgs, c.argv) {
52
+ t.Fatalf("wrong origin: %#v", options)
53
+ }
54
+ }
55
+ })
56
+ }
57
57
  }
@@ -27,7 +27,6 @@ import (
27
27
  "syscall"
28
28
  "time"
29
29
 
30
- "github.com/samchon/ttsc/packages/ttsc/internal/graphsymbols"
31
30
  "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
32
31
  )
33
32
 
@@ -109,8 +108,8 @@ type lspLaunchOptions struct {
109
108
  PluginsFile string `json:"-"`
110
109
  ProgressDelay time.Duration `json:"-"`
111
110
  SuppressExecuteCommandProvider bool `json:"-"`
112
- SuppressedExecuteCommandIDs string `json:"-"`
113
- ExecuteCommandIDPrefix string `json:"-"`
111
+ SuppressedExecuteCommandIDs string `json:"-"`
112
+ ExecuteCommandIDPrefix string `json:"-"`
114
113
  }
115
114
 
116
115
  // resolveLaunch is a private launcher/native protocol. It uses the same parser
@@ -227,14 +226,6 @@ func startLSP(options *lspLaunchOptions) int {
227
226
  return 2
228
227
  }
229
228
 
230
- // The SymbolProvider answers textDocument/documentSymbol and
231
- // textDocument/references from ttsc's compiler-backed code graph. tsgo
232
- // implements both methods itself, so the proxy forwards to tsgo whenever it
233
- // advertises the capability and consults this provider only as a fallback
234
- // (tsgo did not advertise). It loads the program lazily on the first such
235
- // request, so wiring it here adds no startup cost.
236
- symbolProvider := graphsymbols.NewProvider(options.Cwd, options.Tsconfig)
237
-
238
229
  err = runLSPServer(ctx, lspserver.LSPServerOptions{
239
230
  In: stdin,
240
231
  Out: stdout,
@@ -242,7 +233,6 @@ func startLSP(options *lspLaunchOptions) int {
242
233
  Cwd: options.Cwd,
243
234
  TsgoBinary: options.TsgoBinary,
244
235
  Source: source,
245
- SymbolProvider: symbolProvider,
246
236
  SuppressExecuteCommandProvider: options.SuppressExecuteCommandProvider,
247
237
  SuppressedExecuteCommandIDs: splitCSV(options.SuppressedExecuteCommandIDs),
248
238
  ExecuteCommandIDPrefix: options.ExecuteCommandIDPrefix,
@@ -46,12 +46,12 @@ func TestResolveLaunchUsesNativeFlagAuthority(t *testing.T) {
46
46
  defer func() { getwd, runLSPServer, notifyContext = previousGetwd, previousHost, previousNotify }()
47
47
 
48
48
  for _, test := range []struct {
49
- name string
50
- args []string
51
- cwd string
52
- config string
49
+ name string
50
+ args []string
51
+ cwd string
52
+ config string
53
53
  explicit bool
54
- tsgo string
54
+ tsgo string
55
55
  }{
56
56
  {"last assignments", []string{"--stdio", "--cwd", "/a", "-cwd=/b", "--tsconfig=first.json", "-tsconfig", "last.json", "--tsgo=/first", "-tsgo", "/last"}, "/b", "last.json", true, "/last"},
57
57
  {"equal duplicates", []string{"-stdio", "-cwd=/b", "--cwd", "/b", "-tsconfig=last.json", "--tsconfig=last.json"}, "/b", "last.json", true, "/environment-tsgo"},
@@ -107,8 +107,8 @@ func TestResolveLaunchUsesNativeFlagAuthority(t *testing.T) {
107
107
  }
108
108
 
109
109
  for _, test := range []struct {
110
- name string
111
- args []string
110
+ name string
111
+ args []string
112
112
  diagnostic string
113
113
  }{
114
114
  {"unknown", []string{"--stdio", "--cwd=/missing", "--unknown"}, "flag provided but not defined: -unknown"},
package/driver/lsp.go CHANGED
@@ -201,58 +201,19 @@ type LSPCompletionHint = lspserver.LSPCompletionHint
201
201
  // @evidenceExclude contracts/portability.md#os-neutral-implementation This type declaration describes a data shape only; it opens no file, builds no path and branches on no platform.
202
202
  type LSPCompletionItem = lspserver.LSPCompletionItem
203
203
 
204
- // LSPSymbolKind carries LSP symbol classification values for editor outlines.
205
- //
206
- // @evidence contracts/common.md#principled-implementation The alias retains the server's protocol numeric classifications unchanged.
207
- // @evidence contracts/common.md#clear-and-simple-design Outline classification remains one enum shared by provider and proxy.
208
- // @evidence contracts/common.md#prohibited-implementation-shortcuts Protocol values are forwarded without source-name or consumer special cases.
209
- // @evidence contracts/common.md#meaningful-documentation The enum's outline purpose is stated directly under documentation-skill guidance.
210
- // @evidenceExclude contracts/performance.md#bound-retention-and-release-resources A type declaration acquires and holds no runtime resource.
211
- // @evidenceExclude contracts/performance.md#efficient-algorithms A type declaration chooses no processing strategy.
212
- // @evidenceExclude contracts/performance.md#reuse-equivalent-work A type declaration computes nothing, so there is no work to share.
213
- // @evidenceExclude contracts/portability.md#os-neutral-implementation This type declaration describes a data shape only; it opens no file, builds no path and branches on no platform.
214
- type LSPSymbolKind = lspserver.LSPSymbolKind
215
-
216
- // LSPDocumentSymbol forms an outline hierarchy; SelectionRange locates the name
217
- // within the declaration's Range and Children hold nested members.
218
- //
219
- // @evidence contracts/common.md#principled-implementation Alias identity keeps hierarchical symbols and their two range roles intact.
220
- // @evidence contracts/common.md#clear-and-simple-design The provider and proxy share one nested outline representation.
221
- // @evidence contracts/common.md#prohibited-implementation-shortcuts No flattening adapter or guessed member relationship is introduced.
222
- // @evidence contracts/common.md#meaningful-documentation Native prose explains hierarchy and range roles following the documentation skill.
223
- // @evidenceExclude contracts/performance.md#bound-retention-and-release-resources A type declaration acquires and holds no runtime resource.
224
- // @evidenceExclude contracts/performance.md#efficient-algorithms A type declaration chooses no processing strategy.
225
- // @evidenceExclude contracts/performance.md#reuse-equivalent-work A type declaration computes nothing, so there is no work to share.
226
- // @evidenceExclude contracts/portability.md#os-neutral-implementation This type declaration describes a data shape only; it opens no file, builds no path and branches on no platform.
227
- type LSPDocumentSymbol = lspserver.LSPDocumentSymbol
228
-
229
- // LSPLocation identifies a reference range inside the document named by its URI.
204
+ // LSPLocation identifies a range inside the document named by its URI. Plugin
205
+ // diagnostics carry one per related-information site.
230
206
  //
231
207
  // @evidence contracts/common.md#principled-implementation The alias preserves the server's URI and LSP coordinate pair.
232
- // @evidence contracts/common.md#clear-and-simple-design References use the existing location schema instead of a filesystem-path adapter.
208
+ // @evidence contracts/common.md#clear-and-simple-design Related diagnostic information uses the existing location schema instead of a filesystem-path adapter.
233
209
  // @evidence contracts/common.md#prohibited-implementation-shortcuts No OS path spelling is substituted for a protocol document URI.
234
- // @evidence contracts/common.md#meaningful-documentation Native prose distinguishes document identity and range using documentation-skill guidance.
210
+ // @evidence contracts/common.md#meaningful-documentation Native prose distinguishes document identity and range and names the related-information use, following documentation-skill guidance.
235
211
  // @evidenceExclude contracts/performance.md#bound-retention-and-release-resources A type declaration acquires and holds no runtime resource.
236
212
  // @evidenceExclude contracts/performance.md#efficient-algorithms A type declaration chooses no processing strategy.
237
213
  // @evidenceExclude contracts/performance.md#reuse-equivalent-work A type declaration computes nothing, so there is no work to share.
238
214
  // @evidenceExclude contracts/portability.md#os-neutral-implementation This type declaration describes a data shape only; it opens no file, builds no path and branches on no platform.
239
215
  type LSPLocation = lspserver.LSPLocation
240
216
 
241
- // SymbolProvider is the driver-level alias for lspserver.SymbolProvider.
242
- // It is the seam that answers textDocument/documentSymbol and
243
- // textDocument/references from ttsc's compiler-backed code graph when upstream
244
- // tsgo does not advertise them, or when the caller selects local graph answers.
245
- //
246
- // @evidence contracts/common.md#principled-implementation The server interface alias preserves the method set required by local symbol routing.
247
- // @evidence contracts/common.md#clear-and-simple-design Driver embedders implement the existing provider seam without another proxy layer.
248
- // @evidence contracts/common.md#prohibited-implementation-shortcuts The public extension does not replace upstream methods or patch compiler internals.
249
- // @evidence contracts/common.md#meaningful-documentation Native prose names the two routed requests and owning graph under documentation-skill guidance.
250
- // @evidenceExclude contracts/performance.md#bound-retention-and-release-resources A type declaration acquires and holds no runtime resource.
251
- // @evidenceExclude contracts/performance.md#efficient-algorithms A type declaration chooses no processing strategy.
252
- // @evidenceExclude contracts/performance.md#reuse-equivalent-work A type declaration computes nothing, so there is no work to share.
253
- // @evidenceExclude contracts/portability.md#os-neutral-implementation This type declaration describes a data shape only; it opens no file, builds no path and branches on no platform.
254
- type SymbolProvider = lspserver.SymbolProvider
255
-
256
217
  // PluginSource is the driver-level alias for lspserver.PluginSource.
257
218
  // It is the public seam downstream pipelines implement to contribute
258
219
  // diagnostics, code actions, and workspace commands to the LSP proxy.
package/driver/rewrite.go CHANGED
@@ -452,7 +452,7 @@ func collectEmittedRewriteCalls(file *ast.SourceFile) []emittedRewriteCall {
452
452
  if call.QuestionDotToken == nil {
453
453
  if path := rewriteCallPath(call.Expression); path != "" {
454
454
  calls = append(calls, emittedRewriteCall{
455
- start: shimscanner.GetTokenPosOfNode(node, file, false),
455
+ start: shimscanner.GetTokenPosOfNode(node, file, false),
456
456
  calleeEnd: call.Expression.End(), end: node.End(), path: path,
457
457
  })
458
458
  }
@@ -23,10 +23,10 @@ func CompletionHintsPublication(cwd, producer string, generation uint64, outcome
23
23
  return
24
24
  }
25
25
  data := map[string]any{
26
- "producer": producer,
26
+ "producer": producer,
27
27
  "generation": generation,
28
- "outcome": outcome,
29
- "hints": hints,
28
+ "outcome": outcome,
29
+ "hints": hints,
30
30
  }
31
31
  if err != nil {
32
32
  data["error"] = err.Error()
@@ -31,8 +31,8 @@ func TestCompletionHintsPublicationPreservesTerminalOutcomes(t *testing.T) {
31
31
  t.Setenv("TTSC_E2E_TRACE", root)
32
32
  for _, entry := range []struct {
33
33
  outcome string
34
- hints int
35
- err error
34
+ hints int
35
+ err error
36
36
  }{
37
37
  {"published", 0, nil},
38
38
  {"published", 2, nil},
@@ -47,22 +47,32 @@ func TestCompletionHintsPublicationPreservesTerminalOutcomes(t *testing.T) {
47
47
  t.Fatalf("observer sink: %v %v", entries, err)
48
48
  }
49
49
  body, err := os.ReadFile(filepath.Join(root, entries[0].Name()))
50
- if err != nil { t.Fatal(err) }
50
+ if err != nil {
51
+ t.Fatal(err)
52
+ }
51
53
  lines := strings.Split(strings.TrimSuffix(string(body), "\n"), "\n")
52
- expected := []struct{ outcome string; hints float64; err string }{
54
+ expected := []struct {
55
+ outcome string
56
+ hints float64
57
+ err string
58
+ }{
53
59
  {"published", 0, ""}, {"published", 2, ""},
54
60
  {"run-failed", 0, "native failed"}, {"decode-failed", 0, "invalid JSON"},
55
61
  {"superseded", 2, ""},
56
62
  }
57
- if len(lines) != len(expected) { t.Fatalf("got %d terminal records", len(lines)) }
63
+ if len(lines) != len(expected) {
64
+ t.Fatalf("got %d terminal records", len(lines))
65
+ }
58
66
  for index, line := range lines {
59
67
  var record struct {
60
- Schema int `json:"schema"`
61
- Event string `json:"event"`
62
- Cwd string `json:"cwd"`
63
- Data map[string]any `json:"data"`
68
+ Schema int `json:"schema"`
69
+ Event string `json:"event"`
70
+ Cwd string `json:"cwd"`
71
+ Data map[string]any `json:"data"`
72
+ }
73
+ if err := json.Unmarshal([]byte(line), &record); err != nil {
74
+ t.Fatal(err)
64
75
  }
65
- if err := json.Unmarshal([]byte(line), &record); err != nil { t.Fatal(err) }
66
76
  want := expected[index]
67
77
  if record.Schema != 1 || record.Event != "lsp-hints-publication" || record.Cwd != "project" ||
68
78
  record.Data["producer"] != "producer" || record.Data["generation"] != float64(7) ||
@@ -70,7 +80,11 @@ func TestCompletionHintsPublicationPreservesTerminalOutcomes(t *testing.T) {
70
80
  t.Errorf("terminal %d: %s", index, line)
71
81
  }
72
82
  if want.err == "" {
73
- if _, exists := record.Data["error"]; exists { t.Errorf("unexpected terminal error: %s", line) }
74
- } else if record.Data["error"] != want.err { t.Errorf("terminal error: %s", line) }
83
+ if _, exists := record.Data["error"]; exists {
84
+ t.Errorf("unexpected terminal error: %s", line)
85
+ }
86
+ } else if record.Data["error"] != want.err {
87
+ t.Errorf("terminal error: %s", line)
88
+ }
75
89
  }
76
90
  }
@@ -93,12 +93,11 @@ func BuildFiles(prog *driver.Program, selected []string, baseNodes map[string]*N
93
93
  // the compiler AST or into the preceding generation's nodes: resolved is keyed
94
94
  // by *shimast.Node, docHosts holds one per documented declaration, and baseNodes
95
95
  // is the whole prior endpoint index. A consumer that retains the returned Graph
96
- // would otherwise pin all of it for as long as it held the graph —
97
- // internal/graphsymbols retains one for the lifetime of an editor session
98
- // between invalidations, closing the Program while the maps that reference its
99
- // AST live on. Clearing them here rather than at that consumer keeps the
100
- // decision with the producer, which is the only side that knows which fields are
101
- // scratch.
96
+ // would otherwise pin all of it for as long as it held the graph: the resident
97
+ // serve session in cmd/ttscgraph holds a prepared projection until it publishes,
98
+ // and its compiler session can replace and close the Program in the meantime.
99
+ // Clearing them here rather than at that consumer keeps the decision with the
100
+ // producer, which is the only side that knows which fields are scratch.
102
101
  //
103
102
  // ExportedTargets and ImplementationSources are deliberately not cleared: the
104
103
  // shard expansion path in cmd/ttscgraph/serve_shards.go reads both after this
@@ -490,9 +490,9 @@ func nodeFile(id string) string {
490
490
  return parts.path
491
491
  }
492
492
 
493
- // NodeFile is the graph-symbol provider's shared view of the node-id grammar.
494
- // Keeping the parser here prevents its LSP path comparison from drifting from
495
- // the dump producer's edge-evidence lookup.
493
+ // NodeFile is the resident shard producer's shared view of the node-id grammar.
494
+ // Keeping the parser here prevents its per-file shard ownership from drifting
495
+ // from the dump producer's edge-evidence lookup.
496
496
  //
497
497
  // @evidence contracts/common.md#principled-implementation The shared parser requires an unescaped separator and nonempty name/kind suffix, then decodes the reported path. It does not validate the kind vocabulary, path existence or producer authenticity.
498
498
  // @evidence contracts/common.md#clear-and-simple-design One public adapter exposes the same parser used by dump evidence and native graph consumers.
@@ -8,9 +8,8 @@ import (
8
8
  )
9
9
 
10
10
  // ECMALineStarts returns the byte offset of each logical source line. The
11
- // compiler is the authority for which bytes end a line, so graph evidence and
12
- // graph-backed LSP locations cannot drift from its LF, CRLF, CR, LS, and PS
13
- // coordinates.
11
+ // compiler is the authority for which bytes end a line, so graph evidence
12
+ // cannot drift from its LF, CRLF, CR, LS, and PS coordinates.
14
13
  //
15
14
  // @evidence contracts/common.md#principled-implementation Compiler-produced ECMA line boundaries are copied into graph byte-offset coordinates without substituting a newline convention.
16
15
  // @evidence contracts/common.md#clear-and-simple-design This adapter centralizes the compiler-to-graph integer representation so consumers share the same line model.
@@ -29,38 +28,6 @@ func ECMALineStarts(text string) []int {
29
28
  return starts
30
29
  }
31
30
 
32
- // LineEnd returns the byte offset immediately before line's terminator, or the
33
- // end of text for its final logical line. starts must come from ECMALineStarts
34
- // for the same text.
35
- //
36
- // @evidence contracts/common.md#principled-implementation The supplied index bounds one logical line and ECMA terminator widths identify its exclusive content end.
37
- // @evidence contracts/common.md#clear-and-simple-design One bounded scan handles all compiler terminators without allocating a substring or rebuilding the line index.
38
- // @evidence contracts/common.md#prohibited-implementation-shortcuts Invalid line indices return the documented text boundary rather than manufacturing fixture-specific spans.
39
- // @evidence contracts/common.md#meaningful-documentation The native comment identifies byte units, final-line behavior and the same-text index precondition under the documentation skill.
40
- // @evidence contracts/performance.md#efficient-algorithms Index lookup is constant time and scanning costs only the selected line's byte length, with constant temporary space.
41
- // @evidenceExclude contracts/performance.md#reuse-equivalent-work This calculation consumes a caller-owned reusable line index and creates no cross-request computation owner.
42
- // @evidenceExclude contracts/performance.md#bound-retention-and-release-resources No retained state or native resource is acquired.
43
- // @evidenceExclude contracts/portability.md#os-neutral-implementation LineEnd computes from its arguments only; it opens no file, builds no path and calls no platform or process API.
44
- func LineEnd(text string, starts []int, line int) int {
45
- if line < 0 || line >= len(starts) {
46
- return len(text)
47
- }
48
- if line+1 == len(starts) {
49
- return len(text)
50
- }
51
- for i := starts[line]; i < starts[line+1]; {
52
- if lineTerminatorWidth(text, i) > 0 {
53
- return i
54
- }
55
- _, size := utf8.DecodeRuneInString(text[i:])
56
- if size == 0 {
57
- return len(text)
58
- }
59
- i += size
60
- }
61
- return starts[line+1]
62
- }
63
-
64
31
  // FirstCodeOffset advances over leading whitespace and comments so a graph
65
32
  // span begins at its declaration rather than its leading trivia.
66
33
  //
@@ -35,21 +35,6 @@ func (s *recordingResidentSource) recorded() [][]string {
35
35
  return append([][]string(nil), s.calls...)
36
36
  }
37
37
 
38
- // recordingSymbolProvider counts Invalidate calls; the two answer methods are
39
- // never reached by these notification tests. Invalidate is only ever called from
40
- // the notification path, which is the test's own goroutine.
41
- type recordingSymbolProvider struct{ invalidations int }
42
-
43
- func (p *recordingSymbolProvider) DocumentSymbols(string) ([]LSPDocumentSymbol, error) {
44
- return nil, nil
45
- }
46
-
47
- func (p *recordingSymbolProvider) References(string, LSPPosition, bool) ([]LSPLocation, error) {
48
- return nil, nil
49
- }
50
-
51
- func (p *recordingSymbolProvider) Invalidate() { p.invalidations++ }
52
-
53
38
  // testFileURI renders an absolute path as the file URI an editor would send.
54
39
  func testFileURI(path string) string {
55
40
  return "file:///" + strings.TrimPrefix(filepath.ToSlash(path), "/")
@@ -68,24 +53,24 @@ func didOpenEnvelope(uri string, text string) Envelope {
68
53
  }
69
54
 
70
55
  // TestLSPCleanDidOpenRefreshesCompilerState checks invalidation dispatch on
71
- // directly supplied clean and dirty didOpen envelopes. The resident source and
72
- // symbol provider are recording instances, not actual compiler-backed caches.
56
+ // directly supplied clean and dirty didOpen envelopes. The resident source is a
57
+ // recording instance, not an actual compiler-backed cache.
73
58
  //
74
- // Matching disk should dispatch one invalidation to each recording instance and
75
- // name the clean URI; a dirty second file should not increase either count.
59
+ // Matching disk should dispatch one resident invalidation naming the clean URI;
60
+ // a dirty second file should not add another.
76
61
  // Returning handled=false establishes eligibility for forwarding, not an actual
77
62
  // upstream frame. Program freshness and published diagnostic content are not
78
63
  // observed, and the scheduled NullPluginSource diagnostics are not joined here.
79
64
  //
80
- // 1. Write a file, open it with the same text, and assert both caches were
81
- // refreshed and the resident refresh named that document's URI.
65
+ // 1. Write a file, open it with the same text, and assert the resident Program
66
+ // was refreshed for exactly that document's URI.
82
67
  // 2. Open a second file with text that differs from disk.
83
- // 3. Assert neither recorder gained a call and the notification is not handled.
68
+ // 3. Assert the recorder gained no call and the notification is not handled.
84
69
  //
85
- // @evidence contracts/testing.md#behavioral-verification A clean direct didOpen produces one recorded symbol invalidation and one resident invalidation naming its URI; a dirty second open preserves both counts. Both return handled=false. Actual compiler cache refresh, diagnostic content, upstream forwarding, and asynchronous completion are not asserted.
86
- // @evidence contracts/testing.md#independent-expectations The refresh counts and the named URI are literal expectations for the two opened files.
70
+ // @evidence contracts/testing.md#behavioral-verification A clean direct didOpen produces one resident invalidation naming its URI; a dirty second open adds none. Both return handled=false. Actual compiler cache refresh, diagnostic content, upstream forwarding, and asynchronous completion are not asserted.
71
+ // @evidence contracts/testing.md#independent-expectations The refresh count and the named URI are literal expectations for the two opened files.
87
72
  // @evidence contracts/testing.md#distinguishing-cases Two owned files have the same disk bytes, but the clean buffer matches and the dirty buffer differs. They also have different URIs and run sequentially on one proxy; this is not a one-variable-only or reversed-order experiment.
88
- // @evidence contracts/testing.md#execution-ownership Owns two temporary native files and a NewProxy with supported instance-owned recording PluginSource/SymbolProvider and io.Discard streams. Direct handleEditorEnvelope reads buffer/disk state and schedules NullPluginSource diagnostics on the clean branch; no join or diagnostics-result oracle is claimed. No actual compiler Program, sidecar, product CLI, installed consumer, or upstream server runs.
73
+ // @evidence contracts/testing.md#execution-ownership Owns two temporary native files and a NewProxy with supported instance-owned recording PluginSource and io.Discard streams. Direct handleEditorEnvelope reads buffer/disk state and schedules NullPluginSource diagnostics on the clean branch; no join or diagnostics-result oracle is claimed. No actual compiler Program, sidecar, product CLI, installed consumer, or upstream server runs.
89
74
  func TestLSPCleanDidOpenRefreshesCompilerState(t *testing.T) {
90
75
  dir := t.TempDir()
91
76
  clean := filepath.Join(dir, "clean.ts")
@@ -95,12 +80,10 @@ func TestLSPCleanDidOpenRefreshesCompilerState(t *testing.T) {
95
80
  cleanURI := testFileURI(clean)
96
81
 
97
82
  plugins := &recordingResidentSource{}
98
- symbols := &recordingSymbolProvider{}
99
83
  proxy := NewProxy(ProxyOptions{
100
- EditorOut: io.Discard,
101
- UpstreamIn: io.Discard,
102
- Source: plugins,
103
- SymbolProvider: symbols,
84
+ EditorOut: io.Discard,
85
+ UpstreamIn: io.Discard,
86
+ Source: plugins,
104
87
  })
105
88
 
106
89
  handled, err := proxy.handleEditorEnvelope(
@@ -113,9 +96,6 @@ func TestLSPCleanDidOpenRefreshesCompilerState(t *testing.T) {
113
96
  if handled {
114
97
  t.Fatal("clean didOpen was answered locally instead of forwarded to tsgo")
115
98
  }
116
- if symbols.invalidations != 1 {
117
- t.Errorf("clean didOpen symbol invalidations = %d, want 1", symbols.invalidations)
118
- }
119
99
  if len(plugins.recorded()) != 1 {
120
100
  t.Fatalf("clean didOpen resident invalidations = %d, want 1", len(plugins.recorded()))
121
101
  }
@@ -137,9 +117,6 @@ func TestLSPCleanDidOpenRefreshesCompilerState(t *testing.T) {
137
117
  if handled {
138
118
  t.Fatal("dirty didOpen was answered locally instead of forwarded to tsgo")
139
119
  }
140
- if symbols.invalidations != 1 {
141
- t.Errorf("dirty didOpen refreshed the symbol provider: invalidations = %d, want 1", symbols.invalidations)
142
- }
143
120
  if len(plugins.recorded()) != 1 {
144
121
  t.Errorf("dirty didOpen refreshed the resident daemon: calls = %v, want only the clean open's", plugins.recorded())
145
122
  }
@@ -22,10 +22,14 @@ func TestLSPHintsRefreshIgnoresASupersededGeneration(t *testing.T) {
22
22
 
23
23
  if !source.storeCompletionHints(plugin, 2, []LSPCompletionHint{
24
24
  {Scope: "jsdoc", After: "@", Items: []LSPCompletionItem{{Insert: "returns"}}},
25
- }) { t.Error("newer corpus was not accepted") }
25
+ }) {
26
+ t.Error("newer corpus was not accepted")
27
+ }
26
28
  if source.storeCompletionHints(plugin, 1, []LSPCompletionHint{
27
29
  {Scope: "jsdoc", After: "@", Items: []LSPCompletionItem{{Insert: "stale"}}},
28
- }) { t.Error("stale corpus claimed publication") }
30
+ }) {
31
+ t.Error("stale corpus claimed publication")
32
+ }
29
33
 
30
34
  hints := source.CompletionHints()
31
35
  if len(hints) != 1 || len(hints[0].Items) != 1 || hints[0].Items[0].Insert != "returns" {
@@ -34,7 +38,9 @@ func TestLSPHintsRefreshIgnoresASupersededGeneration(t *testing.T) {
34
38
 
35
39
  if !source.storeCompletionHints(plugin, 3, []LSPCompletionHint{
36
40
  {Scope: "jsdoc", After: "@", Items: []LSPCompletionItem{{Insert: "param"}}},
37
- }) { t.Error("new generation was not accepted") }
41
+ }) {
42
+ t.Error("new generation was not accepted")
43
+ }
38
44
  hints = source.CompletionHints()
39
45
  if len(hints) != 1 || hints[0].Items[0].Insert != "param" {
40
46
  t.Fatalf("the guard rejected a newer generation as well: %#v", hints)
@@ -120,6 +120,25 @@ type LSPDiagnostic struct {
120
120
  Message string `json:"message"`
121
121
  }
122
122
 
123
+ // LSPLocation is the wire shape of an LSP Location: a range inside the document
124
+ // its URI names. Diagnostic related information carries one per secondary site.
125
+ //
126
+ // @evidence contracts/common.md#principled-implementation URI plus range identifies a site independently of the document currently open in the editor.
127
+ // @evidence contracts/common.md#clear-and-simple-design Related diagnostic information shares this one location value.
128
+ // @evidence contracts/common.md#prohibited-implementation-shortcuts Locations are producer values rather than guessed local filenames.
129
+ // @evidence contracts/common.md#meaningful-documentation Native prose names the protocol shape and the related-information use, following the documentation skill.
130
+ // @evidenceExclude contracts/performance.md#bound-retention-and-release-resources A type declaration acquires and holds no runtime resource.
131
+ // @evidenceExclude contracts/performance.md#efficient-algorithms A type declaration chooses no processing strategy.
132
+ // @evidenceExclude contracts/performance.md#reuse-equivalent-work A type declaration computes nothing, so there is no work to share.
133
+ // @evidenceExclude contracts/portability.md#os-neutral-implementation This type declaration describes a data shape only; it opens no file, builds no path and branches on no platform.
134
+ type LSPLocation struct {
135
+ // URI identifies the target document using protocol spelling.
136
+ URI string `json:"uri"`
137
+
138
+ // Range locates the site in that document's session UTF-16 coordinates.
139
+ Range LSPRange `json:"range"`
140
+ }
141
+
123
142
  // LSPDiagnosticRelatedInformation is one entry of a diagnostic's
124
143
  // relatedInformation: a secondary location with a message. It reuses the
125
144
  // package's LSPLocation (a URI plus a range). The proxy does not read it — it