doc-detective 4.29.1 → 4.30.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/bin/doc-detective-lsp.js +6 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/common/src/validate.d.ts +16 -1
- package/dist/common/src/validate.d.ts.map +1 -1
- package/dist/common/src/validate.js +18 -1
- package/dist/common/src/validate.js.map +1 -1
- package/dist/core/tests/saveScreenshot.d.ts.map +1 -1
- package/dist/core/tests/saveScreenshot.js +101 -59
- package/dist/core/tests/saveScreenshot.js.map +1 -1
- package/dist/index.cjs +55 -36
- package/dist/lsp/command.d.ts +12 -0
- package/dist/lsp/command.d.ts.map +1 -0
- package/dist/lsp/command.js +24 -0
- package/dist/lsp/command.js.map +1 -0
- package/dist/lsp/completion.d.ts +27 -0
- package/dist/lsp/completion.d.ts.map +1 -0
- package/dist/lsp/completion.js +114 -0
- package/dist/lsp/completion.js.map +1 -0
- package/dist/lsp/diagnostics.d.ts +21 -0
- package/dist/lsp/diagnostics.d.ts.map +1 -0
- package/dist/lsp/diagnostics.js +137 -0
- package/dist/lsp/diagnostics.js.map +1 -0
- package/dist/lsp/gate.d.ts +30 -0
- package/dist/lsp/gate.d.ts.map +1 -0
- package/dist/lsp/gate.js +80 -0
- package/dist/lsp/gate.js.map +1 -0
- package/dist/lsp/hover.d.ts +9 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/lsp/hover.js +56 -0
- package/dist/lsp/hover.js.map +1 -0
- package/dist/lsp/inline.d.ts +25 -0
- package/dist/lsp/inline.d.ts.map +1 -0
- package/dist/lsp/inline.js +208 -0
- package/dist/lsp/inline.js.map +1 -0
- package/dist/lsp/json/positions.d.ts +51 -0
- package/dist/lsp/json/positions.d.ts.map +1 -0
- package/dist/lsp/json/positions.js +101 -0
- package/dist/lsp/json/positions.js.map +1 -0
- package/dist/lsp/messages.d.ts +27 -0
- package/dist/lsp/messages.d.ts.map +1 -0
- package/dist/lsp/messages.js +33 -0
- package/dist/lsp/messages.js.map +1 -0
- package/dist/lsp/model.d.ts +27 -0
- package/dist/lsp/model.d.ts.map +1 -0
- package/dist/lsp/model.js +49 -0
- package/dist/lsp/model.js.map +1 -0
- package/dist/lsp/registry.d.ts +61 -0
- package/dist/lsp/registry.d.ts.map +1 -0
- package/dist/lsp/registry.js +124 -0
- package/dist/lsp/registry.js.map +1 -0
- package/dist/lsp/server.d.ts +40 -0
- package/dist/lsp/server.d.ts.map +1 -0
- package/dist/lsp/server.js +64 -0
- package/dist/lsp/server.js.map +1 -0
- package/dist/lsp/yaml/positions.d.ts +32 -0
- package/dist/lsp/yaml/positions.d.ts.map +1 -0
- package/dist/lsp/yaml/positions.js +92 -0
- package/dist/lsp/yaml/positions.js.map +1 -0
- package/package.json +6 -2
- package/scripts/postinstall.js +144 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { DiagnosticSeverity, } from "vscode-languageserver";
|
|
2
|
+
import { validate } from "../common/src/validate.js";
|
|
3
|
+
import { classifyDocument } from "./gate.js";
|
|
4
|
+
import { buildModel } from "./model.js";
|
|
5
|
+
import { fileTypeForUri, computeInlineDiagnostics } from "./inline.js";
|
|
6
|
+
import { DIAGNOSTIC_SOURCE, ACTION_KEYED_MESSAGE, V2_DEPRECATION_MESSAGE, schemaMessage, } from "./messages.js";
|
|
7
|
+
// Re-exported so existing importers (and tests) keep a single entry point.
|
|
8
|
+
export { DIAGNOSTIC_SOURCE, ACTION_KEYED_MESSAGE, V2_DEPRECATION_MESSAGE, schemaMessage, };
|
|
9
|
+
const SCHEMA_FOR_CLASS = {
|
|
10
|
+
spec: "spec_v3",
|
|
11
|
+
config: "config_v3",
|
|
12
|
+
};
|
|
13
|
+
function offsetRangeToLspRange(doc, range) {
|
|
14
|
+
return {
|
|
15
|
+
start: doc.positionAt(range.start),
|
|
16
|
+
end: doc.positionAt(range.end),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Is `instancePath` inside (or equal to) the step at `pointer`? Used to drop
|
|
21
|
+
* the raw `anyOf` schema errors for a step we've already explained with the
|
|
22
|
+
* friendly action-keyed diagnostic.
|
|
23
|
+
*/
|
|
24
|
+
function isUnderPointer(instancePath, pointer) {
|
|
25
|
+
return instancePath === pointer || instancePath.startsWith(pointer + "/");
|
|
26
|
+
}
|
|
27
|
+
/** Is `pointer` inside (or equal to) `instancePath`? (The inverse relation.) */
|
|
28
|
+
function pointerIsUnder(pointer, instancePath) {
|
|
29
|
+
return pointer === instancePath || pointer.startsWith(instancePath + "/");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Should this schema error be dropped because an action-keyed step already
|
|
33
|
+
* explains it? Two cases: (1) the error sits at/under the offending step, or
|
|
34
|
+
* (2) it's a vague container `anyOf`/`oneOf` failure on an *ancestor* of that
|
|
35
|
+
* step — the propagation of the same problem up to the test/spec level, which
|
|
36
|
+
* says only "must match a schema in anyOf" and is useless next to the precise
|
|
37
|
+
* action-keyed diagnostic.
|
|
38
|
+
*/
|
|
39
|
+
export function isSuppressedByActionKeyed(instancePath, keyword, suppressedPointers) {
|
|
40
|
+
return suppressedPointers.some((pointer) => {
|
|
41
|
+
if (isUnderPointer(instancePath, pointer))
|
|
42
|
+
return true;
|
|
43
|
+
if ((keyword === "anyOf" || keyword === "oneOf") &&
|
|
44
|
+
pointerIsUnder(pointer, instancePath)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Compute all diagnostics for a document. Pure over (uri, text): no filesystem,
|
|
52
|
+
* no network. Handles JSON and YAML specs/configs. Returns `[]` for anything the
|
|
53
|
+
* detection gate doesn't recognize and for empty/unparseable buffers (beyond
|
|
54
|
+
* the syntax errors themselves).
|
|
55
|
+
*/
|
|
56
|
+
export function computeDiagnostics(doc) {
|
|
57
|
+
const text = doc.getText();
|
|
58
|
+
// Markup files (markdown/asciidoc/html/dita) carry tests inline, not as a
|
|
59
|
+
// whole-file spec — route them to the inline pipeline. Naturally silent on
|
|
60
|
+
// files without any Doc Detective statements.
|
|
61
|
+
const markupFileType = fileTypeForUri(doc.uri);
|
|
62
|
+
if (markupFileType)
|
|
63
|
+
return computeInlineDiagnostics(doc, markupFileType);
|
|
64
|
+
const cls = classifyDocument({ uri: doc.uri, text });
|
|
65
|
+
if (!cls)
|
|
66
|
+
return [];
|
|
67
|
+
const model = buildModel(doc.uri, text);
|
|
68
|
+
/* c8 ignore next - classify only returns non-null for .json/.yaml/.yml, which buildModel handles */
|
|
69
|
+
if (!model)
|
|
70
|
+
return [];
|
|
71
|
+
// Surface syntax errors ourselves: because the plugin maps specs to a
|
|
72
|
+
// dedicated language id, the editor's built-in JSON/YAML service may not run.
|
|
73
|
+
const diagnostics = model.syntaxErrors.map((e) => ({
|
|
74
|
+
severity: DiagnosticSeverity.Error,
|
|
75
|
+
range: offsetRangeToLspRange(doc, e.range),
|
|
76
|
+
message: e.message,
|
|
77
|
+
source: DIAGNOSTIC_SOURCE,
|
|
78
|
+
}));
|
|
79
|
+
// A syntactically broken buffer can't be meaningfully schema-checked — the
|
|
80
|
+
// partial value produces misleading "must be object" noise. Show the syntax
|
|
81
|
+
// errors; schema diagnostics reappear once the document parses cleanly.
|
|
82
|
+
if (diagnostics.length > 0)
|
|
83
|
+
return diagnostics;
|
|
84
|
+
const value = model.value;
|
|
85
|
+
if (!value || typeof value !== "object")
|
|
86
|
+
return diagnostics;
|
|
87
|
+
const actionKeyed = model.actionKeyedSteps();
|
|
88
|
+
const result = validate({
|
|
89
|
+
schemaKey: SCHEMA_FOR_CLASS[cls],
|
|
90
|
+
object: value,
|
|
91
|
+
addDefaults: false,
|
|
92
|
+
structuredErrors: true,
|
|
93
|
+
});
|
|
94
|
+
// `structuredErrors: true` guarantees `errorObjects` is populated (empty when
|
|
95
|
+
// valid), so it is never undefined here.
|
|
96
|
+
const errorObjects = result.errorObjects;
|
|
97
|
+
// Decide each action-keyed step's fate *per step*, by whether AJV flagged an
|
|
98
|
+
// error at/under that specific step's pointer:
|
|
99
|
+
// - own errors → the step is genuinely invalid v3 → flagship error, and its
|
|
100
|
+
// raw anyOf noise is suppressed in favor of that one message.
|
|
101
|
+
// - no errors → a valid legacy v2 step (it transforms to v3) → a
|
|
102
|
+
// non-blocking deprecation nudge, and we do NOT suppress
|
|
103
|
+
// unrelated errors on its behalf.
|
|
104
|
+
// This keeps a valid v2 step from being mislabeled just because some *other*
|
|
105
|
+
// part of the document failed to validate.
|
|
106
|
+
const invalidPointers = actionKeyed
|
|
107
|
+
.map((step) => step.pointer)
|
|
108
|
+
.filter((pointer) => errorObjects.some((error) => isUnderPointer(error.instancePath, pointer)));
|
|
109
|
+
for (const error of errorObjects) {
|
|
110
|
+
const instancePath = error.instancePath;
|
|
111
|
+
if (isSuppressedByActionKeyed(instancePath, error.keyword, invalidPointers)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const offsetRange = model.rangeForPath(instancePath);
|
|
115
|
+
/* c8 ignore next - rangeForPath always resolves to at least the root for a parsed tree */
|
|
116
|
+
if (!offsetRange)
|
|
117
|
+
continue;
|
|
118
|
+
diagnostics.push({
|
|
119
|
+
severity: DiagnosticSeverity.Error,
|
|
120
|
+
range: offsetRangeToLspRange(doc, offsetRange),
|
|
121
|
+
message: schemaMessage(error),
|
|
122
|
+
source: DIAGNOSTIC_SOURCE,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
for (const step of actionKeyed) {
|
|
126
|
+
const invalid = invalidPointers.includes(step.pointer);
|
|
127
|
+
diagnostics.push({
|
|
128
|
+
severity: invalid ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
|
|
129
|
+
range: offsetRangeToLspRange(doc, step.keyRange),
|
|
130
|
+
message: invalid ? ACTION_KEYED_MESSAGE : V2_DEPRECATION_MESSAGE,
|
|
131
|
+
source: DIAGNOSTIC_SOURCE,
|
|
132
|
+
code: invalid ? "action-keyed-step" : "legacy-v2-step",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return diagnostics;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/lsp/diagnostics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What kind of Doc Detective document a buffer is, or `null` when the buffer is
|
|
3
|
+
* not a Doc Detective document at all. The server stays completely silent
|
|
4
|
+
* (no diagnostics, no completion) for `null` — the detection gate is biased
|
|
5
|
+
* toward silence, because false noise on an unrelated `package.json` is far
|
|
6
|
+
* worse than a real spec that a user has to opt into with `$schema`.
|
|
7
|
+
*/
|
|
8
|
+
export type DocClass = "spec" | "config" | null;
|
|
9
|
+
export interface ClassifyInput {
|
|
10
|
+
/** Document URI (e.g. `file:///abs/path/foo.spec.json`). */
|
|
11
|
+
uri: string;
|
|
12
|
+
/** Full document text. */
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
15
|
+
/** Extract a lowercase basename from a URI or path, sans query/fragment. */
|
|
16
|
+
export declare function basenameFromUri(uri: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Classify a document by filename first, then by an explicit `$schema` opt-in,
|
|
19
|
+
* then by a shape sniff — in that order of confidence. Pure: no filesystem, no
|
|
20
|
+
* network, tolerant of syntactically broken buffers (an editor sends those
|
|
21
|
+
* constantly), so it never throws.
|
|
22
|
+
*/
|
|
23
|
+
export declare function classifyDocument({ uri, text }: ClassifyInput): DocClass;
|
|
24
|
+
/** Convenience predicate: is this a document the server should touch at all? */
|
|
25
|
+
export declare function shouldHandleDocument(input: ClassifyInput): boolean;
|
|
26
|
+
/** Is this URI a JSON document (by extension)? */
|
|
27
|
+
export declare function isJsonUri(uri: string): boolean;
|
|
28
|
+
/** Is this URI a YAML document (by extension)? */
|
|
29
|
+
export declare function isYamlUri(uri: string): boolean;
|
|
30
|
+
//# sourceMappingURL=gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/lsp/gate.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKnD;AAKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,aAAa,GAAG,QAAQ,CA6CvE;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAElE;AAED,kDAAkD;AAClD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,kDAAkD;AAClD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAG9C"}
|
package/dist/lsp/gate.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { parse as parseJsonc } from "jsonc-parser";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
/** Extract a lowercase basename from a URI or path, sans query/fragment. */
|
|
4
|
+
export function basenameFromUri(uri) {
|
|
5
|
+
// Strip scheme/query/fragment, then take the last path segment.
|
|
6
|
+
const withoutQuery = uri.split(/[?#]/)[0];
|
|
7
|
+
const segments = withoutQuery.split(/[\\/]/);
|
|
8
|
+
return (segments[segments.length - 1] || "").toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
const SPEC_NAME = /\.spec\.(json|ya?ml)$/;
|
|
11
|
+
const CONFIG_NAME = /^\.doc-detective\.(json|ya?ml)$/;
|
|
12
|
+
/**
|
|
13
|
+
* Classify a document by filename first, then by an explicit `$schema` opt-in,
|
|
14
|
+
* then by a shape sniff — in that order of confidence. Pure: no filesystem, no
|
|
15
|
+
* network, tolerant of syntactically broken buffers (an editor sends those
|
|
16
|
+
* constantly), so it never throws.
|
|
17
|
+
*/
|
|
18
|
+
export function classifyDocument({ uri, text }) {
|
|
19
|
+
const name = basenameFromUri(uri);
|
|
20
|
+
// 1. Filename convention — highest confidence.
|
|
21
|
+
if (SPEC_NAME.test(name))
|
|
22
|
+
return "spec";
|
|
23
|
+
if (CONFIG_NAME.test(name))
|
|
24
|
+
return "config";
|
|
25
|
+
// For anything else we only engage on an explicit signal in the content.
|
|
26
|
+
// Parse leniently by extension: YAML files through the YAML parser, everything
|
|
27
|
+
// else through jsonc (tolerant of trailing commas / comments). Both are
|
|
28
|
+
// wrapped so a malformed buffer classifies as `null` rather than throwing.
|
|
29
|
+
const isYaml = name.endsWith(".yaml") || name.endsWith(".yml");
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
if (isYaml) {
|
|
33
|
+
parsed = YAML.parse(text);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// jsonc.parse returns a *partial* value for malformed input (`{"tests":[],`
|
|
37
|
+
// yields `{tests:[]}`), which would wrongly classify garbage as a spec.
|
|
38
|
+
// Collect its errors and refuse to classify when any remain.
|
|
39
|
+
const errors = [];
|
|
40
|
+
parsed = parseJsonc(text, errors, { allowTrailingComma: true });
|
|
41
|
+
if (errors.length > 0)
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
// 2. Explicit `$schema` opt-in — the documented escape hatch for
|
|
52
|
+
// non-conventionally-named files.
|
|
53
|
+
if (typeof parsed.$schema === "string") {
|
|
54
|
+
if (parsed.$schema.includes("spec_v3"))
|
|
55
|
+
return "spec";
|
|
56
|
+
if (parsed.$schema.includes("config_v3"))
|
|
57
|
+
return "config";
|
|
58
|
+
}
|
|
59
|
+
// 3. Shape sniff — only the spec shape is distinctive enough to trust
|
|
60
|
+
// without a name or `$schema`. A top-level `tests` array is the
|
|
61
|
+
// spec_v3 signature. Config is deliberately NOT sniffed: too many
|
|
62
|
+
// unrelated JSON files would match a permissive config shape.
|
|
63
|
+
if (Array.isArray(parsed.tests))
|
|
64
|
+
return "spec";
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/** Convenience predicate: is this a document the server should touch at all? */
|
|
68
|
+
export function shouldHandleDocument(input) {
|
|
69
|
+
return classifyDocument(input) !== null;
|
|
70
|
+
}
|
|
71
|
+
/** Is this URI a JSON document (by extension)? */
|
|
72
|
+
export function isJsonUri(uri) {
|
|
73
|
+
return basenameFromUri(uri).endsWith(".json");
|
|
74
|
+
}
|
|
75
|
+
/** Is this URI a YAML document (by extension)? */
|
|
76
|
+
export function isYamlUri(uri) {
|
|
77
|
+
const name = basenameFromUri(uri);
|
|
78
|
+
return name.endsWith(".yaml") || name.endsWith(".yml");
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/lsp/gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Hover, type Position } from "vscode-languageserver";
|
|
2
|
+
import type { TextDocument } from "vscode-languageserver-textdocument";
|
|
3
|
+
/**
|
|
4
|
+
* Hover over an action key (a step's `"goTo"`, `"find"`, …) surfaces that
|
|
5
|
+
* action's description straight from the schema — one source of truth, no
|
|
6
|
+
* hand-written copies. Spec + JSON only.
|
|
7
|
+
*/
|
|
8
|
+
export declare function computeHover(doc: TextDocument, position: Position): Hover | null;
|
|
9
|
+
//# sourceMappingURL=hover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../src/lsp/hover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AA6BvE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI,CAyBhF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { MarkupKind } from "vscode-languageserver";
|
|
2
|
+
import { findNodeAtOffset, parseTree } from "jsonc-parser";
|
|
3
|
+
import { classifyDocument, isJsonUri } from "./gate.js";
|
|
4
|
+
import { getRegistry } from "./registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Is `keyNode` the key of a property that is a direct child of a `steps`
|
|
7
|
+
* array element? Walks: string(key) → property → object(step) → array →
|
|
8
|
+
* property("steps"). Returns true only for an action key on a step.
|
|
9
|
+
*/
|
|
10
|
+
function isStepPropertyKey(keyNode) {
|
|
11
|
+
const property = keyNode.parent;
|
|
12
|
+
if (!property ||
|
|
13
|
+
property.type !== "property" ||
|
|
14
|
+
property.children?.[0] !== keyNode) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const stepObject = property.parent;
|
|
18
|
+
const array = stepObject?.parent;
|
|
19
|
+
const arrayProperty = array?.parent;
|
|
20
|
+
return (array?.type === "array" &&
|
|
21
|
+
arrayProperty?.type === "property" &&
|
|
22
|
+
arrayProperty.children?.[0]?.value === "steps");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Hover over an action key (a step's `"goTo"`, `"find"`, …) surfaces that
|
|
26
|
+
* action's description straight from the schema — one source of truth, no
|
|
27
|
+
* hand-written copies. Spec + JSON only.
|
|
28
|
+
*/
|
|
29
|
+
export function computeHover(doc, position) {
|
|
30
|
+
const text = doc.getText();
|
|
31
|
+
if (classifyDocument({ uri: doc.uri, text }) !== "spec")
|
|
32
|
+
return null;
|
|
33
|
+
if (!isJsonUri(doc.uri))
|
|
34
|
+
return null;
|
|
35
|
+
const root = parseTree(text);
|
|
36
|
+
if (!root)
|
|
37
|
+
return null;
|
|
38
|
+
const offset = doc.offsetAt(position);
|
|
39
|
+
const node = findNodeAtOffset(root, offset);
|
|
40
|
+
if (!node || node.type !== "string" || !isStepPropertyKey(node))
|
|
41
|
+
return null;
|
|
42
|
+
const action = getRegistry().byKey.get(node.value);
|
|
43
|
+
if (!action)
|
|
44
|
+
return null;
|
|
45
|
+
const parts = [`**${action.title}**`];
|
|
46
|
+
if (action.description)
|
|
47
|
+
parts.push("", action.description);
|
|
48
|
+
return {
|
|
49
|
+
contents: { kind: MarkupKind.Markdown, value: parts.join("\n") },
|
|
50
|
+
range: {
|
|
51
|
+
start: doc.positionAt(node.offset),
|
|
52
|
+
end: doc.positionAt(node.offset + node.length),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=hover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hover.js","sourceRoot":"","sources":["../../src/lsp/hover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA6B,MAAM,uBAAuB,CAAC;AAE9E,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAa,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,OAAa;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAChC,IACE,CAAC,QAAQ;QACT,QAAQ,CAAC,IAAI,KAAK,UAAU;QAC5B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,EAClC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;IACnC,MAAM,KAAK,GAAG,UAAU,EAAE,MAAM,CAAC;IACjC,MAAM,aAAa,GAAG,KAAK,EAAE,MAAM,CAAC;IACpC,OAAO,CACL,KAAK,EAAE,IAAI,KAAK,OAAO;QACvB,aAAa,EAAE,IAAI,KAAK,UAAU;QAClC,aAAa,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,OAAO,CAC/C,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAAiB,EAAE,QAAkB;IAChE,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;IAC3B,IAAI,gBAAgB,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACrE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAE7E,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;IAC7D,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,MAAM,KAAK,GAAG,CAAC,KAAK,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAE3D,OAAO;QACL,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAChE,KAAK,EAAE;YACL,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;YAClC,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;SAC/C;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Diagnostic } from "vscode-languageserver";
|
|
2
|
+
import type { TextDocument } from "vscode-languageserver-textdocument";
|
|
3
|
+
import { type FileType } from "../common/src/fileTypes.js";
|
|
4
|
+
import { type OffsetRange } from "./json/positions.js";
|
|
5
|
+
/** Find the fileType whose extensions match this URI, or null. */
|
|
6
|
+
export declare function fileTypeForUri(uri: string): FileType | null;
|
|
7
|
+
/** A raw inline statement: its parsed payload and the payload's source span. */
|
|
8
|
+
interface InlineStatement {
|
|
9
|
+
payload: any;
|
|
10
|
+
/** Whether parseObject could turn the captured text into an object. */
|
|
11
|
+
parsed: boolean;
|
|
12
|
+
range: OffsetRange;
|
|
13
|
+
}
|
|
14
|
+
export declare function collectMatchOffsets(text: string, patterns?: string[]): number[];
|
|
15
|
+
/** Extract statements of one kind (testStart / step) with payloads + spans. */
|
|
16
|
+
export declare function extractStatements(text: string, patterns: string[] | undefined, ignore: OffsetRange[]): InlineStatement[];
|
|
17
|
+
/**
|
|
18
|
+
* Compute diagnostics for inline Doc Detective test statements embedded in a
|
|
19
|
+
* markup document (markdown/asciidoc/html/dita, or a config fileType). Only the
|
|
20
|
+
* `test` open and `step` statement regions the runner recognizes get language
|
|
21
|
+
* features — the rest of the prose is left alone.
|
|
22
|
+
*/
|
|
23
|
+
export declare function computeInlineDiagnostics(doc: TextDocument, fileType: FileType): Diagnostic[];
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=inline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../src/lsp/inline.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAGX,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAGvE,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAG7E,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAoBxE,kEAAkE;AAClE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAS3D;AAED,gFAAgF;AAChF,UAAU,eAAe;IACvB,OAAO,EAAE,GAAG,CAAC;IACb,uEAAuE;IACvE,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,WAAW,CAAC;CACpB;AA2BD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAe/E;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,EAC9B,MAAM,EAAE,WAAW,EAAE,GACpB,eAAe,EAAE,CAwBnB;AAkGD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,YAAY,EACjB,QAAQ,EAAE,QAAQ,GACjB,UAAU,EAAE,CAad"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { DiagnosticSeverity, } from "vscode-languageserver";
|
|
2
|
+
import { validate } from "../common/src/validate.js";
|
|
3
|
+
import { parseObject } from "../common/src/detectTests.js";
|
|
4
|
+
import { defaultFileTypes } from "../common/src/fileTypes.js";
|
|
5
|
+
import { basenameFromUri } from "./gate.js";
|
|
6
|
+
import { getRegistry } from "./registry.js";
|
|
7
|
+
import { pointerFromPath } from "./json/positions.js";
|
|
8
|
+
import { DIAGNOSTIC_SOURCE, ACTION_KEYED_MESSAGE, V2_DEPRECATION_MESSAGE, schemaMessage, } from "./messages.js";
|
|
9
|
+
/**
|
|
10
|
+
* The fileTypes whose inline test statements we understand, keyed for lookup by
|
|
11
|
+
* extension. Derived from the runner's own `defaultFileTypes` so the detection
|
|
12
|
+
* patterns can never drift from what the runner parses.
|
|
13
|
+
*/
|
|
14
|
+
const MARKUP_FILE_TYPES = [
|
|
15
|
+
defaultFileTypes.markdown,
|
|
16
|
+
defaultFileTypes.asciidoc,
|
|
17
|
+
defaultFileTypes.html,
|
|
18
|
+
defaultFileTypes.dita,
|
|
19
|
+
].filter(Boolean);
|
|
20
|
+
/** Find the fileType whose extensions match this URI, or null. */
|
|
21
|
+
export function fileTypeForUri(uri) {
|
|
22
|
+
const name = basenameFromUri(uri);
|
|
23
|
+
for (const fileType of MARKUP_FILE_TYPES) {
|
|
24
|
+
/* c8 ignore next - every default fileType has extensions; `|| []` guards custom ones */
|
|
25
|
+
for (const ext of fileType.extensions || []) {
|
|
26
|
+
if (name.endsWith(`.${ext.toLowerCase()}`))
|
|
27
|
+
return fileType;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
/** Compile a pattern with the global + hasIndices flags, or null if invalid. */
|
|
33
|
+
/* c8 ignore start - the runner's own patterns always compile; the catch guards malformed custom fileType patterns */
|
|
34
|
+
function compilePattern(pattern) {
|
|
35
|
+
try {
|
|
36
|
+
return new RegExp(pattern, "gd");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/* c8 ignore stop */
|
|
43
|
+
/** Offset spans of `ignoreStart`…`ignoreEnd` regions, to skip statements in. */
|
|
44
|
+
function ignoreRanges(text, fileType) {
|
|
45
|
+
const starts = collectMatchOffsets(text, fileType.inlineStatements?.ignoreStart);
|
|
46
|
+
const ends = collectMatchOffsets(text, fileType.inlineStatements?.ignoreEnd);
|
|
47
|
+
const ranges = [];
|
|
48
|
+
for (const start of starts) {
|
|
49
|
+
// Pair each ignoreStart with the next ignoreEnd after it; if none, ignore to
|
|
50
|
+
// end of document.
|
|
51
|
+
const end = ends.find((e) => e >= start);
|
|
52
|
+
ranges.push({ start, end: end ?? text.length });
|
|
53
|
+
}
|
|
54
|
+
return ranges;
|
|
55
|
+
}
|
|
56
|
+
export function collectMatchOffsets(text, patterns) {
|
|
57
|
+
const offsets = [];
|
|
58
|
+
for (const pattern of patterns || []) {
|
|
59
|
+
const re = compilePattern(pattern);
|
|
60
|
+
/* c8 ignore next - compilePattern only returns null for an invalid custom pattern */
|
|
61
|
+
if (!re)
|
|
62
|
+
continue;
|
|
63
|
+
let m;
|
|
64
|
+
while ((m = re.exec(text))) {
|
|
65
|
+
offsets.push(m.index);
|
|
66
|
+
// A zero-width match doesn't advance lastIndex; nudge it so a degenerate
|
|
67
|
+
// (empty-matching) custom pattern can't spin forever and hang the server.
|
|
68
|
+
if (m[0].length === 0)
|
|
69
|
+
re.lastIndex++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return offsets.sort((a, b) => a - b);
|
|
73
|
+
}
|
|
74
|
+
/** Extract statements of one kind (testStart / step) with payloads + spans. */
|
|
75
|
+
export function extractStatements(text, patterns, ignore) {
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const pattern of patterns || []) {
|
|
78
|
+
const re = compilePattern(pattern);
|
|
79
|
+
/* c8 ignore next - compilePattern only returns null for an invalid custom pattern */
|
|
80
|
+
if (!re)
|
|
81
|
+
continue;
|
|
82
|
+
let m;
|
|
83
|
+
while ((m = re.exec(text))) {
|
|
84
|
+
// A zero-width match doesn't advance lastIndex; nudge it so a degenerate
|
|
85
|
+
// (empty-matching) custom pattern can't spin forever and hang the server.
|
|
86
|
+
if (m[0].length === 0)
|
|
87
|
+
re.lastIndex++;
|
|
88
|
+
if (ignore.some((r) => m.index >= r.start && m.index < r.end))
|
|
89
|
+
continue;
|
|
90
|
+
const group = m.indices?.[1];
|
|
91
|
+
const range = group
|
|
92
|
+
? { start: group[0], end: group[1] }
|
|
93
|
+
: /* c8 ignore next - hasIndices is always present under the 'd' flag */
|
|
94
|
+
{ start: m.index, end: m.index + m[0].length };
|
|
95
|
+
/* c8 ignore next - capture group 1 is always present in the statement patterns */
|
|
96
|
+
const raw = m[1] ?? "";
|
|
97
|
+
const payload = parseObject({ stringifiedObject: raw });
|
|
98
|
+
out.push({ payload, parsed: payload !== null && typeof payload === "object", range });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/** Drop the "must have steps or contexts" errors so an open statement (which
|
|
104
|
+
* legitimately carries neither — later statements supply the steps) isn't
|
|
105
|
+
* falsely flagged. Everything else in the open statement is still validated. */
|
|
106
|
+
function isStepsOrContextsRequirement(error) {
|
|
107
|
+
if (error.instancePath)
|
|
108
|
+
return false; // only the top-level requirement
|
|
109
|
+
if (error.keyword === "anyOf")
|
|
110
|
+
return true;
|
|
111
|
+
if (error.keyword === "required" &&
|
|
112
|
+
["steps", "contexts"].includes(error.params?.missingProperty)) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
function toLspRange(doc, range) {
|
|
118
|
+
return { start: doc.positionAt(range.start), end: doc.positionAt(range.end) };
|
|
119
|
+
}
|
|
120
|
+
/** Validate a `step` fragment and produce its diagnostics. */
|
|
121
|
+
function diagnoseStep(doc, stmt) {
|
|
122
|
+
if (!stmt.parsed)
|
|
123
|
+
return [];
|
|
124
|
+
const payload = stmt.payload;
|
|
125
|
+
const range = toLspRange(doc, stmt.range);
|
|
126
|
+
const isActionKeyed = "action" in payload;
|
|
127
|
+
const result = validate({
|
|
128
|
+
schemaKey: "step_v3",
|
|
129
|
+
object: payload,
|
|
130
|
+
addDefaults: false,
|
|
131
|
+
structuredErrors: true,
|
|
132
|
+
});
|
|
133
|
+
if (result.valid) {
|
|
134
|
+
// A valid v2 (action-keyed) step: deprecation warning, mirroring specs.
|
|
135
|
+
if (isActionKeyed)
|
|
136
|
+
return [warn(range, V2_DEPRECATION_MESSAGE, "legacy-v2-step")];
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
if (isActionKeyed) {
|
|
140
|
+
return [error(range, ACTION_KEYED_MESSAGE, "action-keyed-step")];
|
|
141
|
+
}
|
|
142
|
+
// A single invalid step matches no `anyOf` branch, so AJV reports a failure
|
|
143
|
+
// for EVERY action — dozens of "must have required property …". Collapse that
|
|
144
|
+
// wall: if the author clearly intended one action (a top-level key that is a
|
|
145
|
+
// known action), show only that action's own value errors; otherwise a single
|
|
146
|
+
// "not a recognized step" message.
|
|
147
|
+
const registry = getRegistry();
|
|
148
|
+
const actionKey = Object.keys(payload).find((k) => registry.byKey.has(k));
|
|
149
|
+
if (!actionKey) {
|
|
150
|
+
return [
|
|
151
|
+
error(range, 'Not a recognized Doc Detective step (expected `{"<action>": …}`).'),
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
const prefix = pointerFromPath([actionKey]);
|
|
155
|
+
/* c8 ignore next - errorObjects is always set here (structuredErrors: true) */
|
|
156
|
+
const relevant = (result.errorObjects || []).filter((e) => (e.instancePath || "").startsWith(prefix));
|
|
157
|
+
// One concise diagnostic per step — the action's value can fail several
|
|
158
|
+
// internal branches (e.g. goTo accepts a URL string OR an object), and
|
|
159
|
+
// surfacing all of them is noise. Show the first, most specific message.
|
|
160
|
+
if (relevant.length === 0) {
|
|
161
|
+
return [error(range, `Invalid "${actionKey}" step.`)];
|
|
162
|
+
}
|
|
163
|
+
return [error(range, `${actionKey}: ${schemaMessage(relevant[0])}`)];
|
|
164
|
+
}
|
|
165
|
+
/** Validate a `test` open fragment (relaxed: steps/contexts not required). */
|
|
166
|
+
function diagnoseTestOpen(doc, stmt) {
|
|
167
|
+
if (!stmt.parsed)
|
|
168
|
+
return [];
|
|
169
|
+
const result = validate({
|
|
170
|
+
schemaKey: "test_v3",
|
|
171
|
+
object: stmt.payload,
|
|
172
|
+
addDefaults: false,
|
|
173
|
+
structuredErrors: true,
|
|
174
|
+
});
|
|
175
|
+
if (result.valid)
|
|
176
|
+
return [];
|
|
177
|
+
const range = toLspRange(doc, stmt.range);
|
|
178
|
+
/* c8 ignore next - errorObjects is always set here (structuredErrors: true) */
|
|
179
|
+
return (result.errorObjects || [])
|
|
180
|
+
.filter((e) => !isStepsOrContextsRequirement(e))
|
|
181
|
+
.map((e) => error(range, schemaMessage(e)));
|
|
182
|
+
}
|
|
183
|
+
function error(range, message, code) {
|
|
184
|
+
return { severity: DiagnosticSeverity.Error, range, message, source: DIAGNOSTIC_SOURCE, code };
|
|
185
|
+
}
|
|
186
|
+
function warn(range, message, code) {
|
|
187
|
+
return { severity: DiagnosticSeverity.Warning, range, message, source: DIAGNOSTIC_SOURCE, code };
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Compute diagnostics for inline Doc Detective test statements embedded in a
|
|
191
|
+
* markup document (markdown/asciidoc/html/dita, or a config fileType). Only the
|
|
192
|
+
* `test` open and `step` statement regions the runner recognizes get language
|
|
193
|
+
* features — the rest of the prose is left alone.
|
|
194
|
+
*/
|
|
195
|
+
export function computeInlineDiagnostics(doc, fileType) {
|
|
196
|
+
const text = doc.getText();
|
|
197
|
+
const ignore = ignoreRanges(text, fileType);
|
|
198
|
+
const statements = fileType.inlineStatements || {};
|
|
199
|
+
const diagnostics = [];
|
|
200
|
+
for (const stmt of extractStatements(text, statements.testStart, ignore)) {
|
|
201
|
+
diagnostics.push(...diagnoseTestOpen(doc, stmt));
|
|
202
|
+
}
|
|
203
|
+
for (const stmt of extractStatements(text, statements.step, ignore)) {
|
|
204
|
+
diagnostics.push(...diagnoseStep(doc, stmt));
|
|
205
|
+
}
|
|
206
|
+
return diagnostics;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=inline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline.js","sourceRoot":"","sources":["../../src/lsp/inline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Node, ParseError } from "jsonc-parser";
|
|
2
|
+
/** A character-offset span into the source text (end-exclusive). */
|
|
3
|
+
export interface OffsetRange {
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
}
|
|
7
|
+
/** Result of parsing: the CST root (if any) plus lenient syntax errors. */
|
|
8
|
+
export interface JsonParse {
|
|
9
|
+
root: Node | undefined;
|
|
10
|
+
errors: ParseError[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse JSON(-with-comments) into a position-preserving CST. jsonc-parser is
|
|
14
|
+
* tolerant of the half-typed, comma-trailing states an editor streams, and
|
|
15
|
+
* collects syntax errors rather than throwing.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseJsonTree(text: string): JsonParse;
|
|
18
|
+
/**
|
|
19
|
+
* Turn an AJV `instancePath` (`/tests/0/steps/1/goTo`) into path segments,
|
|
20
|
+
* decoding the JSON-Pointer escapes (`~1`→`/`, `~0`→`~`) and numeric indices.
|
|
21
|
+
*/
|
|
22
|
+
export declare function instancePathToSegments(instancePath: string): Array<string | number>;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the source span for an AJV `instancePath`. When the exact node is
|
|
25
|
+
* absent (e.g. a `required` error names an object whose child doesn't exist),
|
|
26
|
+
* walks up to the nearest present ancestor so the diagnostic still anchors
|
|
27
|
+
* somewhere sensible instead of vanishing. Returns `null` only when even the
|
|
28
|
+
* root can't be located (unparseable input).
|
|
29
|
+
*/
|
|
30
|
+
export declare function rangeForInstancePath(root: Node, instancePath: string): OffsetRange | null;
|
|
31
|
+
/** A step whose author used the `action`-as-value antipattern. */
|
|
32
|
+
export interface ActionKeyedStep {
|
|
33
|
+
/** Span of the offending `"action"` key, for the squiggle. */
|
|
34
|
+
keyRange: OffsetRange;
|
|
35
|
+
/** JSON-Pointer to the step object, used to suppress its `anyOf` noise. */
|
|
36
|
+
pointer: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build a JSON-Pointer from path segments, escaping `~`→`~0` and `/`→`~1` per
|
|
40
|
+
* RFC 6901. Shared by the JSON and YAML action-keyed detectors.
|
|
41
|
+
*/
|
|
42
|
+
export declare function pointerFromPath(segments: Array<string | number>): string;
|
|
43
|
+
/**
|
|
44
|
+
* Find every step written as an object carrying an `action` property inside a
|
|
45
|
+
* `steps` array — the classic Doc Detective mistake (action name as a *value*
|
|
46
|
+
* under `action`, instead of the compact action-as-key form). Returns the
|
|
47
|
+
* `"action"` key span (to squiggle) and the step's JSON-Pointer (to suppress
|
|
48
|
+
* the otherwise-overwhelming `anyOf` schema failures for that same step).
|
|
49
|
+
*/
|
|
50
|
+
export declare function findActionKeyedSteps(root: Node): ActionKeyedStep[];
|
|
51
|
+
//# sourceMappingURL=positions.d.ts.map
|