@markup-carve/carve-lsp 0.1.2 → 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 +61 -14
- package/dist/analyze.d.ts +15 -1
- package/dist/analyze.js +214 -16
- package/dist/analyze.js.map +1 -1
- package/dist/analyze.test.js +29 -1
- package/dist/analyze.test.js.map +1 -1
- package/dist/captions.d.ts +49 -0
- package/dist/captions.js +124 -0
- package/dist/captions.js.map +1 -0
- 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 +6 -2
- package/dist/completion.js +84 -8
- package/dist/completion.js.map +1 -1
- package/dist/completion.test.js +7 -0
- package/dist/completion.test.js.map +1 -1
- package/dist/composite-figure-crossref.test.d.ts +1 -0
- package/dist/composite-figure-crossref.test.js +265 -0
- package/dist/composite-figure-crossref.test.js.map +1 -0
- package/dist/composite-figure.test.d.ts +1 -0
- package/dist/composite-figure.test.js +76 -0
- package/dist/composite-figure.test.js.map +1 -0
- package/dist/definition.d.ts +4 -2
- package/dist/definition.js +28 -5
- package/dist/definition.js.map +1 -1
- package/dist/dependencies.d.ts +15 -0
- package/dist/dependencies.js +59 -0
- package/dist/dependencies.js.map +1 -0
- package/dist/diagnostic-scheduler.d.ts +58 -0
- package/dist/diagnostic-scheduler.js +70 -0
- package/dist/diagnostic-scheduler.js.map +1 -0
- package/dist/diagnostic-scheduler.test.d.ts +1 -0
- package/dist/diagnostic-scheduler.test.js +75 -0
- package/dist/diagnostic-scheduler.test.js.map +1 -0
- 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/folding.d.ts +1 -0
- package/dist/folding.js +36 -1
- package/dist/folding.js.map +1 -1
- package/dist/folding.test.js +6 -1
- package/dist/folding.test.js.map +1 -1
- 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 +78 -19
- package/dist/hover.js.map +1 -1
- package/dist/include-cache.d.ts +26 -0
- package/dist/include-cache.js +74 -0
- package/dist/include-cache.js.map +1 -0
- 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/include-definition-denial.test.d.ts +1 -0
- package/dist/include-definition-denial.test.js +86 -0
- package/dist/include-definition-denial.test.js.map +1 -0
- package/dist/include-definition.d.ts +3 -0
- package/dist/include-definition.js +37 -0
- package/dist/include-definition.js.map +1 -0
- package/dist/include-features.test.d.ts +1 -0
- package/dist/include-features.test.js +133 -0
- package/dist/include-features.test.js.map +1 -0
- package/dist/include-path.d.ts +6 -0
- package/dist/include-path.js +31 -2
- package/dist/include-path.js.map +1 -1
- package/dist/include-settings.d.ts +2 -0
- package/dist/include-settings.js +1 -0
- package/dist/include-settings.js.map +1 -1
- package/dist/includes.d.ts +8 -0
- package/dist/includes.js +28 -7
- package/dist/includes.js.map +1 -1
- 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/references.d.ts +2 -0
- package/dist/references.js +113 -21
- package/dist/references.js.map +1 -1
- 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 +45 -4
- package/dist/semantic.js.map +1 -1
- package/dist/semantic.test.js +11 -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 +353 -24
- 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/versioned-cache.d.ts +8 -0
- package/dist/versioned-cache.js +17 -0
- package/dist/versioned-cache.js.map +1 -0
- package/dist/versioned-cache.test.d.ts +1 -0
- package/dist/versioned-cache.test.js +13 -0
- package/dist/versioned-cache.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,49 @@
|
|
|
1
|
+
import { type Document, type Position as SourcePosition } from '@markup-carve/carve';
|
|
2
|
+
/**
|
|
3
|
+
* The captioned hosts a `</#id>` can name, and the text each one resolves to.
|
|
4
|
+
*
|
|
5
|
+
* The server used to walk HEADINGS for every crossref feature, so a `</#fig>`
|
|
6
|
+
* naming a captioned figure - a construct that predates composite figures -
|
|
7
|
+
* offered no completion, jumped nowhere and hovered as if the `#` inside it
|
|
8
|
+
* opened a section. This is the other half of the crossref target set (PART 9R
|
|
9
|
+
* R4): `figure`, `table`, `figure_group`, and a group's PANELS.
|
|
10
|
+
*
|
|
11
|
+
* WHERE THE NUMBER COMES FROM. Not from here. A numbered caption carries a
|
|
12
|
+
* `caption_number` node whose `n` the engine already resolved, so the text is
|
|
13
|
+
* assembled from the caption the author wrote up to and including that node -
|
|
14
|
+
* "Figure 2" out of `^ Figure #: Group caption`. Re-deriving the sequence would
|
|
15
|
+
* be a second copy of PART 9R R5, and the two would drift the first time a
|
|
16
|
+
* label sequence changed.
|
|
17
|
+
*
|
|
18
|
+
* The PANEL LETTER is the one thing derived here, because no engine API exposes
|
|
19
|
+
* it: panel order among the panels only, a..z then aa, ab (PART 9 §4c). The
|
|
20
|
+
* tests pin it against the anchor text `carveToHtml` writes for the same id, so
|
|
21
|
+
* the derivation is measured against the engine rather than against a reading
|
|
22
|
+
* of the clause.
|
|
23
|
+
*/
|
|
24
|
+
export interface CaptionTarget {
|
|
25
|
+
/** The authored id, as written. */
|
|
26
|
+
id: string;
|
|
27
|
+
/** What the host is, for a completion label and a hover heading. */
|
|
28
|
+
kind: 'figure' | 'table' | 'composite figure' | 'panel';
|
|
29
|
+
/**
|
|
30
|
+
* The text a `</#id>` resolves to - "Figure 2", "Figure 2a", "Table 1" - or
|
|
31
|
+
* null when the host drew no number. §4c: an unnumbered group's panels are
|
|
32
|
+
* anchors but not caption crossref targets, and the same has always been true
|
|
33
|
+
* of an id on an uncaptioned figure.
|
|
34
|
+
*/
|
|
35
|
+
text: string | null;
|
|
36
|
+
/** The host's own span, for go-to-definition and find-references. */
|
|
37
|
+
pos: SourcePosition | undefined;
|
|
38
|
+
}
|
|
39
|
+
/** Every captioned host in the document that carries an id, in source order. */
|
|
40
|
+
export declare function captionTargets(doc: Document): CaptionTarget[];
|
|
41
|
+
/** The target an id names, or null. Ids match case-insensitively, as elsewhere. */
|
|
42
|
+
export declare function captionTargetById(doc: Document, id: string): CaptionTarget | null;
|
|
43
|
+
/**
|
|
44
|
+
* The letter a panel takes in crossref text: a..z, then aa, ab, ... (§4c).
|
|
45
|
+
*
|
|
46
|
+
* Bijective base-26, not base-26 with a zero digit: the panel after `z` is `aa`,
|
|
47
|
+
* so 26 must not carry as `ba`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function panelLetter(index: number): string;
|
package/dist/captions.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { smartPunctuationText } from './inline-text.js';
|
|
2
|
+
/** Every captioned host in the document that carries an id, in source order. */
|
|
3
|
+
export function captionTargets(doc) {
|
|
4
|
+
const targets = [];
|
|
5
|
+
collect(doc.children, targets);
|
|
6
|
+
return targets;
|
|
7
|
+
}
|
|
8
|
+
/** The target an id names, or null. Ids match case-insensitively, as elsewhere. */
|
|
9
|
+
export function captionTargetById(doc, id) {
|
|
10
|
+
const wanted = id.toLowerCase();
|
|
11
|
+
return captionTargets(doc).find((target) => target.id.toLowerCase() === wanted) ?? null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The letter a panel takes in crossref text: a..z, then aa, ab, ... (§4c).
|
|
15
|
+
*
|
|
16
|
+
* Bijective base-26, not base-26 with a zero digit: the panel after `z` is `aa`,
|
|
17
|
+
* so 26 must not carry as `ba`.
|
|
18
|
+
*/
|
|
19
|
+
export function panelLetter(index) {
|
|
20
|
+
let out = '';
|
|
21
|
+
let n = index;
|
|
22
|
+
do {
|
|
23
|
+
out = String.fromCharCode(97 + (n % 26)) + out;
|
|
24
|
+
n = Math.floor(n / 26) - 1;
|
|
25
|
+
} while (n >= 0);
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
function collect(nodes, targets) {
|
|
29
|
+
for (const node of nodes) {
|
|
30
|
+
if (node.type === 'figure_group') {
|
|
31
|
+
const groupText = captionRefText(node.caption);
|
|
32
|
+
pushTarget(targets, node, 'composite figure', groupText);
|
|
33
|
+
collectPanels(node, groupText, targets);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (node.type === 'figure') {
|
|
37
|
+
pushTarget(targets, node, 'figure', captionRefText(node.caption));
|
|
38
|
+
}
|
|
39
|
+
else if (node.type === 'table') {
|
|
40
|
+
pushTarget(targets, node, 'table', captionRefText(node.caption));
|
|
41
|
+
}
|
|
42
|
+
// The walk continues into every container, because a captioned figure in a
|
|
43
|
+
// block quote or a list item is a crossref target like any other. A group's
|
|
44
|
+
// children are walked by collectPanels above instead, which needs the panel
|
|
45
|
+
// ORDER that this loop does not carry.
|
|
46
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
47
|
+
collect(node.children.filter(isBlockNode), targets);
|
|
48
|
+
}
|
|
49
|
+
if (node.type === 'figure' && isBlockNode(node.target)) {
|
|
50
|
+
collect([node.target], targets);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A group's panels are its DIRECT `figure` and `table` children, in source
|
|
56
|
+
* order (§4c). Everything else in the body is plain group content: it can still
|
|
57
|
+
* hold crossref targets of its own, but it draws no letter and does not shift
|
|
58
|
+
* the letters of the panels around it.
|
|
59
|
+
*/
|
|
60
|
+
function collectPanels(group, groupText, targets) {
|
|
61
|
+
let panelIndex = 0;
|
|
62
|
+
for (const child of group.children) {
|
|
63
|
+
const isPanel = child.type === 'figure' || child.type === 'table';
|
|
64
|
+
if (isPanel) {
|
|
65
|
+
pushTarget(targets, child, 'panel', groupText === null ? null : `${groupText}${panelLetter(panelIndex)}`);
|
|
66
|
+
panelIndex++;
|
|
67
|
+
if (child.type === 'figure' && isBlockNode(child.target))
|
|
68
|
+
collect([child.target], targets);
|
|
69
|
+
if ('children' in child && Array.isArray(child.children)) {
|
|
70
|
+
collect(child.children.filter(isBlockNode), targets);
|
|
71
|
+
}
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
collect([child], targets);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function pushTarget(targets, node, kind, text) {
|
|
78
|
+
const id = node.attrs?.id;
|
|
79
|
+
if (!id)
|
|
80
|
+
return;
|
|
81
|
+
targets.push({ id, kind, text, pos: node.pos });
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The crossref text a caption yields: everything up to and including its
|
|
85
|
+
* `caption_number`, trimmed. A caption with no number yields null - there is
|
|
86
|
+
* nothing for `</#id>` to render, and the engine leaves such a reference as
|
|
87
|
+
* literal text.
|
|
88
|
+
*/
|
|
89
|
+
function captionRefText(caption) {
|
|
90
|
+
if (!caption)
|
|
91
|
+
return null;
|
|
92
|
+
let out = '';
|
|
93
|
+
for (const node of caption) {
|
|
94
|
+
if (node.type === 'caption_number') {
|
|
95
|
+
const n = node.n;
|
|
96
|
+
if (typeof n !== 'number')
|
|
97
|
+
return null;
|
|
98
|
+
return `${out}${n}`.trim();
|
|
99
|
+
}
|
|
100
|
+
out += plainText([node]);
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
function plainText(nodes) {
|
|
105
|
+
let out = '';
|
|
106
|
+
for (const node of nodes) {
|
|
107
|
+
if (node.type === 'text')
|
|
108
|
+
out += node.value;
|
|
109
|
+
else if ('children' in node && Array.isArray(node.children)) {
|
|
110
|
+
out += plainText(node.children);
|
|
111
|
+
}
|
|
112
|
+
else if (node.type === 'code')
|
|
113
|
+
out += node.value;
|
|
114
|
+
else if (node.type === 'literal_inline')
|
|
115
|
+
out += node.content;
|
|
116
|
+
else
|
|
117
|
+
out += smartPunctuationText(node);
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
function isBlockNode(node) {
|
|
122
|
+
return Boolean(node && typeof node === 'object' && 'type' in node);
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=captions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captions.js","sourceRoot":"","sources":["../src/captions.ts"],"names":[],"mappings":"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"}
|
package/dist/codelens.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { scanDocument } from './workspace-index.js';
|
|
1
2
|
/**
|
|
2
3
|
* A non-interactive code lens above each footnote definition showing how many
|
|
3
4
|
* times it is referenced in the document. A definition with zero references is
|
|
@@ -31,6 +32,17 @@ export function codeLenses(source) {
|
|
|
31
32
|
},
|
|
32
33
|
});
|
|
33
34
|
});
|
|
35
|
+
const tokens = scanDocument('file:///document.crv', source);
|
|
36
|
+
for (const declaration of tokens) {
|
|
37
|
+
if (!declaration.declaration || declaration.kind === 'footnote')
|
|
38
|
+
continue;
|
|
39
|
+
const count = tokens.filter((token) => !token.declaration && token.kind === declaration.kind &&
|
|
40
|
+
token.key.toLocaleLowerCase() === declaration.key.toLocaleLowerCase()).length;
|
|
41
|
+
lenses.push({
|
|
42
|
+
range: declaration.range,
|
|
43
|
+
command: { title: count === 1 ? '1 reference' : `${count} references`, command: '' },
|
|
44
|
+
});
|
|
45
|
+
}
|
|
34
46
|
return lenses;
|
|
35
47
|
}
|
|
36
48
|
//# sourceMappingURL=codelens.js.map
|
package/dist/codelens.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codelens.js","sourceRoot":"","sources":["../src/codelens.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codelens.js","sourceRoot":"","sources":["../src/codelens.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAEnC,oFAAoF;IACpF,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAA;IAC3C,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;YACxD,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACvD,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,MAAM,GAAe,EAAE,CAAA;IAC7B,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;QAC7B,MAAM,GAAG,GAAG,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACnD,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAE,CAAA;QACrB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACvC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC,MAAM,CAAA;QAC5B,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE;gBACL,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE;gBACzC,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;aAC3D;YACD,OAAO,EAAE;gBACP,KAAK,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,KAAK,aAAa;gBAC1D,OAAO,EAAE,EAAE;aACZ;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,YAAY,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAA;IAC3D,KAAK,MAAM,WAAW,IAAI,MAAM,EAAE,CAAC;QACjC,IAAI,CAAC,WAAW,CAAC,WAAW,IAAI,WAAW,CAAC,IAAI,KAAK,UAAU;YAAE,SAAQ;QACzE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CACpC,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI;YACrD,KAAK,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,WAAW,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAA;QAC/E,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,WAAW,CAAC,KAAK;YACxB,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,KAAK,aAAa,EAAE,OAAO,EAAE,EAAE,EAAE;SACrF,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
|
package/dist/codelens.test.js
CHANGED
|
@@ -15,4 +15,9 @@ test('uses the singular form for a single reference', () => {
|
|
|
15
15
|
test('emits no lenses without footnote definitions', () => {
|
|
16
16
|
assert.deepEqual(codeLenses('just prose with [^ref] but no def line that matches? [^ref]'), []);
|
|
17
17
|
});
|
|
18
|
+
test('counts heading, citation, and link-reference usages', () => {
|
|
19
|
+
const source = '{#h}\n# H\n\nSee </#h> and [x][r] [@c].\n\n[r]: /u\n[@c]: Entry\n';
|
|
20
|
+
const titles = codeLenses(source).map((lens) => lens.command?.title);
|
|
21
|
+
assert.deepEqual(titles, ['1 reference', '1 reference', '1 reference']);
|
|
22
|
+
});
|
|
18
23
|
//# sourceMappingURL=codelens.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codelens.test.js","sourceRoot":"","sources":["../src/codelens.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC1D,MAAM,MAAM,GAAG,4CAA4C,CAAA;IAC3D,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAA;IACjC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAClD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAA;IAC1C,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAA;AAC5C,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,MAAM,GAAG,UAAU,CAAC,wBAAwB,CAAC,CAAA;IACnD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;AACxD,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,6DAA6D,CAAC,EAAE,EAAE,CAAC,CAAA;AACjG,CAAC,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"codelens.test.js","sourceRoot":"","sources":["../src/codelens.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC1D,MAAM,MAAM,GAAG,4CAA4C,CAAA;IAC3D,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAA;IACjC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAClD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAA;IAC1C,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAA;AAC5C,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,MAAM,GAAG,UAAU,CAAC,wBAAwB,CAAC,CAAA;IACnD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;AACxD,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,6DAA6D,CAAC,EAAE,EAAE,CAAC,CAAA;AACjG,CAAC,CAAC,CAAA;AAEF,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,MAAM,MAAM,GAAG,mEAAmE,CAAA;IAClF,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACpE,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC,CAAA;AACzE,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
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 { parse } from '@markup-carve/carve';
|
|
6
|
+
import { colonFenceHighlights, colonFenceOpeners, colonFenceStructure } from './colon-fences.js';
|
|
7
|
+
/**
|
|
8
|
+
* WHY THIS FILE EXISTS (#157). `colonFenceStructure` used to name the colon-fence
|
|
9
|
+
* node types in a hand-written chain, and returned `null` for anything the chain
|
|
10
|
+
* did not name. A missing kind is therefore invisible rather than loud: no
|
|
11
|
+
* opener/closer pair, no highlight, no length-mismatch diagnostic, and no failing
|
|
12
|
+
* test - the container simply is not there.
|
|
13
|
+
*
|
|
14
|
+
* Two kinds had already fallen through it. The fenced block quote (carve#1718) is
|
|
15
|
+
* not a node type at all - it is a `block_quote` carrying `fenced: true` - and
|
|
16
|
+
* `figure_group` (carve#1122) was never added when composite figures shipped. So
|
|
17
|
+
* the gate below is written against the CORPUS rather than against a list this
|
|
18
|
+
* repo maintains: it reads what a `:::` line does from the parser and the source,
|
|
19
|
+
* and a seventh kind fails it on arrival instead of being skipped.
|
|
20
|
+
*/
|
|
21
|
+
const corpusDir = fileURLToPath(new URL('../tests/spec/tests/corpus/', import.meta.url));
|
|
22
|
+
/**
|
|
23
|
+
* Types that can legitimately START on a `:::` run without being a fence: prose
|
|
24
|
+
* the parser folded a literal `:::` line into, and the nodes it reads verbatim.
|
|
25
|
+
* The gate is written as this inversion on purpose - a NEW container type is not
|
|
26
|
+
* on it, so it is expected by default rather than having to be remembered.
|
|
27
|
+
*/
|
|
28
|
+
const NOT_A_FENCE = new Set(['text', 'paragraph', 'code', 'code_block', 'raw_block', 'raw_inline', 'literal_inline', 'comment']);
|
|
29
|
+
/**
|
|
30
|
+
* The independent reading of "a colon fence is here": any node in the parsed
|
|
31
|
+
* document whose start position lands on a `:::` run and which is not one of the
|
|
32
|
+
* above. No fence type is named, no `children` is required, and the walk starts at
|
|
33
|
+
* the document rather than at its `children` - so a kind that is not a type of its
|
|
34
|
+
* own, a kind carrying no children, and a container hanging off a field other
|
|
35
|
+
* than `children` each fail this gate rather than disappearing.
|
|
36
|
+
*/
|
|
37
|
+
function fenceLinesInAst(source) {
|
|
38
|
+
const lines = source.replace(/\r\n?/g, '\n').split('\n');
|
|
39
|
+
const found = new Set();
|
|
40
|
+
const walk = (value) => {
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
for (const child of value)
|
|
43
|
+
walk(child);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (!value || typeof value !== 'object')
|
|
47
|
+
return;
|
|
48
|
+
const node = value;
|
|
49
|
+
const pos = node.pos;
|
|
50
|
+
if (node.type && !NOT_A_FENCE.has(String(node.type)) && pos?.startLine && pos.startColumn && /^:{3,}/.test((lines[pos.startLine - 1] ?? '').slice(pos.startColumn - 1))) {
|
|
51
|
+
found.add(pos.startLine - 1);
|
|
52
|
+
}
|
|
53
|
+
for (const [name, child] of Object.entries(node))
|
|
54
|
+
if (name !== 'pos' && name !== 'attrs')
|
|
55
|
+
walk(child);
|
|
56
|
+
};
|
|
57
|
+
walk(parse(source, { positions: true }));
|
|
58
|
+
return [...found].sort((a, b) => a - b);
|
|
59
|
+
}
|
|
60
|
+
test('every colon fence in the corpus is a container the server recognizes', () => {
|
|
61
|
+
const documents = readdirSync(corpusDir).filter((name) => name.endsWith('.crv')).sort();
|
|
62
|
+
assert.ok(documents.length > 0, 'the colon-fence gate found no corpus documents');
|
|
63
|
+
const missed = [];
|
|
64
|
+
const kinds = new Set();
|
|
65
|
+
let checked = 0;
|
|
66
|
+
for (const name of documents) {
|
|
67
|
+
const source = readFileSync(`${corpusDir}/${name}`, 'utf8');
|
|
68
|
+
const expected = fenceLinesInAst(source);
|
|
69
|
+
if (expected.length === 0)
|
|
70
|
+
continue;
|
|
71
|
+
const sites = new Map(colonFenceOpeners(source).map((site) => [site.range.start.line, site]));
|
|
72
|
+
for (const line of expected) {
|
|
73
|
+
checked += 1;
|
|
74
|
+
const site = sites.get(line);
|
|
75
|
+
if (site)
|
|
76
|
+
kinds.add(site.kind);
|
|
77
|
+
else
|
|
78
|
+
missed.push(`${name}:${line + 1}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
assert.ok(checked > 0, 'the corpus contains no colon fences, so this gate proved nothing');
|
|
82
|
+
assert.deepEqual(missed, [], `colon fences the server does not see (a node type it cannot name?): ${missed.join(', ')}`);
|
|
83
|
+
// The gate is only worth its runtime while the corpus still spells the kinds
|
|
84
|
+
// that fell through the old chain, so it says so rather than assuming it.
|
|
85
|
+
for (const kind of ['block quote', 'figure group']) {
|
|
86
|
+
assert.ok(kinds.has(kind), `the corpus no longer exercises a ${kind} colon fence, so this gate stopped covering it`);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
test('a fenced block quote pairs, and the marker spelling has nothing to pair', () => {
|
|
90
|
+
// Both directions on the shape #157 is about: only the fenced spelling has
|
|
91
|
+
// fences, so only it produces a pair - reporting one for `> x` would be the
|
|
92
|
+
// over-broad fix.
|
|
93
|
+
const fenced = colonFenceStructure('::: >\nquoted\n:::\n');
|
|
94
|
+
assert.deepEqual(fenced.pairs.map(({ opener, closer }) => [opener.kind, opener.range.start.line, closer.range.start.line]), [['block quote', 0, 2]]);
|
|
95
|
+
assert.deepEqual(colonFenceStructure('> quoted\n').pairs, []);
|
|
96
|
+
});
|
|
97
|
+
test('putting the cursor on either fence of a block quote highlights both', () => {
|
|
98
|
+
const source = '::: >\nquoted\n:::\n';
|
|
99
|
+
for (const position of [{ line: 0, character: 1 }, { line: 2, character: 1 }]) {
|
|
100
|
+
assert.deepEqual(colonFenceHighlights(source, position).map((h) => h.range.start.line), [0, 2], JSON.stringify(position));
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
test('the length-mismatch diagnostic fires inside a fenced block quote', () => {
|
|
104
|
+
// The container has to become a `parent` in the walk for this to be reachable
|
|
105
|
+
// at all; while the quote was skipped, no diagnostic could be raised inside one.
|
|
106
|
+
const [found] = colonFenceStructure(':::: >\nquoted\n:::\n').diagnostics;
|
|
107
|
+
assert.equal(found?.code, 'colon-fence-length-mismatch');
|
|
108
|
+
assert.deepEqual(found?.data, { authoredWidth: 3, expectedWidth: 4, openerLine: 1, openerColumn: 1, outcome: 'nested container' });
|
|
109
|
+
});
|
|
110
|
+
test('a composite figure pairs too', () => {
|
|
111
|
+
// The kind that was already missing before the one #157 was filed for.
|
|
112
|
+
assert.deepEqual(colonFenceStructure('::: figure\n\n\n\n:::\n^ Two panels\n').pairs
|
|
113
|
+
.map(({ opener, closer }) => [opener.kind, opener.range.start.line, closer.range.start.line]), [['figure group', 0, 4]]);
|
|
114
|
+
});
|
|
115
|
+
test('a closer is found whether or not the extent spans it', () => {
|
|
116
|
+
// A div spans its closing fence and a fenced quote does not, so pairing off
|
|
117
|
+
// `pos.end` read the wrong line for one of them. Here the quote's extent ends
|
|
118
|
+
// ON the inner div's closer.
|
|
119
|
+
assert.deepEqual(colonFenceStructure('::: >\n::: note\nx\n:::\n:::\n').pairs
|
|
120
|
+
.map(({ opener, closer }) => [opener.kind, opener.range.start.line, closer.range.start.line])
|
|
121
|
+
.sort((a, b) => Number(a[1]) - Number(b[1])), [['block quote', 0, 4], ['note admonition', 1, 3]]);
|
|
122
|
+
});
|
|
123
|
+
test('a bare fence inside a verbatim block closes nothing', () => {
|
|
124
|
+
for (const source of [':::: >\n```\n::::\n```\n::::\n', ':::: >\n%%%\n::::\n%%%\n::::\n']) {
|
|
125
|
+
assert.deepEqual(colonFenceStructure(source).pairs.map(({ closer }) => closer.range.start.line), [4], source);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
//# sourceMappingURL=colon-fence-kinds.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colon-fence-kinds.test.js","sourceRoot":"","sources":["../src/colon-fence-kinds.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Diagnostic, type DocumentHighlight, type Hover, type InlayHint, type Position, type Range } from 'vscode-languageserver/node.js';
|
|
2
|
+
export interface FenceSite {
|
|
3
|
+
kind: string;
|
|
4
|
+
width: number;
|
|
5
|
+
range: Range;
|
|
6
|
+
}
|
|
7
|
+
export interface FencePair {
|
|
8
|
+
opener: FenceSite;
|
|
9
|
+
closer: FenceSite;
|
|
10
|
+
}
|
|
11
|
+
export declare function colonFenceStructure(source: string): {
|
|
12
|
+
openers: FenceSite[];
|
|
13
|
+
pairs: FencePair[];
|
|
14
|
+
diagnostics: Diagnostic[];
|
|
15
|
+
};
|
|
16
|
+
/** Every colon-fence opener the server recognizes, closed or not. */
|
|
17
|
+
export declare function colonFenceOpeners(source: string): FenceSite[];
|
|
18
|
+
export declare function colonFenceHighlights(source: string, position: Position): DocumentHighlight[];
|
|
19
|
+
export declare function colonFenceHover(source: string, position: Position): Hover | null;
|
|
20
|
+
export declare function colonFenceInlayHints(source: string, range: Range): InlayHint[];
|
|
21
|
+
export declare function linkedColonFenceRanges(source: string, position: Position): {
|
|
22
|
+
ranges: Range[];
|
|
23
|
+
wordPattern: string;
|
|
24
|
+
} | null;
|
|
@@ -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":"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"}
|
|
@@ -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"}
|