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,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
3
|
import { type ProjectInputPathIdentity } from "../../../internal/pathIdentity/ProjectInputPathIdentity";
|
|
4
|
+
import { type ProjectInputPathIdentityContext } from "../../../internal/pathIdentity/ProjectInputPathIdentityContext";
|
|
4
5
|
import { createProjectInputPathIdentityContext } from "../../../internal/pathIdentity/createProjectInputPathIdentityContext";
|
|
5
6
|
import { literalGlobRoot } from "./literalGlobRoot";
|
|
6
7
|
|
|
@@ -12,6 +13,8 @@ import { literalGlobRoot } from "./literalGlobRoot";
|
|
|
12
13
|
* lane only after the surrounding change detector admits the event; unchanged
|
|
13
14
|
* bytes remain quiet before this classifier is observed.
|
|
14
15
|
*
|
|
16
|
+
* @param identities The decision's identity transaction; absence creates a
|
|
17
|
+
* native one for this classification alone.
|
|
15
18
|
* @evidence contracts/common.md#principled-implementation Exact selection files and immediate resolution-directory entries choose execution reload; a strictly nested declared glob can explain its own ancestor's membership delta without exempting unrelated selection changes.
|
|
16
19
|
* @evidence contracts/common.md#clear-and-simple-design Exact-file membership, immediate-entry membership and causal explanations have distinct helpers under one classification operation.
|
|
17
20
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts A data exemption must explain the same directory's observed delta, not cancel every reload because some unrelated data event occurred.
|
|
@@ -22,15 +25,17 @@ import { literalGlobRoot } from "./literalGlobRoot";
|
|
|
22
25
|
*
|
|
23
26
|
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Identity caches and predicate collections are local to one decision, without a resident historical cache or native handle.
|
|
24
27
|
*/
|
|
25
|
-
export function projectInputReloadEventShouldNotify(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
export function projectInputReloadEventShouldNotify(
|
|
29
|
+
input: {
|
|
30
|
+
causedBy?: readonly string[];
|
|
31
|
+
changed?: string;
|
|
32
|
+
changedInputs: readonly string[];
|
|
33
|
+
reloadDirectories?: readonly string[];
|
|
34
|
+
reloadFiles: readonly string[];
|
|
35
|
+
globs?: readonly string[];
|
|
36
|
+
},
|
|
37
|
+
identities: ProjectInputPathIdentityContext = createProjectInputPathIdentityContext(),
|
|
38
|
+
): boolean {
|
|
34
39
|
const reloadFiles = new Set(
|
|
35
40
|
input.reloadFiles.map((location) => identities.resolve(location).key),
|
|
36
41
|
);
|
|
@@ -9,6 +9,7 @@ import { readProjectConfig } from "../../../compiler/internal/project/readProjec
|
|
|
9
9
|
import { SidecarEnvironment } from "../../../compiler/internal/sharedHost/SidecarEnvironment";
|
|
10
10
|
import { E2ETrace } from "../../../internal/E2ETrace";
|
|
11
11
|
import { OwnedSynchronousProcess } from "../../../internal/OwnedSynchronousProcess";
|
|
12
|
+
import { TtsxNodeOptions } from "../../../internal/TtsxNodeOptions";
|
|
12
13
|
import { createCanonicalTempDirectory } from "../../../internal/createCanonicalTempDirectory";
|
|
13
14
|
import { javascriptRuntimeCapabilities } from "../../../internal/javascriptRuntimeCapabilities";
|
|
14
15
|
import { resolveNodeBinary } from "../../../internal/resolveNodeBinary";
|
|
@@ -445,6 +446,7 @@ export function loadProjectPlugins(options: {
|
|
|
445
446
|
effectiveEnv,
|
|
446
447
|
watchReplacements,
|
|
447
448
|
packageReaders,
|
|
449
|
+
sourceIdentities,
|
|
448
450
|
))
|
|
449
451
|
if (reportsPluginSource(directory)) interests.add(directory);
|
|
450
452
|
}
|
|
@@ -534,6 +536,7 @@ export function loadProjectPlugins(options: {
|
|
|
534
536
|
effectiveEnv,
|
|
535
537
|
watchReplacements,
|
|
536
538
|
packageReaders,
|
|
539
|
+
sourceIdentities,
|
|
537
540
|
);
|
|
538
541
|
// Admission can now narrow the provisional interests without restamping the
|
|
539
542
|
// retained package/module baselines. Final host errors still keep these roots.
|
|
@@ -1438,9 +1441,13 @@ class CommonJsDescriptorLoadError extends Error {
|
|
|
1438
1441
|
function loadCommonJsDescriptor(
|
|
1439
1442
|
request: string,
|
|
1440
1443
|
context: ITtscPluginFactoryContext,
|
|
1441
|
-
|
|
1444
|
+
loadEnv: NodeJS.ProcessEnv,
|
|
1442
1445
|
descriptorCache: DescriptorCacheOptions,
|
|
1443
1446
|
): IsolatedPluginDescriptor {
|
|
1447
|
+
// The evaluator is ttsc's own helper: inside a ttsx run it gets the user's
|
|
1448
|
+
// NODE_OPTIONS, not ttsx's preloads, which would refuse the descriptor cache
|
|
1449
|
+
// and run in every evaluation (#1740). The runtime probe below does the same.
|
|
1450
|
+
const effectiveEnv = TtsxNodeOptions.forHelpers(loadEnv);
|
|
1444
1451
|
const runtime = pluginDescriptorRuntimeBinary(effectiveEnv);
|
|
1445
1452
|
const ttsx = effectiveEnv.TTSC_TTSX_BINARY ?? process.env.TTSC_TTSX_BINARY;
|
|
1446
1453
|
const runtimeCapabilities = javascriptRuntimeCapabilities(
|
|
@@ -2349,6 +2356,7 @@ function pluginBuildDirectories(
|
|
|
2349
2356
|
env: NodeJS.ProcessEnv,
|
|
2350
2357
|
replacements?: Map<string, readonly string[]>,
|
|
2351
2358
|
readers?: Map<string, SourcePluginWorkspace.GoModReader>,
|
|
2359
|
+
identities?: PluginContentIdentities.Store,
|
|
2352
2360
|
): string[] {
|
|
2353
2361
|
const directories = new Set<string>();
|
|
2354
2362
|
for (const record of records) {
|
|
@@ -2363,6 +2371,7 @@ function pluginBuildDirectories(
|
|
|
2363
2371
|
env,
|
|
2364
2372
|
replacements,
|
|
2365
2373
|
readers,
|
|
2374
|
+
identities,
|
|
2366
2375
|
))
|
|
2367
2376
|
directories.add(directory);
|
|
2368
2377
|
}
|
|
@@ -2382,17 +2391,20 @@ function pluginReplacementWatchDirectories(
|
|
|
2382
2391
|
env: NodeJS.ProcessEnv,
|
|
2383
2392
|
observed?: Map<string, readonly string[]>,
|
|
2384
2393
|
readers?: Map<string, SourcePluginWorkspace.GoModReader>,
|
|
2394
|
+
identities?: PluginContentIdentities.Store,
|
|
2385
2395
|
): readonly string[] {
|
|
2386
2396
|
const previous = observed?.get(moduleRoot);
|
|
2387
2397
|
if (previous !== undefined) return previous;
|
|
2388
2398
|
let directories: readonly string[];
|
|
2389
2399
|
try {
|
|
2400
|
+
// The load's store goes with the reader this registers first (#1739).
|
|
2390
2401
|
directories = pluginModuleReplaceDirectories(
|
|
2391
2402
|
moduleRoot,
|
|
2392
2403
|
env,
|
|
2393
2404
|
undefined,
|
|
2394
2405
|
undefined,
|
|
2395
2406
|
readers,
|
|
2407
|
+
identities,
|
|
2396
2408
|
).map((replacement) => replacement.directory);
|
|
2397
2409
|
} catch {
|
|
2398
2410
|
// An unreadable manifest/tool is not valid metadata. The already observed
|
|
@@ -30,13 +30,19 @@ export function resolveNativeSource(
|
|
|
30
30
|
plugin: ITtscPlugin,
|
|
31
31
|
config: ITtscProjectPluginConfig,
|
|
32
32
|
index: number,
|
|
33
|
-
options?: {
|
|
33
|
+
options?: {
|
|
34
|
+
env?: NodeJS.ProcessEnv;
|
|
35
|
+
observation?: NativeSourcePackages.Package;
|
|
36
|
+
},
|
|
34
37
|
): { kind: "executable" | "linked"; moduleRoot: string } {
|
|
35
38
|
const label = pluginLabel(plugin, config, index);
|
|
36
39
|
requirePluginSource(source, label);
|
|
37
40
|
const { moduleRoot } = resolvePluginGoModule(source, label);
|
|
38
|
-
const observation =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const observation =
|
|
42
|
+
options?.observation ??
|
|
43
|
+
NativeSourcePackages.propose(
|
|
44
|
+
[{ source, label, ownModule: plugin.stage === "check" }],
|
|
45
|
+
SidecarEnvironment.merge(options?.env ?? process.env),
|
|
46
|
+
)[0]!.observation;
|
|
41
47
|
return { kind: NativeSourcePackages.kind(observation, label), moduleRoot };
|
|
42
48
|
}
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { resolvePhysicalPath } from "../../../internal/pathIdentity/resolvePhysicalPath";
|
|
5
5
|
import { GoToolResolution } from "./GoToolResolution";
|
|
6
6
|
import type { IPluginModuleReplaceDirectory } from "./IPluginModuleReplaceDirectory";
|
|
7
|
+
import type { PluginContentIdentities } from "./PluginContentIdentities";
|
|
7
8
|
import { SourcePluginWorkspace } from "./SourcePluginWorkspace";
|
|
8
9
|
import { resolveGoCompiler } from "./resolveGoCompiler";
|
|
9
10
|
import { selectPluginModuleReplaceDirectories } from "./selectPluginModuleReplaceDirectories";
|
|
@@ -30,6 +31,8 @@ import { selectPluginModuleReplaceDirectories } from "./selectPluginModuleReplac
|
|
|
30
31
|
* @param env The build's effective environment.
|
|
31
32
|
* @param goBinary The Go tool the build runs, or `undefined` to resolve it as
|
|
32
33
|
* the build does.
|
|
34
|
+
* @param identities The load's record store, carried by a reader this call
|
|
35
|
+
* registers so the load's later reads reuse recorded syntax.
|
|
33
36
|
* @returns Each replacement outside the module, sorted by module path: the
|
|
34
37
|
* replaced module path and version, the target as `go.mod` spells it, and the
|
|
35
38
|
* target's absolute path.
|
|
@@ -52,6 +55,7 @@ export function pluginModuleReplaceDirectories(
|
|
|
52
55
|
goBinary?: string,
|
|
53
56
|
reader?: SourcePluginWorkspace.GoModReader,
|
|
54
57
|
readers?: Map<string, SourcePluginWorkspace.GoModReader>,
|
|
58
|
+
identities?: PluginContentIdentities.Store,
|
|
55
59
|
): IPluginModuleReplaceDirectory[] {
|
|
56
60
|
const root = path.resolve(moduleRoot);
|
|
57
61
|
let text: string;
|
|
@@ -72,7 +76,12 @@ export function pluginModuleReplaceDirectories(
|
|
|
72
76
|
root,
|
|
73
77
|
);
|
|
74
78
|
const parsed = (
|
|
75
|
-
reader ??
|
|
79
|
+
reader ??
|
|
80
|
+
// WARNING (#1739): the first reader registered for a Go binary serves the
|
|
81
|
+
// whole load, so it must carry the load's record store, or every later
|
|
82
|
+
// reader request of the load gets a store-less one and the go.mod syntax
|
|
83
|
+
// records are never read or written.
|
|
84
|
+
SourcePluginWorkspace.createGoModReader(go, root, env, readers, identities)
|
|
76
85
|
).read(root);
|
|
77
86
|
// Parsed syntax may be shared; physical containment is current native state
|
|
78
87
|
// and must be recomputed even when these manifest bytes are unchanged.
|
|
@@ -86,8 +86,8 @@ export interface TtscCommonOptions {
|
|
|
86
86
|
}[];
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
|
-
* Retained native compiler argument base when project selection redirects
|
|
90
|
-
*
|
|
89
|
+
* Retained native compiler argument base when project selection redirects the
|
|
90
|
+
* Program to a different root. Absent retains the selected project root.
|
|
91
91
|
* Response frames and relative native options use this base; plugin/rule
|
|
92
92
|
* context and config-relative paths retain their separate project authority.
|
|
93
93
|
*/
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
package graphsymbols
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"testing"
|
|
5
|
-
"unicode/utf8"
|
|
6
|
-
|
|
7
|
-
"github.com/samchon/ttsc/packages/ttsc/internal/graph"
|
|
8
|
-
"github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
// TestPositionHelpersFollowECMALineTerminators verifies graph-backed LSP
|
|
12
|
-
// positions use the compiler's LF, CRLF, CR, LS, and PS line boundaries.
|
|
13
|
-
//
|
|
14
|
-
// An offset-to-position helper that counted only LF while its inverse counted
|
|
15
|
-
// CR too would map an editor cursor to a graph offset that returns on a
|
|
16
|
-
// different line. Literal starts and ends establish three authored logical
|
|
17
|
-
// lines; the test walks their UTF-8 boundaries, including an emoji, and requires
|
|
18
|
-
// a round trip. Columns are not compared to literal UTF-16 counts, so symmetric
|
|
19
|
-
// column mistakes can pass. No editor/server request is exercised.
|
|
20
|
-
//
|
|
21
|
-
// 1. Build three lines with each ECMAScript terminator.
|
|
22
|
-
// 2. Enumerate every valid cursor boundary before each terminator.
|
|
23
|
-
// 3. Convert each offset to UTF-16 LSP position and back to the same offset.
|
|
24
|
-
//
|
|
25
|
-
// @evidence contracts/testing.md#behavioral-verification Verifies graph-backed LSP positions use the compiler's LF, CRLF, CR, LS, and PS line boundaries.
|
|
26
|
-
// @evidence contracts/testing.md#independent-expectations Each case embeds one ECMAScript terminator (LF, CRLF, CR, LS or PS) in a three-line literal, so the expected line count (three) and the line of every offset follow from the language's line-terminator definition. Columns are checked only by an offset -> position -> offset round trip over every UTF-8 rune boundary (including the two-unit emoji), not against literal UTF-16 column numbers, so a column error made symmetrically by both helpers would not be detected.
|
|
27
|
-
// @evidence contracts/testing.md#distinguishing-cases Build three lines with each ECMAScript terminator; Enumerate every valid cursor boundary before each terminator; Convert each offset to UTF-16 LSP position and back to the same offset; the five terminator subtests differ only in the terminator, and a position on the line index just past the last line must not resolve (negative case). No empty-text or out-of-range-column input is exercised here.
|
|
28
|
-
// @evidence contracts/testing.md#execution-ownership Directly calls the maintained line and position helpers on authored UTF-8 strings in this process. Literal byte boundaries establish enumeration; round-trip column expectations share the two converters and are not a separate UTF-16 oracle. It creates no native fixture, loads no Program, installs no consumer, and starts no product process or editor/server connection.
|
|
29
|
-
func TestPositionHelpersFollowECMALineTerminators(t *testing.T) {
|
|
30
|
-
cases := []struct {
|
|
31
|
-
name string
|
|
32
|
-
terminator string
|
|
33
|
-
}{
|
|
34
|
-
{name: "LF", terminator: "\n"},
|
|
35
|
-
{name: "CRLF", terminator: "\r\n"},
|
|
36
|
-
{name: "CR", terminator: "\r"},
|
|
37
|
-
{name: "LS", terminator: "\u2028"},
|
|
38
|
-
{name: "PS", terminator: "\u2029"},
|
|
39
|
-
}
|
|
40
|
-
for _, tc := range cases {
|
|
41
|
-
t.Run(tc.name, func(t *testing.T) {
|
|
42
|
-
text := "alpha😀" + tc.terminator + "beta" + tc.terminator + "gamma"
|
|
43
|
-
starts := graph.ECMALineStarts(text)
|
|
44
|
-
if len(starts) != 3 {
|
|
45
|
-
t.Fatalf("line starts = %v, want three lines", starts)
|
|
46
|
-
}
|
|
47
|
-
expectedStarts := []int{0, 9 + len(tc.terminator), 13 + 2*len(tc.terminator)}
|
|
48
|
-
expectedEnds := []int{9, 13 + len(tc.terminator), len(text)}
|
|
49
|
-
for line, start := range starts {
|
|
50
|
-
end := graph.LineEnd(text, starts, line)
|
|
51
|
-
if start != expectedStarts[line] || end != expectedEnds[line] {
|
|
52
|
-
t.Fatalf("line %d boundaries = (%d, %d), want (%d, %d)", line, start, end, expectedStarts[line], expectedEnds[line])
|
|
53
|
-
}
|
|
54
|
-
for offset := start; ; {
|
|
55
|
-
position := offsetToPosition(text, offset)
|
|
56
|
-
if position.Line != line {
|
|
57
|
-
t.Fatalf("offset %d position line = %d, want %d", offset, position.Line, line)
|
|
58
|
-
}
|
|
59
|
-
restored, ok := lspPositionToOffset(text, position)
|
|
60
|
-
if !ok || restored != offset {
|
|
61
|
-
t.Fatalf("offset %d -> %+v -> (%d, %t), want itself", offset, position, restored, ok)
|
|
62
|
-
}
|
|
63
|
-
if offset == end {
|
|
64
|
-
break
|
|
65
|
-
}
|
|
66
|
-
_, size := utf8.DecodeRuneInString(text[offset:])
|
|
67
|
-
if size == 0 {
|
|
68
|
-
t.Fatal("unexpected zero-width UTF-8 rune")
|
|
69
|
-
}
|
|
70
|
-
offset += size
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if _, ok := lspPositionToOffset(text, lspserver.LSPPosition{Line: len(starts), Character: 0}); ok {
|
|
74
|
-
t.Fatal("position on a nonexistent source line must not resolve")
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
}
|
|
78
|
-
}
|