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.
- package/cmd/ttsc/compiler_argument_cwd_test.go +38 -38
- package/cmd/ttscserver/main.go +2 -12
- package/cmd/ttscserver/resolve_launch_uses_native_flag_authority_test.go +7 -7
- package/driver/lsp.go +4 -43
- package/driver/rewrite.go +1 -1
- package/internal/e2etrace/completion_hints.go +3 -3
- package/internal/e2etrace/completion_hints_test.go +26 -12
- package/internal/graph/build.go +5 -6
- package/internal/graph/graph.go +3 -3
- package/internal/graph/line_model.go +2 -35
- package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +13 -36
- package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +9 -3
- package/internal/lspserver/lsp_plugin.go +19 -0
- package/internal/lspserver/lsp_proxy.go +9 -129
- package/internal/lspserver/lsp_server.go +0 -8
- package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +6 -36
- package/internal/sourceprocess/windows_target_owns_suspended_admission_test.go +5 -5
- package/lib/compiler/internal/build/BuildExecution.js +1 -1
- package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
- package/lib/compiler/internal/build/TsgoArguments.js.map +1 -1
- package/lib/compiler/internal/project/CompilerProjectSelection.d.ts +9 -8
- package/lib/compiler/internal/project/CompilerProjectSelection.js +11 -9
- package/lib/compiler/internal/project/CompilerProjectSelection.js.map +1 -1
- package/lib/compiler/internal/project/resolveOwningProjectConfig.d.ts +20 -8
- package/lib/compiler/internal/project/resolveOwningProjectConfig.js +21 -12
- package/lib/compiler/internal/project/resolveOwningProjectConfig.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.d.ts +5 -4
- package/lib/compiler/internal/runSingleFileEmit.js +5 -4
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/flags/ParseOptions.d.ts +2 -2
- package/lib/internal/E2ETrace.js.map +1 -1
- package/lib/internal/OwnedNativeProcess.d.ts +2 -2
- package/lib/internal/OwnedNativeProcess.js +2 -2
- package/lib/internal/SourceNativeRetirement.d.ts +8 -7
- package/lib/internal/SourceNativeRetirement.js +19 -10
- package/lib/internal/SourceNativeRetirement.js.map +1 -1
- package/lib/internal/TtsxNodeOptions.d.ts +79 -0
- package/lib/internal/TtsxNodeOptions.js +121 -0
- package/lib/internal/TtsxNodeOptions.js.map +1 -0
- package/lib/internal/javascriptRuntimeCapabilities.js +4 -1
- package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
- package/lib/internal/parseWindowsDirectoryCaseSensitivity.d.ts +5 -5
- package/lib/internal/parseWindowsDirectoryCaseSensitivity.js +5 -5
- package/lib/internal/pathIdentity/createCaseSensitivityObservations.d.ts +35 -0
- package/lib/internal/pathIdentity/createCaseSensitivityObservations.js +209 -0
- package/lib/internal/pathIdentity/createCaseSensitivityObservations.js.map +1 -0
- package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js +2 -143
- package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js.map +1 -1
- package/lib/internal/projectInputPathIdentity.d.ts +1 -0
- package/lib/internal/projectInputPathIdentity.js +1 -0
- package/lib/internal/projectInputPathIdentity.js.map +1 -1
- package/lib/launcher/internal/parseTtsxCLI.d.ts +5 -4
- package/lib/launcher/internal/parseTtsxCLI.js +5 -4
- package/lib/launcher/internal/parseTtsxCLI.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/resolveSingleFileOutput.d.ts +8 -9
- package/lib/launcher/internal/resolveSingleFileOutput.js +8 -9
- package/lib/launcher/internal/resolveSingleFileOutput.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +5 -5
- package/lib/launcher/internal/runTtsx.js +5 -2
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtime/RuntimeEntrySources.d.ts +40 -0
- package/lib/launcher/internal/runtime/RuntimeEntrySources.js +57 -0
- package/lib/launcher/internal/runtime/RuntimeEntrySources.js.map +1 -0
- package/lib/launcher/internal/runtime/RuntimeProjectOwnership.d.ts +5 -4
- package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js +8 -6
- package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js.map +1 -1
- package/lib/launcher/internal/runtime/commonJsExportNames.d.ts +2 -1
- package/lib/launcher/internal/runtime/commonJsExportNames.js +2 -1
- package/lib/launcher/internal/runtime/commonJsExportNames.js.map +1 -1
- package/lib/launcher/internal/runtime/installRuntimeHooks.js +35 -9
- package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
- package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js +3 -1
- package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js.map +1 -1
- package/lib/launcher/internal/watch/WatchTopology.d.ts +74 -12
- package/lib/launcher/internal/watch/WatchTopology.js +301 -92
- package/lib/launcher/internal/watch/WatchTopology.js.map +1 -1
- package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.d.ts +4 -1
- package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js +3 -2
- package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js.map +1 -1
- package/lib/plugin/internal/load/loadProjectPlugins.js +13 -7
- package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
- package/lib/plugin/internal/load/resolveNativeSource.js +2 -3
- package/lib/plugin/internal/load/resolveNativeSource.js.map +1 -1
- package/lib/plugin/internal/source/pluginModuleReplaceDirectories.d.ts +4 -1
- package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js +9 -2
- package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js.map +1 -1
- package/lib/structures/internal/TtscCommonOptions.d.ts +2 -2
- package/package.json +9 -9
- package/src/compiler/internal/build/BuildExecution.ts +5 -2
- package/src/compiler/internal/build/TsgoArguments.ts +4 -1
- package/src/compiler/internal/project/CompilerProjectSelection.ts +14 -14
- package/src/compiler/internal/project/resolveOwningProjectConfig.ts +29 -14
- package/src/compiler/internal/runSingleFileEmit.ts +11 -7
- package/src/flags/ParseOptions.ts +2 -2
- package/src/internal/E2ETrace.ts +3 -1
- package/src/internal/OwnedNativeProcess.ts +2 -2
- package/src/internal/SourceNativeRetirement.ts +42 -19
- package/src/internal/TtsxNodeOptions.ts +124 -0
- package/src/internal/javascriptRuntimeCapabilities.ts +6 -1
- package/src/internal/parseWindowsDirectoryCaseSensitivity.ts +5 -5
- package/src/internal/pathIdentity/createCaseSensitivityObservations.ts +242 -0
- package/src/internal/pathIdentity/createFilesystemPathIdentityContext.ts +2 -169
- package/src/internal/projectInputPathIdentity.ts +1 -0
- package/src/launcher/internal/parseTtsxCLI.ts +5 -4
- package/src/launcher/internal/prepareExecution.ts +4 -1
- package/src/launcher/internal/resolveSingleFileOutput.ts +8 -9
- package/src/launcher/internal/runTtsc.ts +5 -5
- package/src/launcher/internal/runTtsx.ts +17 -8
- package/src/launcher/internal/runtime/RuntimeEntrySources.ts +57 -0
- package/src/launcher/internal/runtime/RuntimeProjectOwnership.ts +23 -10
- package/src/launcher/internal/runtime/commonJsExportNames.ts +11 -4
- package/src/launcher/internal/runtime/installRuntimeHooks.ts +55 -13
- package/src/launcher/internal/runtime/resolveRuntimeCleanTargets.ts +17 -5
- package/src/launcher/internal/watch/WatchTopology.ts +387 -99
- package/src/launcher/internal/watch/projectInputReloadEventShouldNotify.ts +14 -9
- package/src/plugin/internal/load/loadProjectPlugins.ts +13 -1
- package/src/plugin/internal/load/resolveNativeSource.ts +10 -4
- package/src/plugin/internal/source/pluginModuleReplaceDirectories.ts +10 -1
- package/src/structures/internal/TtscCommonOptions.ts +2 -2
- package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +0 -78
- package/internal/graphsymbols/provider.go +0 -650
- package/internal/lspserver/lsp_symbols.go +0 -212
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
package main
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"reflect"
|
|
6
|
+
"testing"
|
|
7
7
|
|
|
8
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
}
|
package/cmd/ttscserver/main.go
CHANGED
|
@@ -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
|
|
113
|
-
ExecuteCommandIDPrefix
|
|
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
|
|
50
|
-
args
|
|
51
|
-
cwd
|
|
52
|
-
config
|
|
49
|
+
name string
|
|
50
|
+
args []string
|
|
51
|
+
cwd string
|
|
52
|
+
config string
|
|
53
53
|
explicit bool
|
|
54
|
-
tsgo
|
|
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
|
|
111
|
-
args
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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:
|
|
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":
|
|
26
|
+
"producer": producer,
|
|
27
27
|
"generation": generation,
|
|
28
|
-
"outcome":
|
|
29
|
-
"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
|
|
35
|
-
err
|
|
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 {
|
|
50
|
+
if err != nil {
|
|
51
|
+
t.Fatal(err)
|
|
52
|
+
}
|
|
51
53
|
lines := strings.Split(strings.TrimSuffix(string(body), "\n"), "\n")
|
|
52
|
-
expected := []struct
|
|
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) {
|
|
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
|
|
61
|
-
Event
|
|
62
|
-
Cwd
|
|
63
|
-
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 {
|
|
74
|
-
|
|
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
|
}
|
package/internal/graph/build.go
CHANGED
|
@@ -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
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
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
|
package/internal/graph/graph.go
CHANGED
|
@@ -490,9 +490,9 @@ func nodeFile(id string) string {
|
|
|
490
490
|
return parts.path
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
-
// NodeFile is the
|
|
494
|
-
// Keeping the parser here prevents its
|
|
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
|
|
12
|
-
//
|
|
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
|
|
72
|
-
//
|
|
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
|
|
75
|
-
//
|
|
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
|
|
81
|
-
// refreshed
|
|
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
|
|
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
|
|
86
|
-
// @evidence contracts/testing.md#independent-expectations The refresh
|
|
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
|
|
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:
|
|
101
|
-
UpstreamIn:
|
|
102
|
-
Source:
|
|
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
|
-
}) {
|
|
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
|
-
}) {
|
|
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
|
-
}) {
|
|
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
|