@valbuild/language-server 0.143.1 → 0.144.1
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/CHANGELOG.md +24 -0
- package/README.md +1 -1
- package/dist/declarations/src/diagnostics.d.ts +1 -1
- package/dist/valbuild-language-server.cjs.dev.js +219 -164
- package/dist/valbuild-language-server.cjs.prod.js +219 -164
- package/dist/valbuild-language-server.esm.js +220 -165
- package/package.json +4 -4
|
@@ -7,7 +7,7 @@ import { TextDocuments, TextDocumentSyncKind, createConnection, ProposedFeatures
|
|
|
7
7
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
8
8
|
import crypto from 'crypto';
|
|
9
9
|
import { createRequire } from 'node:module';
|
|
10
|
-
import { fixHandlers, createService, analyzeValModule, createFixPatch, parsePersonalAccessTokenFile, getPersonalAccessTokenPath, startValLogin, awaitValLoginConfirmation, persistPersonalAccessToken, ValLoginError, findAndEvalValConfigFile, patchSourceFile, getSettings, uploadRemoteFile,
|
|
10
|
+
import { fixHandlers, createService, analyzeValModule, createFixPatch, findJsonEntryFilePath, rebaseContentOp, classifyJsonValuesOp, parsePersonalAccessTokenFile, getPersonalAccessTokenPath, startValLogin, awaitValLoginConfirmation, persistPersonalAccessToken, ValLoginError, findAndEvalValConfigFile, patchSourceFile, getSettings, uploadRemoteFile, planJsonValuesEntryExtraction, extractVideoMetadataFromFile, extractImageMetadata, extractFileMetadata, findDefaultExport } from '@valbuild/server';
|
|
11
11
|
import { resolveSchemaSourceFixes, SCHEMA_SOURCE_FIXES, getRoutesWithModulePaths } from '@valbuild/shared/internal';
|
|
12
12
|
import { result } from '@valbuild/core/fp';
|
|
13
13
|
import { JSONOps, applyPatch, deepClone } from '@valbuild/core/patch';
|
|
@@ -172,7 +172,7 @@ var packageJson = {
|
|
|
172
172
|
"lsp",
|
|
173
173
|
"language-server"
|
|
174
174
|
],
|
|
175
|
-
version: "0.
|
|
175
|
+
version: "0.144.1",
|
|
176
176
|
bin: {
|
|
177
177
|
"val-language-server": "./bin.js"
|
|
178
178
|
},
|
|
@@ -1717,8 +1717,8 @@ function longestResolvedPrefixRange(modulePath, modulePathMap) {
|
|
|
1717
1717
|
/**
|
|
1718
1718
|
* Gallery checks core emits unconditionally.
|
|
1719
1719
|
*
|
|
1720
|
-
* `RecordSchema.validate` attaches these to every `s.
|
|
1721
|
-
* / `s.
|
|
1720
|
+
* `RecordSchema.validate` attaches these to every `s.imagedir()` / `s.filedir()`
|
|
1721
|
+
* / `s.videodir()` module whether or not anything is actually wrong (see
|
|
1722
1722
|
* `packages/core/src/schema/record.ts`): they are placeholders asking someone to
|
|
1723
1723
|
* go and look. `val validate` looks by running the matching fix handler, which
|
|
1724
1724
|
* reports success when the directory is unique and every file is accounted for.
|
|
@@ -1828,7 +1828,7 @@ function dropDeferredPlaceholders(validation) {
|
|
|
1828
1828
|
*
|
|
1829
1829
|
* `ImageSchema.validate` reports this (`packages/core/src/schema/image.ts`:
|
|
1830
1830
|
* "The gallery does not have an image at '…'"), and `VideoSchema.validate` the
|
|
1831
|
-
* same for a field picked from an `s.
|
|
1831
|
+
* same for a field picked from an `s.videodir()` ("The set does not have a
|
|
1832
1832
|
* video at '…'"), but neither attaches a `ValidationFix`,
|
|
1833
1833
|
* because the remedy is not a change to this module: either the gallery gains an
|
|
1834
1834
|
* entry, or the file moves into the gallery's directory. Both are edits to
|
|
@@ -2016,6 +2016,144 @@ function toModuleFilePath(valRoot, uri) {
|
|
|
2016
2016
|
return `/${relative.split(path.sep).join("/")}`;
|
|
2017
2017
|
}
|
|
2018
2018
|
|
|
2019
|
+
const jsonOps = new JSONOps();
|
|
2020
|
+
|
|
2021
|
+
/**
|
|
2022
|
+
* Turning a fix patch into an edit when the value it fixes is NOT in the
|
|
2023
|
+
* `.val.ts` the diagnostic was reported on.
|
|
2024
|
+
*
|
|
2025
|
+
* A `.jsonValues()` record keeps each entry's value in its own `*.val.json`; the
|
|
2026
|
+
* `.val.ts` holds `c.json(() => import("./x.val.json"))` and nothing below it.
|
|
2027
|
+
* So a fix for, say, an `s.image()` inside an entry produces a patch whose path
|
|
2028
|
+
* starts at the module root (`["/jobb/student", "pageImage", "width"]`) but
|
|
2029
|
+
* whose target lives in another file entirely.
|
|
2030
|
+
*
|
|
2031
|
+
* Applying such a patch to the `.val.ts` cannot work: the walk reaches the
|
|
2032
|
+
* `c.json(...)` call expression and `TSOps` refuses it ("Expression must be a
|
|
2033
|
+
* literal"). That refusal is why an editor silently offered no quick fix at all
|
|
2034
|
+
* for anything inside an entry — `computeFixEdit` saw an error result and
|
|
2035
|
+
* dropped the action.
|
|
2036
|
+
*
|
|
2037
|
+
* The routing here is the same one the Studio's publish and `val validate --fix`
|
|
2038
|
+
* use — {@link classifyJsonValuesOp} to find the entry, {@link rebaseContentOp}
|
|
2039
|
+
* to drop the entry-key prefix — so all three write the same file the same way.
|
|
2040
|
+
* What is specific to the editor is only the last step: the result comes back as
|
|
2041
|
+
* a `TextEdit` against the entry's document rather than as a write to disk, so
|
|
2042
|
+
* the edit goes through the editor's undo stack and respects an unsaved buffer.
|
|
2043
|
+
*/
|
|
2044
|
+
|
|
2045
|
+
function jsonEntryEditFor({
|
|
2046
|
+
patch,
|
|
2047
|
+
schema,
|
|
2048
|
+
moduleFilePath,
|
|
2049
|
+
valTsText,
|
|
2050
|
+
valRoot,
|
|
2051
|
+
read
|
|
2052
|
+
}) {
|
|
2053
|
+
if (!schema) {
|
|
2054
|
+
return undefined;
|
|
2055
|
+
}
|
|
2056
|
+
const entry = entryOpsOf(patch, schema);
|
|
2057
|
+
if (!entry) {
|
|
2058
|
+
return undefined;
|
|
2059
|
+
}
|
|
2060
|
+
const jsonFilePath = findJsonEntryFilePath(moduleFilePath, ts.createSourceFile(moduleFilePath, valTsText, ts.ScriptTarget.ES2020), entry.entryKey);
|
|
2061
|
+
if (!jsonFilePath) {
|
|
2062
|
+
return undefined;
|
|
2063
|
+
}
|
|
2064
|
+
const absolutePath = path.join(valRoot, jsonFilePath);
|
|
2065
|
+
const before = read?.(absolutePath) ?? readFile(absolutePath);
|
|
2066
|
+
if (before === undefined) {
|
|
2067
|
+
return undefined;
|
|
2068
|
+
}
|
|
2069
|
+
let content;
|
|
2070
|
+
try {
|
|
2071
|
+
content = JSON.parse(before);
|
|
2072
|
+
} catch {
|
|
2073
|
+
// A malformed entry file is already reported as a load error; offering an
|
|
2074
|
+
// edit computed from a guess at its contents would be worse than offering
|
|
2075
|
+
// nothing.
|
|
2076
|
+
return undefined;
|
|
2077
|
+
}
|
|
2078
|
+
for (const op of entry.ops) {
|
|
2079
|
+
// Root-only, like the rest of the `.jsonValues()` machinery, so the prefix
|
|
2080
|
+
// to drop is exactly the entry key.
|
|
2081
|
+
const rebased = rebaseContentOp(op, 1);
|
|
2082
|
+
if (result.isErr(rebased)) {
|
|
2083
|
+
return undefined;
|
|
2084
|
+
}
|
|
2085
|
+
const applied = applyPatch(deepClone(content), jsonOps, [rebased.value]);
|
|
2086
|
+
if (result.isErr(applied)) {
|
|
2087
|
+
return undefined;
|
|
2088
|
+
}
|
|
2089
|
+
content = applied.value;
|
|
2090
|
+
}
|
|
2091
|
+
// Re-serialising the whole value and diffing it is what keeps this honest:
|
|
2092
|
+
// the alternative is editing JSON text by hand, and the fix would then differ
|
|
2093
|
+
// from what the CLI writes. `minimalTextEdit` narrows the result to the region
|
|
2094
|
+
// that actually changed, so a two-space-indented file (what prettier and the
|
|
2095
|
+
// CLI both produce) yields an edit covering the changed properties and no
|
|
2096
|
+
// more. A file indented differently gets a correct but larger edit.
|
|
2097
|
+
const after = JSON.stringify(content, null, 2) + (before.endsWith("\n") ? "\n" : "");
|
|
2098
|
+
const uri = pathToUri(absolutePath);
|
|
2099
|
+
const edit = minimalTextEdit(before, after, TextDocument.create(uri, "json", 0, before));
|
|
2100
|
+
return edit && {
|
|
2101
|
+
uri,
|
|
2102
|
+
edit
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
/**
|
|
2107
|
+
* The ops of `patch`, if every one of them edits the content of ONE
|
|
2108
|
+
* `.jsonValues()` entry. `undefined` for anything else — an ordinary module, a
|
|
2109
|
+
* patch that also touches the `.val.ts`, or one that adds or removes a whole
|
|
2110
|
+
* entry — because those either belong on the `.val.ts` (the caller's existing
|
|
2111
|
+
* path) or need both files written at once, which a single `TextEdit` cannot do.
|
|
2112
|
+
*/
|
|
2113
|
+
function entryOpsOf(patch, schema) {
|
|
2114
|
+
let entryKey;
|
|
2115
|
+
const ops = [];
|
|
2116
|
+
for (const op of patch) {
|
|
2117
|
+
const cls = classifyJsonValuesOp(schema, op.path);
|
|
2118
|
+
if (cls.kind !== "entry") {
|
|
2119
|
+
return undefined;
|
|
2120
|
+
}
|
|
2121
|
+
// Nested `.jsonValues()` is rejected as a module error long before a fix is
|
|
2122
|
+
// offered; treat it as out of scope rather than writing to a guessed file.
|
|
2123
|
+
if (cls.recordPath.length > 0 || cls.subPath.length === 0) {
|
|
2124
|
+
return undefined;
|
|
2125
|
+
}
|
|
2126
|
+
// `move` and `copy` carry a second path, and `rebaseContentOp` slices `from`
|
|
2127
|
+
// by the same prefix as `path` -- so an op reaching outside this entry would
|
|
2128
|
+
// have its source silently reinterpreted as a path inside this entry's file.
|
|
2129
|
+
if (op.op === "move" || op.op === "copy") {
|
|
2130
|
+
const fromCls = classifyJsonValuesOp(schema, op.from);
|
|
2131
|
+
if (fromCls.kind !== "entry" || fromCls.entryKey !== cls.entryKey) {
|
|
2132
|
+
return undefined;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
if (entryKey !== undefined && entryKey !== cls.entryKey) {
|
|
2136
|
+
return undefined;
|
|
2137
|
+
}
|
|
2138
|
+
entryKey = cls.entryKey;
|
|
2139
|
+
ops.push(op);
|
|
2140
|
+
}
|
|
2141
|
+
if (entryKey === undefined || ops.length === 0) {
|
|
2142
|
+
return undefined;
|
|
2143
|
+
}
|
|
2144
|
+
return {
|
|
2145
|
+
entryKey,
|
|
2146
|
+
ops
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
function readFile(filePath) {
|
|
2150
|
+
try {
|
|
2151
|
+
return fs.readFileSync(filePath, "utf8");
|
|
2152
|
+
} catch {
|
|
2153
|
+
return undefined;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2019
2157
|
/**
|
|
2020
2158
|
* `workspace/executeCommand` handlers.
|
|
2021
2159
|
*
|
|
@@ -2290,9 +2428,11 @@ function createValCommands(deps) {
|
|
|
2290
2428
|
// them in the same edit, so one undo takes back all of it.
|
|
2291
2429
|
const unwritten = [];
|
|
2292
2430
|
for (const other of outcome.otherModulePatches ?? []) {
|
|
2293
|
-
const
|
|
2294
|
-
if (
|
|
2295
|
-
(
|
|
2431
|
+
const otherEdits = await editsForModulePatch(project, other);
|
|
2432
|
+
if (otherEdits) {
|
|
2433
|
+
for (const otherEdit of otherEdits) {
|
|
2434
|
+
(changes[otherEdit.uri] ??= []).push(otherEdit.edit);
|
|
2435
|
+
}
|
|
2296
2436
|
} else {
|
|
2297
2437
|
unwritten.push(other.moduleFilePath);
|
|
2298
2438
|
}
|
|
@@ -2319,31 +2459,84 @@ function createValCommands(deps) {
|
|
|
2319
2459
|
}
|
|
2320
2460
|
|
|
2321
2461
|
/**
|
|
2322
|
-
* The
|
|
2323
|
-
* against the editor's
|
|
2462
|
+
* The edits for a patch to another module than the one the command ran on,
|
|
2463
|
+
* against the editor's buffers when the files are open and the disk
|
|
2464
|
+
* otherwise. `undefined` when any part of it cannot be written: half a rename
|
|
2465
|
+
* is worse than a warning naming the module.
|
|
2466
|
+
*
|
|
2467
|
+
* A `.jsonValues()` entry's value is in its own `*.val.json`, not in the
|
|
2468
|
+
* `.val.ts` — so the ops are split by the entry they land in, and each
|
|
2469
|
+
* entry's go through `jsonEntryEditFor`, the same routing a quick fix inside
|
|
2470
|
+
* an entry uses. Only what is left is applied to the `.val.ts`.
|
|
2324
2471
|
*/
|
|
2325
|
-
function
|
|
2326
|
-
const
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2472
|
+
async function editsForModulePatch(project, other) {
|
|
2473
|
+
const {
|
|
2474
|
+
valRoot
|
|
2475
|
+
} = project;
|
|
2476
|
+
const moduleFile = path.join(valRoot, other.moduleFilePath);
|
|
2477
|
+
const uri = pathToUri(moduleFile);
|
|
2478
|
+
const valTsText = readBuffer(moduleFile);
|
|
2479
|
+
if (valTsText === undefined) {
|
|
2480
|
+
return undefined;
|
|
2481
|
+
}
|
|
2482
|
+
const moduleResult = await project.getModule(other.moduleFilePath, {
|
|
2483
|
+
validate: false
|
|
2484
|
+
});
|
|
2485
|
+
const schema = moduleResult.status === "ok" ? moduleResult.content.schema : undefined;
|
|
2486
|
+
const valTsOps = [];
|
|
2487
|
+
const entryOps = new Map();
|
|
2488
|
+
for (const op of other.patch) {
|
|
2489
|
+
const cls = schema && classifyJsonValuesOp(schema, op.path);
|
|
2490
|
+
if (cls && cls.kind === "entry") {
|
|
2491
|
+
const ops = entryOps.get(cls.entryKey) ?? [];
|
|
2492
|
+
ops.push(op);
|
|
2493
|
+
entryOps.set(cls.entryKey, ops);
|
|
2494
|
+
} else {
|
|
2495
|
+
valTsOps.push(op);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
const edits = [];
|
|
2499
|
+
for (const ops of entryOps.values()) {
|
|
2500
|
+
const entryEdit = jsonEntryEditFor({
|
|
2501
|
+
patch: ops,
|
|
2502
|
+
schema,
|
|
2503
|
+
moduleFilePath: other.moduleFilePath,
|
|
2504
|
+
valTsText,
|
|
2505
|
+
valRoot,
|
|
2506
|
+
read: readBuffer
|
|
2507
|
+
});
|
|
2508
|
+
if (!entryEdit) {
|
|
2333
2509
|
return undefined;
|
|
2334
2510
|
}
|
|
2335
|
-
|
|
2511
|
+
edits.push(entryEdit);
|
|
2512
|
+
}
|
|
2513
|
+
if (valTsOps.length > 0) {
|
|
2514
|
+
const patched = patchSourceFile(valTsText, valTsOps);
|
|
2515
|
+
if (result.isErr(patched)) {
|
|
2516
|
+
return undefined;
|
|
2517
|
+
}
|
|
2518
|
+
const edit = minimalTextEdit(valTsText, patched.value.text, TextDocument.create(uri, "typescript", 0, valTsText));
|
|
2519
|
+
if (edit) {
|
|
2520
|
+
edits.push({
|
|
2521
|
+
uri,
|
|
2522
|
+
edit
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
return edits;
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
/** A file as the editor has it, falling back to disk. */
|
|
2530
|
+
function readBuffer(fsPath) {
|
|
2531
|
+
const open = deps.getDocument(pathToUri(fsPath));
|
|
2532
|
+
if (open) {
|
|
2533
|
+
return open.getText();
|
|
2336
2534
|
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2535
|
+
try {
|
|
2536
|
+
return fs.readFileSync(fsPath, "utf8");
|
|
2537
|
+
} catch {
|
|
2340
2538
|
return undefined;
|
|
2341
2539
|
}
|
|
2342
|
-
const otherEdit = minimalTextEdit(otherBefore, otherPatched.value.text, otherDocument);
|
|
2343
|
-
return otherEdit && {
|
|
2344
|
-
uri,
|
|
2345
|
-
edit: otherEdit
|
|
2346
|
-
};
|
|
2347
2540
|
}
|
|
2348
2541
|
return {
|
|
2349
2542
|
async execute(command, args) {
|
|
@@ -2526,144 +2719,6 @@ function valModulesEntryText({
|
|
|
2526
2719
|
return hasElements ? `,\n${indentation}${entry}` : `\n${indentation}${entry}\n${indentation.slice(2)}`;
|
|
2527
2720
|
}
|
|
2528
2721
|
|
|
2529
|
-
const jsonOps = new JSONOps();
|
|
2530
|
-
|
|
2531
|
-
/**
|
|
2532
|
-
* Turning a fix patch into an edit when the value it fixes is NOT in the
|
|
2533
|
-
* `.val.ts` the diagnostic was reported on.
|
|
2534
|
-
*
|
|
2535
|
-
* A `.jsonValues()` record keeps each entry's value in its own `*.val.json`; the
|
|
2536
|
-
* `.val.ts` holds `c.json(() => import("./x.val.json"))` and nothing below it.
|
|
2537
|
-
* So a fix for, say, an `s.image()` inside an entry produces a patch whose path
|
|
2538
|
-
* starts at the module root (`["/jobb/student", "pageImage", "width"]`) but
|
|
2539
|
-
* whose target lives in another file entirely.
|
|
2540
|
-
*
|
|
2541
|
-
* Applying such a patch to the `.val.ts` cannot work: the walk reaches the
|
|
2542
|
-
* `c.json(...)` call expression and `TSOps` refuses it ("Expression must be a
|
|
2543
|
-
* literal"). That refusal is why an editor silently offered no quick fix at all
|
|
2544
|
-
* for anything inside an entry — `computeFixEdit` saw an error result and
|
|
2545
|
-
* dropped the action.
|
|
2546
|
-
*
|
|
2547
|
-
* The routing here is the same one the Studio's publish and `val validate --fix`
|
|
2548
|
-
* use — {@link classifyJsonValuesOp} to find the entry, {@link rebaseContentOp}
|
|
2549
|
-
* to drop the entry-key prefix — so all three write the same file the same way.
|
|
2550
|
-
* What is specific to the editor is only the last step: the result comes back as
|
|
2551
|
-
* a `TextEdit` against the entry's document rather than as a write to disk, so
|
|
2552
|
-
* the edit goes through the editor's undo stack and respects an unsaved buffer.
|
|
2553
|
-
*/
|
|
2554
|
-
|
|
2555
|
-
function jsonEntryEditFor({
|
|
2556
|
-
patch,
|
|
2557
|
-
schema,
|
|
2558
|
-
moduleFilePath,
|
|
2559
|
-
valTsText,
|
|
2560
|
-
valRoot,
|
|
2561
|
-
read
|
|
2562
|
-
}) {
|
|
2563
|
-
if (!schema) {
|
|
2564
|
-
return undefined;
|
|
2565
|
-
}
|
|
2566
|
-
const entry = entryOpsOf(patch, schema);
|
|
2567
|
-
if (!entry) {
|
|
2568
|
-
return undefined;
|
|
2569
|
-
}
|
|
2570
|
-
const jsonFilePath = findJsonEntryFilePath(moduleFilePath, ts.createSourceFile(moduleFilePath, valTsText, ts.ScriptTarget.ES2020), entry.entryKey);
|
|
2571
|
-
if (!jsonFilePath) {
|
|
2572
|
-
return undefined;
|
|
2573
|
-
}
|
|
2574
|
-
const absolutePath = path.join(valRoot, jsonFilePath);
|
|
2575
|
-
const before = read?.(absolutePath) ?? readFile(absolutePath);
|
|
2576
|
-
if (before === undefined) {
|
|
2577
|
-
return undefined;
|
|
2578
|
-
}
|
|
2579
|
-
let content;
|
|
2580
|
-
try {
|
|
2581
|
-
content = JSON.parse(before);
|
|
2582
|
-
} catch {
|
|
2583
|
-
// A malformed entry file is already reported as a load error; offering an
|
|
2584
|
-
// edit computed from a guess at its contents would be worse than offering
|
|
2585
|
-
// nothing.
|
|
2586
|
-
return undefined;
|
|
2587
|
-
}
|
|
2588
|
-
for (const op of entry.ops) {
|
|
2589
|
-
// Root-only, like the rest of the `.jsonValues()` machinery, so the prefix
|
|
2590
|
-
// to drop is exactly the entry key.
|
|
2591
|
-
const rebased = rebaseContentOp(op, 1);
|
|
2592
|
-
if (result.isErr(rebased)) {
|
|
2593
|
-
return undefined;
|
|
2594
|
-
}
|
|
2595
|
-
const applied = applyPatch(deepClone(content), jsonOps, [rebased.value]);
|
|
2596
|
-
if (result.isErr(applied)) {
|
|
2597
|
-
return undefined;
|
|
2598
|
-
}
|
|
2599
|
-
content = applied.value;
|
|
2600
|
-
}
|
|
2601
|
-
// Re-serialising the whole value and diffing it is what keeps this honest:
|
|
2602
|
-
// the alternative is editing JSON text by hand, and the fix would then differ
|
|
2603
|
-
// from what the CLI writes. `minimalTextEdit` narrows the result to the region
|
|
2604
|
-
// that actually changed, so a two-space-indented file (what prettier and the
|
|
2605
|
-
// CLI both produce) yields an edit covering the changed properties and no
|
|
2606
|
-
// more. A file indented differently gets a correct but larger edit.
|
|
2607
|
-
const after = JSON.stringify(content, null, 2) + (before.endsWith("\n") ? "\n" : "");
|
|
2608
|
-
const uri = pathToUri(absolutePath);
|
|
2609
|
-
const edit = minimalTextEdit(before, after, TextDocument.create(uri, "json", 0, before));
|
|
2610
|
-
return edit && {
|
|
2611
|
-
uri,
|
|
2612
|
-
edit
|
|
2613
|
-
};
|
|
2614
|
-
}
|
|
2615
|
-
|
|
2616
|
-
/**
|
|
2617
|
-
* The ops of `patch`, if every one of them edits the content of ONE
|
|
2618
|
-
* `.jsonValues()` entry. `undefined` for anything else — an ordinary module, a
|
|
2619
|
-
* patch that also touches the `.val.ts`, or one that adds or removes a whole
|
|
2620
|
-
* entry — because those either belong on the `.val.ts` (the caller's existing
|
|
2621
|
-
* path) or need both files written at once, which a single `TextEdit` cannot do.
|
|
2622
|
-
*/
|
|
2623
|
-
function entryOpsOf(patch, schema) {
|
|
2624
|
-
let entryKey;
|
|
2625
|
-
const ops = [];
|
|
2626
|
-
for (const op of patch) {
|
|
2627
|
-
const cls = classifyJsonValuesOp(schema, op.path);
|
|
2628
|
-
if (cls.kind !== "entry") {
|
|
2629
|
-
return undefined;
|
|
2630
|
-
}
|
|
2631
|
-
// Nested `.jsonValues()` is rejected as a module error long before a fix is
|
|
2632
|
-
// offered; treat it as out of scope rather than writing to a guessed file.
|
|
2633
|
-
if (cls.recordPath.length > 0 || cls.subPath.length === 0) {
|
|
2634
|
-
return undefined;
|
|
2635
|
-
}
|
|
2636
|
-
// `move` and `copy` carry a second path, and `rebaseContentOp` slices `from`
|
|
2637
|
-
// by the same prefix as `path` -- so an op reaching outside this entry would
|
|
2638
|
-
// have its source silently reinterpreted as a path inside this entry's file.
|
|
2639
|
-
if (op.op === "move" || op.op === "copy") {
|
|
2640
|
-
const fromCls = classifyJsonValuesOp(schema, op.from);
|
|
2641
|
-
if (fromCls.kind !== "entry" || fromCls.entryKey !== cls.entryKey) {
|
|
2642
|
-
return undefined;
|
|
2643
|
-
}
|
|
2644
|
-
}
|
|
2645
|
-
if (entryKey !== undefined && entryKey !== cls.entryKey) {
|
|
2646
|
-
return undefined;
|
|
2647
|
-
}
|
|
2648
|
-
entryKey = cls.entryKey;
|
|
2649
|
-
ops.push(op);
|
|
2650
|
-
}
|
|
2651
|
-
if (entryKey === undefined || ops.length === 0) {
|
|
2652
|
-
return undefined;
|
|
2653
|
-
}
|
|
2654
|
-
return {
|
|
2655
|
-
entryKey,
|
|
2656
|
-
ops
|
|
2657
|
-
};
|
|
2658
|
-
}
|
|
2659
|
-
function readFile(filePath) {
|
|
2660
|
-
try {
|
|
2661
|
-
return fs.readFileSync(filePath, "utf8");
|
|
2662
|
-
} catch {
|
|
2663
|
-
return undefined;
|
|
2664
|
-
}
|
|
2665
|
-
}
|
|
2666
|
-
|
|
2667
2722
|
/**
|
|
2668
2723
|
* What the client announced it will do with a `WorkspaceEdit`.
|
|
2669
2724
|
*
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"lsp",
|
|
12
12
|
"language-server"
|
|
13
13
|
],
|
|
14
|
-
"version": "0.
|
|
14
|
+
"version": "0.144.1",
|
|
15
15
|
"bin": {
|
|
16
16
|
"val-language-server": "./bin.js"
|
|
17
17
|
},
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"typescript": "^6.0.3",
|
|
30
30
|
"vscode-languageserver": "^10.1.0",
|
|
31
31
|
"vscode-languageserver-textdocument": "^1.0.14",
|
|
32
|
-
"@valbuild/core": "0.
|
|
33
|
-
"@valbuild/
|
|
34
|
-
"@valbuild/
|
|
32
|
+
"@valbuild/core": "0.144.0",
|
|
33
|
+
"@valbuild/server": "0.144.1",
|
|
34
|
+
"@valbuild/shared": "0.144.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/jest": "^30.0.0",
|