@markup-carve/carve-lsp 0.1.3 → 0.1.4
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/README.md +52 -13
- package/dist/analyze.d.ts +6 -0
- package/dist/analyze.js +39 -2
- package/dist/analyze.js.map +1 -1
- package/dist/analyze.test.js +7 -0
- package/dist/analyze.test.js.map +1 -1
- package/dist/codelens.js +12 -0
- package/dist/codelens.js.map +1 -1
- package/dist/codelens.test.js +5 -0
- package/dist/codelens.test.js.map +1 -1
- package/dist/colon-fence-kinds.test.d.ts +1 -0
- package/dist/colon-fence-kinds.test.js +128 -0
- package/dist/colon-fence-kinds.test.js.map +1 -0
- package/dist/colon-fences.d.ts +24 -0
- package/dist/colon-fences.js +174 -0
- package/dist/colon-fences.js.map +1 -0
- package/dist/colon-fences.test.d.ts +1 -0
- package/dist/colon-fences.test.js +23 -0
- package/dist/colon-fences.test.js.map +1 -0
- package/dist/completion.d.ts +5 -1
- package/dist/completion.js +40 -10
- package/dist/completion.js.map +1 -1
- package/dist/completion.test.js +7 -0
- package/dist/completion.test.js.map +1 -1
- package/dist/document-highlights.d.ts +2 -0
- package/dist/document-highlights.js +21 -0
- package/dist/document-highlights.js.map +1 -0
- package/dist/document-highlights.test.d.ts +1 -0
- package/dist/document-highlights.test.js +13 -0
- package/dist/document-highlights.test.js.map +1 -0
- package/dist/document-links.d.ts +3 -0
- package/dist/document-links.js +36 -0
- package/dist/document-links.js.map +1 -0
- package/dist/document-links.test.d.ts +1 -0
- package/dist/document-links.test.js +12 -0
- package/dist/document-links.test.js.map +1 -0
- package/dist/format-corpus.test.d.ts +1 -0
- package/dist/format-corpus.test.js +16 -0
- package/dist/format-corpus.test.js.map +1 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.js +38 -74
- package/dist/format.js.map +1 -1
- package/dist/format.test.js +27 -32
- package/dist/format.test.js.map +1 -1
- package/dist/hover.js +20 -0
- package/dist/hover.js.map +1 -1
- package/dist/include-completion.d.ts +3 -0
- package/dist/include-completion.js +68 -0
- package/dist/include-completion.js.map +1 -0
- package/dist/include-completion.test.d.ts +1 -0
- package/dist/include-completion.test.js +15 -0
- package/dist/include-completion.test.js.map +1 -0
- package/dist/inlay-hints.d.ts +3 -0
- package/dist/inlay-hints.js +43 -0
- package/dist/inlay-hints.js.map +1 -0
- package/dist/inlay-hints.test.d.ts +1 -0
- package/dist/inlay-hints.test.js +10 -0
- package/dist/inlay-hints.test.js.map +1 -0
- package/dist/lint-actions.d.ts +3 -0
- package/dist/lint-actions.js +90 -0
- package/dist/lint-actions.js.map +1 -0
- package/dist/lint-actions.test.d.ts +1 -0
- package/dist/lint-actions.test.js +31 -0
- package/dist/lint-actions.test.js.map +1 -0
- package/dist/no-git-dependencies.test.d.ts +1 -0
- package/dist/no-git-dependencies.test.js +243 -0
- package/dist/no-git-dependencies.test.js.map +1 -0
- package/dist/packaging.test.d.ts +1 -0
- package/dist/packaging.test.js +69 -0
- package/dist/packaging.test.js.map +1 -0
- package/dist/selection.d.ts +2 -0
- package/dist/selection.js +36 -0
- package/dist/selection.js.map +1 -0
- package/dist/selection.test.d.ts +1 -0
- package/dist/selection.test.js +10 -0
- package/dist/selection.test.js.map +1 -0
- package/dist/semantic.d.ts +5 -3
- package/dist/semantic.js +31 -4
- package/dist/semantic.js.map +1 -1
- package/dist/semantic.test.js +5 -0
- package/dist/semantic.test.js.map +1 -1
- package/dist/server-capabilities.test.d.ts +1 -0
- package/dist/server-capabilities.test.js +84 -0
- package/dist/server-capabilities.test.js.map +1 -0
- package/dist/server.js +236 -20
- package/dist/server.js.map +1 -1
- package/dist/settings.d.ts +11 -0
- package/dist/settings.js +50 -0
- package/dist/settings.js.map +1 -0
- package/dist/settings.test.d.ts +1 -0
- package/dist/settings.test.js +22 -0
- package/dist/settings.test.js.map +1 -0
- package/dist/spec-0-1-4-constructs.test.d.ts +1 -0
- package/dist/spec-0-1-4-constructs.test.js +101 -0
- package/dist/spec-0-1-4-constructs.test.js.map +1 -0
- package/dist/table-diagnostics.d.ts +3 -0
- package/dist/table-diagnostics.js +60 -0
- package/dist/table-diagnostics.js.map +1 -0
- package/dist/table-diagnostics.test.d.ts +1 -0
- package/dist/table-diagnostics.test.js +12 -0
- package/dist/table-diagnostics.test.js.map +1 -0
- package/dist/workspace-graph.d.ts +25 -0
- package/dist/workspace-graph.js +94 -0
- package/dist/workspace-graph.js.map +1 -0
- package/dist/workspace-graph.test.d.ts +1 -0
- package/dist/workspace-graph.test.js +31 -0
- package/dist/workspace-graph.test.js.map +1 -0
- package/dist/workspace-index.d.ts +40 -0
- package/dist/workspace-index.js +254 -0
- package/dist/workspace-index.js.map +1 -0
- package/dist/workspace-index.test.d.ts +1 -0
- package/dist/workspace-index.test.js +65 -0
- package/dist/workspace-index.test.js.map +1 -0
- package/dist/workspace-loader.d.ts +11 -0
- package/dist/workspace-loader.js +50 -0
- package/dist/workspace-loader.js.map +1 -0
- package/dist/workspace-loader.test.d.ts +1 -0
- package/dist/workspace-loader.test.js +19 -0
- package/dist/workspace-loader.test.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { DiagnosticSeverity, DocumentHighlightKind, InlayHintKind } from 'vscode-languageserver/node.js';
|
|
2
|
+
import { parse } from '@markup-carve/carve';
|
|
3
|
+
/**
|
|
4
|
+
* Node types the parser reads verbatim. A `:::` line inside one of them is text,
|
|
5
|
+
* not a fence, so the closer scan below has to step over their whole extent.
|
|
6
|
+
*/
|
|
7
|
+
const OPAQUE_TYPES = new Set(['code_block', 'raw_block', 'comment']);
|
|
8
|
+
/**
|
|
9
|
+
* Labels for the node types whose humanized `type` would read wrong. This map is
|
|
10
|
+
* NOT a gate: a colon-fence node type missing from it still gets a site, a pair
|
|
11
|
+
* and a label - it only loses the hyphen. That is the difference from the chain
|
|
12
|
+
* this replaced, where an unnamed type returned `null` and every consumer of
|
|
13
|
+
* `colonFenceStructure` silently skipped the container (#157).
|
|
14
|
+
*/
|
|
15
|
+
const FENCE_KIND_LABELS = { hard_break_block: 'hard-break block' };
|
|
16
|
+
export function colonFenceStructure(source) {
|
|
17
|
+
const lines = source.replace(/\r\n?/g, '\n').split('\n');
|
|
18
|
+
const unclosed = [];
|
|
19
|
+
const document = parse(source, { positions: true, onUnclosedContainer: (site) => unclosed.push(site) });
|
|
20
|
+
const key = (line, column, width) => `${line}:${column}:${width}`;
|
|
21
|
+
const unclosedKeys = new Set(unclosed.map((site) => key(site.line, site.column, site.fenceWidth)));
|
|
22
|
+
const openers = new Map();
|
|
23
|
+
const opaque = [];
|
|
24
|
+
const diagnostics = [];
|
|
25
|
+
const walk = (value, parent) => {
|
|
26
|
+
if (Array.isArray(value)) {
|
|
27
|
+
for (const child of value)
|
|
28
|
+
walk(child, parent);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!value || typeof value !== 'object')
|
|
32
|
+
return;
|
|
33
|
+
const node = value;
|
|
34
|
+
if (OPAQUE_TYPES.has(String(node.type ?? ''))) {
|
|
35
|
+
const span = lineSpan(node);
|
|
36
|
+
if (span)
|
|
37
|
+
opaque.push(span);
|
|
38
|
+
}
|
|
39
|
+
const site = astFenceSite(node, lines);
|
|
40
|
+
const nextParent = site ?? parent;
|
|
41
|
+
if (site) {
|
|
42
|
+
// A parent is walked before its children, so the innermost site on a line wins.
|
|
43
|
+
openers.set(site.range.start.line, site);
|
|
44
|
+
const siteKey = key(site.range.start.line + 1, site.range.start.character + 1, site.width);
|
|
45
|
+
const run = lineRun(lines[site.range.start.line] ?? '', site.range.start.character);
|
|
46
|
+
if (unclosedKeys.has(siteKey) && run?.bare && parent && site.width !== parent.width) {
|
|
47
|
+
diagnostics.push({ severity: DiagnosticSeverity.Warning, source: 'carve', code: 'colon-fence-length-mismatch', range: site.range,
|
|
48
|
+
message: `Bare ${site.width}-colon fence does not close the innermost ${parent.width}-colon ${parent.kind} opened at ${parent.range.start.line + 1}:${parent.range.start.character + 1}; it currently opens a nested container.`,
|
|
49
|
+
data: { authoredWidth: site.width, expectedWidth: parent.width, openerLine: parent.range.start.line + 1, openerColumn: parent.range.start.character + 1, outcome: 'nested container' } });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const [name, child] of Object.entries(node))
|
|
53
|
+
if (name !== 'pos' && name !== 'attrs')
|
|
54
|
+
walk(child, nextParent);
|
|
55
|
+
};
|
|
56
|
+
// The whole document, not just `children`: footnote bodies hang off
|
|
57
|
+
// `footnoteDefs`, and a colon fence inside one is a container like any other.
|
|
58
|
+
walk(document, null);
|
|
59
|
+
return { openers: [...openers.values()].sort((a, b) => a.range.start.line - b.range.start.line), pairs: pairFences(lines, openers, opaque), diagnostics };
|
|
60
|
+
}
|
|
61
|
+
/** Every colon-fence opener the server recognizes, closed or not. */
|
|
62
|
+
export function colonFenceOpeners(source) {
|
|
63
|
+
return colonFenceStructure(source).openers;
|
|
64
|
+
}
|
|
65
|
+
export function colonFenceHighlights(source, position) {
|
|
66
|
+
const pair = colonFenceStructure(source).pairs.find(({ opener, closer }) => contains(opener.range, position) || contains(closer.range, position));
|
|
67
|
+
return pair ? [
|
|
68
|
+
{ range: pair.opener.range, kind: DocumentHighlightKind.Write },
|
|
69
|
+
{ range: pair.closer.range, kind: DocumentHighlightKind.Read },
|
|
70
|
+
] : [];
|
|
71
|
+
}
|
|
72
|
+
export function colonFenceHover(source, position) {
|
|
73
|
+
const pair = colonFenceStructure(source).pairs.find(({ closer }) => contains(closer.range, position));
|
|
74
|
+
if (!pair)
|
|
75
|
+
return null;
|
|
76
|
+
return {
|
|
77
|
+
contents: { kind: 'markdown', value: `**${pair.opener.kind} closer**\n\nEnds the ${pair.opener.width}-colon container from ${pair.opener.range.start.line + 1}:${pair.opener.range.start.character + 1} through ${pair.closer.range.end.line + 1}:${pair.closer.range.end.character + 1}.` },
|
|
78
|
+
range: pair.closer.range,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function colonFenceInlayHints(source, range) {
|
|
82
|
+
return colonFenceStructure(source).pairs
|
|
83
|
+
.filter(({ closer }) => closer.range.start.line >= range.start.line && closer.range.start.line <= range.end.line)
|
|
84
|
+
.map(({ opener, closer }) => ({ position: closer.range.end, label: ` closes ${opener.kind} from line ${opener.range.start.line + 1}`, kind: InlayHintKind.Type, paddingLeft: true }));
|
|
85
|
+
}
|
|
86
|
+
export function linkedColonFenceRanges(source, position) {
|
|
87
|
+
const pair = colonFenceStructure(source).pairs.find(({ opener, closer }) => contains(opener.range, position) || contains(closer.range, position));
|
|
88
|
+
return pair ? { ranges: [pair.opener.range, pair.closer.range], wordPattern: ':{3,}' } : null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Pair each opener with its closer by walking the source once.
|
|
92
|
+
*
|
|
93
|
+
* The extents cannot do this. A `div`, an `admonition` and a `figure_group` span
|
|
94
|
+
* their closing fence; a `::: >` block quote spans only its content, and a nested
|
|
95
|
+
* container's closer can sit on the very line its extent ends. Deriving the
|
|
96
|
+
* closer from `pos.end` therefore reads the wrong line for some kinds and no line
|
|
97
|
+
* for others - which is the second half of #157, under the missing kind.
|
|
98
|
+
*
|
|
99
|
+
* A bare run closes the innermost open container only when the widths match
|
|
100
|
+
* EXACTLY (carve#455); at any other width it opens a nested container, and the
|
|
101
|
+
* parser has already told us so by giving that line a node of its own.
|
|
102
|
+
*/
|
|
103
|
+
function pairFences(lines, openers, opaque) {
|
|
104
|
+
const pairs = [];
|
|
105
|
+
const stack = [];
|
|
106
|
+
// Flattened once. Testing every span per line is quadratic in a document made
|
|
107
|
+
// mostly of code blocks, and this runs on the highlight and hover paths.
|
|
108
|
+
const verbatim = new Uint8Array(lines.length);
|
|
109
|
+
for (const [from, to] of opaque) {
|
|
110
|
+
for (let line = Math.max(0, from); line <= Math.min(lines.length - 1, to); line++)
|
|
111
|
+
verbatim[line] = 1;
|
|
112
|
+
}
|
|
113
|
+
for (let line = 0; line < lines.length; line++) {
|
|
114
|
+
if (verbatim[line])
|
|
115
|
+
continue;
|
|
116
|
+
const opener = openers.get(line);
|
|
117
|
+
if (opener) {
|
|
118
|
+
stack.push(opener);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const top = stack[stack.length - 1];
|
|
122
|
+
if (!top)
|
|
123
|
+
continue;
|
|
124
|
+
const column = top.range.start.character;
|
|
125
|
+
const text = lines[line] ?? '';
|
|
126
|
+
if (!/^[ \t]*$/.test(text.slice(0, column)))
|
|
127
|
+
continue;
|
|
128
|
+
const run = lineRun(text, column);
|
|
129
|
+
if (!run?.bare || run.width !== top.width)
|
|
130
|
+
continue;
|
|
131
|
+
pairs.push({ opener: top, closer: fenceSite(line, column, run.width, top.kind) });
|
|
132
|
+
stack.pop();
|
|
133
|
+
}
|
|
134
|
+
return pairs;
|
|
135
|
+
}
|
|
136
|
+
function fenceSite(line, character, width, kind) {
|
|
137
|
+
return { kind, width, range: { start: { line, character }, end: { line, character: character + width } } };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A colon fence is recognized by the shape the schema describes - a block
|
|
141
|
+
* container (it carries `children`) whose start position lands on a `:::` run -
|
|
142
|
+
* rather than by a list of node types. That is what makes a new kind work on
|
|
143
|
+
* arrival: the fenced block quote is a `block_quote` carrying `fenced: true`
|
|
144
|
+
* rather than a type of its own, and `figure_group` was never on the list at all.
|
|
145
|
+
* The `>`-marker spelling of a quote starts on `>`, so the same test excludes it
|
|
146
|
+
* without having to read `fenced`.
|
|
147
|
+
*/
|
|
148
|
+
function astFenceSite(node, lines) {
|
|
149
|
+
if (!Array.isArray(node.children))
|
|
150
|
+
return null;
|
|
151
|
+
const pos = node.pos;
|
|
152
|
+
if (!pos?.startLine || !pos.startColumn)
|
|
153
|
+
return null;
|
|
154
|
+
const run = lineRun(lines[pos.startLine - 1] ?? '', pos.startColumn - 1);
|
|
155
|
+
return run ? fenceSite(pos.startLine - 1, pos.startColumn - 1, run.width, fenceKind(node)) : null;
|
|
156
|
+
}
|
|
157
|
+
function fenceKind(node) {
|
|
158
|
+
const type = String(node.type ?? '');
|
|
159
|
+
if (type === 'admonition')
|
|
160
|
+
return `${String(node.kind ?? '')} admonition`.trim();
|
|
161
|
+
return FENCE_KIND_LABELS[type] ?? type.replace(/_/g, ' ');
|
|
162
|
+
}
|
|
163
|
+
function lineSpan(node) {
|
|
164
|
+
const pos = node.pos;
|
|
165
|
+
return pos?.startLine && pos.endLine ? [pos.startLine - 1, pos.endLine - 1] : null;
|
|
166
|
+
}
|
|
167
|
+
function lineRun(line, at) {
|
|
168
|
+
const match = /^(:{3,})(.*)$/.exec(line.slice(at));
|
|
169
|
+
return match ? { width: match[1].length, bare: /^[ \t]*$/.test(match[2]) } : null;
|
|
170
|
+
}
|
|
171
|
+
function contains(range, p) {
|
|
172
|
+
return p.line === range.start.line && p.character >= range.start.character && p.character <= range.end.character;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=colon-fences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colon-fences.js","sourceRoot":"","sources":["../src/colon-fences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,aAAa,EAAkG,MAAM,+BAA+B,CAAA;AACxM,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAO3C;;;GAGG;AACH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAA;AAEpE;;;;;;GAMG;AACH,MAAM,iBAAiB,GAA2B,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,CAAA;AAE1F,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACxD,MAAM,QAAQ,GAAwB,EAAE,CAAA;IACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACvG,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,MAAc,EAAE,KAAa,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,KAAK,EAAE,CAAA;IACzF,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;IAClG,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAA;IAC5C,MAAM,MAAM,GAAqC,EAAE,CAAA;IACnD,MAAM,WAAW,GAAiB,EAAE,CAAA;IACpC,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,MAAwB,EAAQ,EAAE;QAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAAC,KAAK,MAAM,KAAK,IAAI,KAAK;gBAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAAC,OAAM;QAAC,CAAC;QACpF,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAM;QAC/C,MAAM,IAAI,GAAG,KAAgC,CAAA;QAC7C,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YAAC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;YAAC,IAAI,IAAI;gBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAAC,CAAC;QAC3G,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACtC,MAAM,UAAU,GAAG,IAAI,IAAI,MAAM,CAAA;QACjC,IAAI,IAAI,EAAE,CAAC;YACT,gFAAgF;YAChF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YACxC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;YAC1F,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;YACnF,IAAI,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,EAAE,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;gBACpF,WAAW,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,6BAA6B,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK;oBAC9H,OAAO,EAAE,QAAQ,IAAI,CAAC,KAAK,6CAA6C,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAI,cAAc,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,0CAA0C;oBAChO,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAA;YAC7L,CAAC;QACH,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO;gBAAE,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;IACnH,CAAC,CAAA;IACD,oEAAoE;IACpE,8EAA8E;IAC9E,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IACpB,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,CAAA;AAC3J,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC,OAAO,CAAA;AAC5C,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,QAAkB;IACrE,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;IACjJ,OAAO,IAAI,CAAC,CAAC,CAAC;QACZ,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,qBAAqB,CAAC,KAAK,EAAE;QAC/D,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,qBAAqB,CAAC,IAAI,EAAE;KAC/D,CAAC,CAAC,CAAC,EAAE,CAAA;AACR,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,QAAkB;IAChE,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;IACrG,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IACtB,OAAO;QACL,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,MAAM,CAAC,IAAI,yBAAyB,IAAI,CAAC,MAAM,CAAC,KAAK,yBAAyB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,GAAG,EAAE;QAC5R,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;KACzB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,KAAY;IAC/D,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC,KAAK;SACrC,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;SAChH,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,WAAW,MAAM,CAAC,IAAI,cAAc,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AACzL,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc,EAAE,QAAkB;IACvE,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;IACjJ,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAC/F,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,UAAU,CAAC,KAAe,EAAE,OAA+B,EAAE,MAAwC;IAC5G,MAAM,KAAK,GAAgB,EAAE,CAAA;IAC7B,MAAM,KAAK,GAAgB,EAAE,CAAA;IAC7B,8EAA8E;IAC9E,yEAAyE;IACzE,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC7C,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC;QAChC,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACvG,CAAC;IACD,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;QAC/C,IAAI,QAAQ,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAChC,IAAI,MAAM,EAAE,CAAC;YAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAAC,SAAQ;QAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QACnC,IAAI,CAAC,GAAG;YAAE,SAAQ;QAClB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAA;QACxC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QAC9B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YAAE,SAAQ;QACrD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACjC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK;YAAE,SAAQ;QACnD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACjF,KAAK,CAAC,GAAG,EAAE,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,SAAiB,EAAE,KAAa,EAAE,IAAY;IAC7E,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,GAAG,KAAK,EAAE,EAAE,EAAE,CAAA;AAC5G,CAAC;AACD;;;;;;;;GAQG;AACH,SAAS,YAAY,CAAC,IAA6B,EAAE,KAAe;IAClE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAA;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,GAA+D,CAAA;IAChF,IAAI,CAAC,GAAG,EAAE,SAAS,IAAI,CAAC,GAAG,CAAC,WAAW;QAAE,OAAO,IAAI,CAAA;IACpD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,CAAA;IACxE,OAAO,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,EAAE,GAAG,CAAC,WAAW,GAAG,CAAC,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACnG,CAAC;AACD,SAAS,SAAS,CAAC,IAA6B;IAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;IACpC,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAA;IAChF,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AAC3D,CAAC;AACD,SAAS,QAAQ,CAAC,IAA6B;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,GAA2D,CAAA;IAC5E,OAAO,GAAG,EAAE,SAAS,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACpF,CAAC;AACD,SAAS,OAAO,CAAC,IAAY,EAAE,EAAU;IACvC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;IAClD,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAE,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACrF,CAAC;AACD,SAAS,QAAQ,CAAC,KAAY,EAAE,CAAW;IACzC,OAAO,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAA;AAClH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { colonFenceHover, colonFenceInlayHints, colonFenceStructure, linkedColonFenceRanges } from './colon-fences.js';
|
|
4
|
+
test('reports a near closer with widths, opener, and actual outcome', () => {
|
|
5
|
+
const [found] = colonFenceStructure(':::: note\nbody\n:::\n').diagnostics;
|
|
6
|
+
assert.equal(found?.code, 'colon-fence-length-mismatch');
|
|
7
|
+
assert.deepEqual(found?.data, { authoredWidth: 3, expectedWidth: 4, openerLine: 1, openerColumn: 1, outcome: 'nested container' });
|
|
8
|
+
});
|
|
9
|
+
test('does not report exact, typed, escaped, deliberate nested, or opaque runs', () => {
|
|
10
|
+
for (const source of [
|
|
11
|
+
':::: note\n::::\n', ':::: note\n::: tip\n:::\n::::\n',
|
|
12
|
+
':::: note\n\\:::\n::::\n', ':::: note\n:::\nx\n:::\n::::\n',
|
|
13
|
+
':::: note\n```\n:::\n```\n::::\n', ':::: note\n%%%\n:::\n%%%\n::::\n',
|
|
14
|
+
])
|
|
15
|
+
assert.deepEqual(colonFenceStructure(source).diagnostics, []);
|
|
16
|
+
});
|
|
17
|
+
test('exposes exact pairs for linked editing, hover, and nesting hints', () => {
|
|
18
|
+
const source = ':::: warning\nbody\n::::\n';
|
|
19
|
+
assert.equal(linkedColonFenceRanges(source, { line: 0, character: 2 })?.ranges.length, 2);
|
|
20
|
+
assert.match(String((colonFenceHover(source, { line: 2, character: 2 })?.contents).value), /warning admonition/);
|
|
21
|
+
assert.equal(colonFenceInlayHints(source, { start: { line: 0, character: 0 }, end: { line: 3, character: 0 } }).length, 1);
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=colon-fences.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colon-fences.test.js","sourceRoot":"","sources":["../src/colon-fences.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAEtH,IAAI,CAAC,+DAA+D,EAAE,GAAG,EAAE;IACzE,MAAM,CAAC,KAAK,CAAC,GAAG,mBAAmB,CAAC,wBAAwB,CAAC,CAAC,WAAW,CAAA;IACzE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,6BAA6B,CAAC,CAAA;IACxD,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAA;AACpI,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,0EAA0E,EAAE,GAAG,EAAE;IACpF,KAAK,MAAM,MAAM,IAAI;QACnB,mBAAmB,EAAE,iCAAiC;QACtD,0BAA0B,EAAE,gCAAgC;QAC5D,kCAAkC,EAAE,kCAAkC;KACvE;QAAE,MAAM,CAAC,SAAS,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AAClE,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,kEAAkE,EAAE,GAAG,EAAE;IAC5E,MAAM,MAAM,GAAG,4BAA4B,CAAA;IAC3C,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IACzF,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,QAA8B,CAAA,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC,CAAA;IACrI,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;AAC5H,CAAC,CAAC,CAAA"}
|
package/dist/completion.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type CompletionItem, type Position } from 'vscode-languageserver/node.js';
|
|
2
|
+
import type { IndexedToken } from './workspace-index.js';
|
|
2
3
|
/**
|
|
3
4
|
* Context-aware completions driven by the text immediately before the cursor:
|
|
4
5
|
* - `::: ` opens a container -> canonical admonition kinds, and `figure`
|
|
@@ -6,4 +7,7 @@ import { type CompletionItem, type Position } from 'vscode-languageserver/node.j
|
|
|
6
7
|
* - `[^` footnote reference -> defined footnote labels
|
|
7
8
|
* - `][` reference link -> defined link reference labels
|
|
8
9
|
*/
|
|
9
|
-
export
|
|
10
|
+
export interface CompletionContext {
|
|
11
|
+
workspaceTokens?: IndexedToken[];
|
|
12
|
+
}
|
|
13
|
+
export declare function completionAt(source: string, position: Position, context?: CompletionContext): CompletionItem[];
|
package/dist/completion.js
CHANGED
|
@@ -11,14 +11,7 @@ const ADMONITIONS = ['note', 'tip', 'warning', 'danger', 'info', 'success', 'exa
|
|
|
11
11
|
* labelled for what it opens.
|
|
12
12
|
*/
|
|
13
13
|
const FIGURE_GROUP = 'figure';
|
|
14
|
-
|
|
15
|
-
* Context-aware completions driven by the text immediately before the cursor:
|
|
16
|
-
* - `::: ` opens a container -> canonical admonition kinds, and `figure`
|
|
17
|
-
* - `</#` cross-reference -> heading ids in the document
|
|
18
|
-
* - `[^` footnote reference -> defined footnote labels
|
|
19
|
-
* - `][` reference link -> defined link reference labels
|
|
20
|
-
*/
|
|
21
|
-
export function completionAt(source, position) {
|
|
14
|
+
export function completionAt(source, position, context = {}) {
|
|
22
15
|
const line = source.split(/\r?\n/)[position.line] ?? '';
|
|
23
16
|
const prefix = line.slice(0, position.character);
|
|
24
17
|
let match;
|
|
@@ -32,10 +25,14 @@ export function completionAt(source, position) {
|
|
|
32
25
|
// A crossref reaches a captioned host as well as a heading (PART 9R R4).
|
|
33
26
|
// Offering only heading ids said the others were not targets, which is the
|
|
34
27
|
// reading that made a `</#fig>` naming a figure look like a typo.
|
|
35
|
-
|
|
28
|
+
const workspace = (context.workspaceTokens ?? [])
|
|
29
|
+
.filter((token) => token.declaration && (token.kind === 'heading' || token.kind === 'caption'))
|
|
30
|
+
.map((token) => completion(token.key, CompletionItemKind.Reference, match[1], position, `${token.kind} in workspace`));
|
|
31
|
+
return uniqueCompletions([
|
|
36
32
|
...headingIds(source).map((id) => completion(id, CompletionItemKind.Reference, match[1], position, 'Heading id')),
|
|
37
33
|
...resolvableCaptionIds(source).map(({ id, detail }) => completion(id, CompletionItemKind.Reference, match[1], position, detail)),
|
|
38
|
-
|
|
34
|
+
...workspace,
|
|
35
|
+
]);
|
|
39
36
|
}
|
|
40
37
|
if ((match = /\[\^([\w-]*)$/.exec(prefix))) {
|
|
41
38
|
return footnoteLabels(source).map((label) => completion(label, CompletionItemKind.Reference, match[1], position, 'Footnote'));
|
|
@@ -43,8 +40,32 @@ export function completionAt(source, position) {
|
|
|
43
40
|
if ((match = /\]\[([\w-]*)$/.exec(prefix))) {
|
|
44
41
|
return linkReferenceLabels(source).map((label) => completion(label, CompletionItemKind.Reference, match[1], position, 'Link reference'));
|
|
45
42
|
}
|
|
43
|
+
if ((match = /\[@([\w:.-]*)$/.exec(prefix))) {
|
|
44
|
+
const local = citationLabels(source);
|
|
45
|
+
const workspace = (context.workspaceTokens ?? [])
|
|
46
|
+
.filter((token) => token.declaration && token.kind === 'citation')
|
|
47
|
+
.map((token) => token.key);
|
|
48
|
+
return [...new Set([...local, ...workspace])].map((label) => completion(label, CompletionItemKind.Reference, match[1], position, 'Citation key'));
|
|
49
|
+
}
|
|
50
|
+
if ((match = /\{[^}\n]*\b([\w-]*)$/.exec(prefix))) {
|
|
51
|
+
const attributes = ['#id', '.class', 'align=', 'valign=', 'width=', 'aligns=', 'valigns=', 'widths=',
|
|
52
|
+
'header-rows=', 'header-cols=', 'footer-rows=', 'start=', 'type=', 'title=', 'label='];
|
|
53
|
+
return attributes.map((attribute) => completion(attribute, CompletionItemKind.Property, match[1], position, 'Carve attribute'));
|
|
54
|
+
}
|
|
55
|
+
if ((match = /:(\w*)$/.exec(prefix))) {
|
|
56
|
+
return ['abbr', 'time', 'kbd', 'samp', 'var', 'cite', 'dfn'].map((name) => completion(name, CompletionItemKind.TypeParameter, match[1], position, 'Semantic span'));
|
|
57
|
+
}
|
|
46
58
|
return [];
|
|
47
59
|
}
|
|
60
|
+
function uniqueCompletions(items) {
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
return items.filter((item) => {
|
|
63
|
+
if (seen.has(item.label))
|
|
64
|
+
return false;
|
|
65
|
+
seen.add(item.label);
|
|
66
|
+
return true;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
48
69
|
function completion(value, kind, partial, position, detail) {
|
|
49
70
|
return {
|
|
50
71
|
label: value,
|
|
@@ -123,6 +144,15 @@ function linkReferenceLabels(source) {
|
|
|
123
144
|
}
|
|
124
145
|
return [...labels];
|
|
125
146
|
}
|
|
147
|
+
function citationLabels(source) {
|
|
148
|
+
const labels = new Set();
|
|
149
|
+
for (const line of source.split(/\r?\n/)) {
|
|
150
|
+
const match = /^\s{0,3}\[@([^\]]+)\]:/.exec(line);
|
|
151
|
+
if (match)
|
|
152
|
+
labels.add(match[1]);
|
|
153
|
+
}
|
|
154
|
+
return [...labels];
|
|
155
|
+
}
|
|
126
156
|
function walkBlocks(nodes, visit) {
|
|
127
157
|
for (const node of nodes) {
|
|
128
158
|
visit(node);
|
package/dist/completion.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../src/completion.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAGnB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAiC,MAAM,qBAAqB,CAAA;AACnF,OAAO,EAAE,cAAc,EAAsB,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../src/completion.ts"],"names":[],"mappings":"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"}
|
package/dist/completion.test.js
CHANGED
|
@@ -32,4 +32,11 @@ test('completion replaces the typed partial', () => {
|
|
|
32
32
|
// Nothing to complete against (no headings) -> empty, but must not throw.
|
|
33
33
|
assert.deepEqual(items, []);
|
|
34
34
|
});
|
|
35
|
+
test('suggests citation keys, semantic spans, and structural attributes', () => {
|
|
36
|
+
assert.deepEqual(labels('[@doc]: Entry\n\n[@do', 2, 4), ['doc']);
|
|
37
|
+
assert.ok(labels(':k', 0, 2).includes('kbd'));
|
|
38
|
+
const attrs = labels('{vali', 0, 5);
|
|
39
|
+
assert.ok(attrs.includes('valign='));
|
|
40
|
+
assert.ok(attrs.includes('valigns='));
|
|
41
|
+
});
|
|
35
42
|
//# sourceMappingURL=completion.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completion.test.js","sourceRoot":"","sources":["../src/completion.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,IAAY,EAAE,SAAiB,EAAY,EAAE,CAC3E,YAAY,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAErE,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACjC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;IACjC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAA;AACtC,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACtD,MAAM,MAAM,GAAG,sBAAsB,CAAA;IACrC,oEAAoE;IACpE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAA;AACzD,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAC5C,MAAM,MAAM,GAAG,8BAA8B,CAAA;IAC7C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;AAC/C,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC1C,MAAM,MAAM,GAAG,4CAA4C,CAAA;IAC3D,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;AACnD,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,wDAAwD,EAAE,GAAG,EAAE;IAClE,MAAM,MAAM,GAAG,+DAA+D,CAAA;IAC9E,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;AACnD,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC1C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AACtD,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;IACjD,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAA;IAC/D,0EAA0E;IAC1E,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AAC7B,CAAC,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"completion.test.js","sourceRoot":"","sources":["../src/completion.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DocumentHighlightKind } from 'vscode-languageserver/node.js';
|
|
2
|
+
import { scanDocument } from './workspace-index.js';
|
|
3
|
+
import { colonFenceHighlights } from './colon-fences.js';
|
|
4
|
+
export function documentHighlights(uri, source, position) {
|
|
5
|
+
const fences = colonFenceHighlights(source, position);
|
|
6
|
+
if (fences.length)
|
|
7
|
+
return fences;
|
|
8
|
+
const tokens = scanDocument(uri, source);
|
|
9
|
+
const target = tokens.find((token) => token.range.start.line === position.line &&
|
|
10
|
+
position.character >= token.range.start.character &&
|
|
11
|
+
position.character <= token.range.end.character);
|
|
12
|
+
if (!target)
|
|
13
|
+
return [];
|
|
14
|
+
return tokens
|
|
15
|
+
.filter((token) => token.kind === target.kind && token.key.toLocaleLowerCase() === target.key.toLocaleLowerCase())
|
|
16
|
+
.map((token) => ({
|
|
17
|
+
range: token.range,
|
|
18
|
+
kind: token.declaration ? DocumentHighlightKind.Write : DocumentHighlightKind.Read,
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=document-highlights.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-highlights.js","sourceRoot":"","sources":["../src/document-highlights.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAyC,MAAM,+BAA+B,CAAA;AAC5G,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAExD,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,MAAc,EAAE,QAAkB;IAChF,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACrD,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,MAAM,CAAA;IAChC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IACxC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CACnC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI;QACxC,QAAQ,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS;QACjD,QAAQ,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IAClD,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAA;IACtB,OAAO,MAAM;SACV,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC;SACjH,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACf,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,qBAAqB,CAAC,IAAI;KACnF,CAAC,CAAC,CAAA;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { DocumentHighlightKind } from 'vscode-languageserver/node.js';
|
|
4
|
+
import { documentHighlights } from './document-highlights.js';
|
|
5
|
+
test('highlights a declaration and its references', () => {
|
|
6
|
+
const found = documentHighlights('file:///a.crv', '[^n]: note\n\nSee [^n].\n', { line: 2, character: 7 });
|
|
7
|
+
assert.deepEqual(found.map((item) => item.kind), [DocumentHighlightKind.Write, DocumentHighlightKind.Read]);
|
|
8
|
+
});
|
|
9
|
+
test('highlights a colon fence and its exact closer', () => {
|
|
10
|
+
const found = documentHighlights('file:///a.crv', ':::: note\nbody\n::::\n', { line: 2, character: 2 });
|
|
11
|
+
assert.deepEqual(found.map((item) => item.range.start.line), [0, 2]);
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=document-highlights.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-highlights.test.js","sourceRoot":"","sources":["../src/document-highlights.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;IACvD,MAAM,KAAK,GAAG,kBAAkB,CAAC,eAAe,EAAE,2BAA2B,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAA;IACzG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,qBAAqB,CAAC,KAAK,EAAE,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAA;AAC7G,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,KAAK,GAAG,kBAAkB,CAAC,eAAe,EAAE,yBAAyB,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAA;IACvG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;AACtE,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Clickable destinations whose target is explicit in source. */
|
|
2
|
+
export function documentLinks(uri, source) {
|
|
3
|
+
const links = [];
|
|
4
|
+
const lines = source.replace(/\r\n?/g, '\n').split('\n');
|
|
5
|
+
for (let line = 0; line < lines.length; line += 1) {
|
|
6
|
+
const text = lines[line];
|
|
7
|
+
for (const match of text.matchAll(/!?\[[^\]]*\]\(([^\s)]+)(?:\s+"[^"]*")?\)/g)) {
|
|
8
|
+
add(links, uri, match[1], line, match.index + match[0].indexOf(match[1]));
|
|
9
|
+
}
|
|
10
|
+
for (const match of text.matchAll(/<((?:https?|mailto):[^>]+)>/g)) {
|
|
11
|
+
add(links, uri, match[1], line, match.index + 1);
|
|
12
|
+
}
|
|
13
|
+
const definition = /^(?: {0,3})\[[^\]]+\]: ([^\s]+)(?:\s+"[^"]*")?/.exec(text);
|
|
14
|
+
if (definition)
|
|
15
|
+
add(links, uri, definition[1], line, text.indexOf(definition[1]));
|
|
16
|
+
}
|
|
17
|
+
return links;
|
|
18
|
+
}
|
|
19
|
+
function add(links, uri, destination, line, character) {
|
|
20
|
+
let target;
|
|
21
|
+
try {
|
|
22
|
+
target = new URL(destination, uri).toString();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
links.push({
|
|
28
|
+
target,
|
|
29
|
+
tooltip: `Open ${destination}`,
|
|
30
|
+
range: {
|
|
31
|
+
start: { line, character },
|
|
32
|
+
end: { line, character: character + destination.length },
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=document-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-links.js","sourceRoot":"","sources":["../src/document-links.ts"],"names":[],"mappings":"AAEA,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,MAAc;IACvD,MAAM,KAAK,GAAmB,EAAE,CAAA;IAChC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACxD,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAE,CAAA;QACzB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,2CAA2C,CAAC,EAAE,CAAC;YAC/E,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAA;QAC9E,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,8BAA8B,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAM,GAAG,CAAC,CAAC,CAAA;QACpD,CAAC;QACD,MAAM,UAAU,GAAG,gDAAgD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9E,IAAI,UAAU;YAAE,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC,CAAC,CAAA;IACrF,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,GAAG,CAAC,KAAqB,EAAE,GAAW,EAAE,WAAmB,EAAE,IAAY,EAAE,SAAiB;IACnG,IAAI,MAAc,CAAA;IAClB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAA;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAM;IACR,CAAC;IACD,KAAK,CAAC,IAAI,CAAC;QACT,MAAM;QACN,OAAO,EAAE,QAAQ,WAAW,EAAE;QAC9B,KAAK,EAAE;YACL,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAC1B,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,GAAG,WAAW,CAAC,MAAM,EAAE;SACzD;KACF,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { documentLinks } from './document-links.js';
|
|
4
|
+
test('links inline, reference-definition, and autolink destinations', () => {
|
|
5
|
+
const links = documentLinks('file:///docs/a.crv', '[x](b.crv#h)\n[r]: https://example.test\n<mailto:a@example.test>\n');
|
|
6
|
+
assert.deepEqual(links.map((link) => link.target), [
|
|
7
|
+
'file:///docs/b.crv#h',
|
|
8
|
+
'https://example.test/',
|
|
9
|
+
'mailto:a@example.test',
|
|
10
|
+
]);
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=document-links.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-links.test.js","sourceRoot":"","sources":["../src/document-links.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD,IAAI,CAAC,+DAA+D,EAAE,GAAG,EAAE;IACzE,MAAM,KAAK,GAAG,aAAa,CAAC,oBAAoB,EAAE,oEAAoE,CAAC,CAAA;IACvH,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QACjD,sBAAsB;QACtB,uBAAuB;QACvB,uBAAuB;KACxB,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { carveToHtml } from '@markup-carve/carve';
|
|
6
|
+
import { formatDocument } from './format.js';
|
|
7
|
+
const corpusDir = fileURLToPath(new URL('../tests/spec/tests/corpus/', import.meta.url));
|
|
8
|
+
test('formatting preserves rendered HTML for every corpus document', () => {
|
|
9
|
+
const documents = readdirSync(corpusDir).filter((name) => name.endsWith('.crv')).sort();
|
|
10
|
+
assert.ok(documents.length > 0, 'the formatter gate found no corpus documents');
|
|
11
|
+
for (const name of documents) {
|
|
12
|
+
const source = readFileSync(`${corpusDir}/${name}`, 'utf8');
|
|
13
|
+
assert.equal(carveToHtml(formatDocument(source)), carveToHtml(source), name);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=format-corpus.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-corpus.test.js","sourceRoot":"","sources":["../src/format-corpus.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5C,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,6BAA6B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAExF,IAAI,CAAC,8DAA8D,EAAE,GAAG,EAAE;IACxE,MAAM,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACvF,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,8CAA8C,CAAC,CAAA;IAE/E,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,SAAS,IAAI,IAAI,EAAE,EAAE,MAAM,CAAC,CAAA;QAC3D,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAA;IAC9E,CAAC;AACH,CAAC,CAAC,CAAA"}
|
package/dist/format.d.ts
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
1
|
export declare function formatDocument(source: string): string;
|
|
2
|
+
export declare function formatRange(source: string, startLine: number, endLine: number): string;
|
|
3
|
+
/** Prefix inserted after Enter for container-shaped lines. */
|
|
4
|
+
export declare function continuationPrefix(source: string, line: number): string;
|