@markup-carve/carve-lsp 0.1.0 → 0.1.2
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 +55 -0
- package/dist/analyze.d.ts +17 -1
- package/dist/analyze.js +57 -1
- package/dist/analyze.js.map +1 -1
- package/dist/analyze.test.js +95 -0
- package/dist/analyze.test.js.map +1 -1
- package/dist/definition.js +26 -2
- package/dist/definition.js.map +1 -1
- package/dist/definition.test.js +8 -0
- package/dist/definition.test.js.map +1 -1
- package/dist/folding.js +4 -4
- package/dist/folding.js.map +1 -1
- package/dist/hover.js +33 -16
- package/dist/hover.js.map +1 -1
- package/dist/include-directive.d.ts +61 -0
- package/dist/include-directive.js +158 -0
- package/dist/include-directive.js.map +1 -0
- package/dist/include-directive.test.d.ts +1 -0
- package/dist/include-directive.test.js +83 -0
- package/dist/include-directive.test.js.map +1 -0
- package/dist/include-path.d.ts +63 -0
- package/dist/include-path.js +135 -0
- package/dist/include-path.js.map +1 -0
- package/dist/include-path.test.d.ts +1 -0
- package/dist/include-path.test.js +189 -0
- package/dist/include-path.test.js.map +1 -0
- package/dist/include-security-conformance.test.d.ts +1 -0
- package/dist/include-security-conformance.test.js +100 -0
- package/dist/include-security-conformance.test.js.map +1 -0
- package/dist/include-settings.d.ts +50 -0
- package/dist/include-settings.js +135 -0
- package/dist/include-settings.js.map +1 -0
- package/dist/include-settings.test.d.ts +1 -0
- package/dist/include-settings.test.js +191 -0
- package/dist/include-settings.test.js.map +1 -0
- package/dist/includes.d.ts +78 -0
- package/dist/includes.js +158 -0
- package/dist/includes.js.map +1 -0
- package/dist/includes.test.d.ts +1 -0
- package/dist/includes.test.js +202 -0
- package/dist/includes.test.js.map +1 -0
- package/dist/inline-text.d.ts +24 -0
- package/dist/inline-text.js +34 -0
- package/dist/inline-text.js.map +1 -0
- package/dist/inline-text.test.d.ts +1 -0
- package/dist/inline-text.test.js +32 -0
- package/dist/inline-text.test.js.map +1 -0
- package/dist/position-encoding.test.d.ts +1 -0
- package/dist/position-encoding.test.js +143 -0
- package/dist/position-encoding.test.js.map +1 -0
- package/dist/position.d.ts +82 -0
- package/dist/position.js +137 -0
- package/dist/position.js.map +1 -0
- package/dist/position.test.d.ts +1 -0
- package/dist/position.test.js +61 -0
- package/dist/position.test.js.map +1 -0
- package/dist/references.js +7 -0
- package/dist/references.js.map +1 -1
- package/dist/semantic.d.ts +14 -0
- package/dist/semantic.js +113 -27
- package/dist/semantic.js.map +1 -1
- package/dist/semantic.test.d.ts +1 -0
- package/dist/semantic.test.js +93 -0
- package/dist/semantic.test.js.map +1 -0
- package/dist/server.js +50 -23
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/hover.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MarkupKind } from 'vscode-languageserver/node.js';
|
|
2
2
|
import { parse, resolve, } from '@markup-carve/carve';
|
|
3
|
+
import { astColumnToCharacter, characterToAstColumn, sourceLines } from './position.js';
|
|
3
4
|
const rules = [
|
|
4
5
|
{
|
|
5
6
|
pattern: /#{1,6}/g,
|
|
@@ -39,15 +40,16 @@ const rules = [
|
|
|
39
40
|
},
|
|
40
41
|
];
|
|
41
42
|
export function hoverAt(source, position) {
|
|
43
|
+
const lines = sourceLines(source);
|
|
42
44
|
try {
|
|
43
|
-
const hover = astHoverAt(resolve(parse(source)), position);
|
|
45
|
+
const hover = astHoverAt(resolve(parse(source)), position, lines);
|
|
44
46
|
if (hover)
|
|
45
47
|
return hover;
|
|
46
48
|
}
|
|
47
49
|
catch {
|
|
48
50
|
// Fall back to lexical help below for documents that do not parse.
|
|
49
51
|
}
|
|
50
|
-
const line =
|
|
52
|
+
const line = lines[position.line];
|
|
51
53
|
if (line === undefined)
|
|
52
54
|
return null;
|
|
53
55
|
for (const rule of rules) {
|
|
@@ -71,10 +73,17 @@ export function hoverAt(source, position) {
|
|
|
71
73
|
}
|
|
72
74
|
return null;
|
|
73
75
|
}
|
|
74
|
-
function astHoverAt(doc, position) {
|
|
76
|
+
function astHoverAt(doc, position, lines) {
|
|
77
|
+
// The request counts UTF-16 code units and the AST may count codepoints, so
|
|
78
|
+
// the cursor converts once here and every comparison below stays in the
|
|
79
|
+
// parser's unit. The range converts back on the way out.
|
|
80
|
+
const cursor = {
|
|
81
|
+
line: position.line,
|
|
82
|
+
character: characterToAstColumn(lines[position.line] ?? '', position.character) - 1,
|
|
83
|
+
};
|
|
75
84
|
const matches = [];
|
|
76
85
|
for (const node of doc.children)
|
|
77
|
-
collectBlock(matches, node,
|
|
86
|
+
collectBlock(matches, node, cursor);
|
|
78
87
|
matches.sort((a, b) => spanSize(a.pos) - spanSize(b.pos));
|
|
79
88
|
const match = matches[0];
|
|
80
89
|
if (!match)
|
|
@@ -84,7 +93,7 @@ function astHoverAt(doc, position) {
|
|
|
84
93
|
kind: MarkupKind.Markdown,
|
|
85
94
|
value: match.contents,
|
|
86
95
|
},
|
|
87
|
-
range: toRange(match.pos),
|
|
96
|
+
range: toRange(match.pos, lines),
|
|
88
97
|
};
|
|
89
98
|
}
|
|
90
99
|
function collectBlock(matches, node, position) {
|
|
@@ -96,7 +105,7 @@ function collectBlock(matches, node, position) {
|
|
|
96
105
|
case 'paragraph':
|
|
97
106
|
collectInline(matches, node.children, position);
|
|
98
107
|
break;
|
|
99
|
-
case '
|
|
108
|
+
case 'block_quote':
|
|
100
109
|
node.children.forEach((child) => collectBlock(matches, child, position));
|
|
101
110
|
break;
|
|
102
111
|
case 'list':
|
|
@@ -108,7 +117,7 @@ function collectBlock(matches, node, position) {
|
|
|
108
117
|
if (node.type === 'admonition' && node.title)
|
|
109
118
|
collectInline(matches, node.title, position);
|
|
110
119
|
break;
|
|
111
|
-
case '
|
|
120
|
+
case 'definition_list':
|
|
112
121
|
node.items.forEach((item) => {
|
|
113
122
|
item.terms.forEach((term) => collectInline(matches, term, position));
|
|
114
123
|
item.definitions.forEach((definition) => definition.forEach((child) => collectBlock(matches, child, position)));
|
|
@@ -145,11 +154,11 @@ function blockContents(node) {
|
|
|
145
154
|
switch (node.type) {
|
|
146
155
|
case 'heading':
|
|
147
156
|
return '**Heading**\n\n`#` through `######` create section headings.';
|
|
148
|
-
case '
|
|
157
|
+
case 'code_block':
|
|
149
158
|
return '**Code Block**\n\nTriple backtick or tilde fences create verbatim code blocks.';
|
|
150
|
-
case '
|
|
159
|
+
case 'raw_block':
|
|
151
160
|
return '**Raw Block**\n\nRaw passthrough blocks render only for matching output formats.';
|
|
152
|
-
case '
|
|
161
|
+
case 'block_quote':
|
|
153
162
|
return '**Block Quote**\n\nLines beginning with `>` create quoted blocks.';
|
|
154
163
|
case 'list':
|
|
155
164
|
return '**List**\n\nUse bullets, task markers, or ordered markers for lists.';
|
|
@@ -167,20 +176,22 @@ function inlineContents(node) {
|
|
|
167
176
|
switch (node.type) {
|
|
168
177
|
case 'strong':
|
|
169
178
|
return '**Bold**\n\nCarve uses single asterisks for bold text: `*bold*`.';
|
|
170
|
-
case '
|
|
179
|
+
case 'emphasis':
|
|
171
180
|
return '**Italic**\n\nCarve uses slashes for italic text: `/italic/`.';
|
|
172
181
|
case 'underline':
|
|
173
182
|
return '**Underline**\n\nCarve uses underscores for underline: `_underlined_`.';
|
|
174
183
|
case 'strike':
|
|
175
184
|
return '**Strikethrough**\n\nCarve uses tildes for strikethrough: `~removed~`.';
|
|
176
|
-
case '
|
|
185
|
+
case 'subscript':
|
|
177
186
|
return '**Subscript**\n\nCarve subscript uses the braced form `{,text,}`, e.g. `H{,2,}O`. A bare comma is literal text.';
|
|
178
|
-
case '
|
|
187
|
+
case 'superscript':
|
|
179
188
|
return '**Superscript**\n\nCarve superscript uses the braced form `{^text^}`, e.g. `E=mc{^2^}`. A bare caret is literal text.';
|
|
180
189
|
case 'highlight':
|
|
181
190
|
return '**Highlight**\n\nCarve highlight is a single equals: `=marked=` (use the forced form `{=marked=}` intraword).';
|
|
182
191
|
case 'code':
|
|
183
192
|
return '**Inline Code**\n\nBackticks mark inline code spans.';
|
|
193
|
+
case 'literal_inline':
|
|
194
|
+
return '**Inline Literal**\n\nA `!` prefix on a verbatim span renders its content as ordinary prose, without code styling: `` !`/kaet/` ``. Mirrors the `$`-math prefix.';
|
|
184
195
|
case 'link':
|
|
185
196
|
return '**Link**\n\nInline links use `[label](url)` or reference-link syntax.';
|
|
186
197
|
case 'image':
|
|
@@ -208,10 +219,16 @@ function contains(pos, position) {
|
|
|
208
219
|
return false;
|
|
209
220
|
return true;
|
|
210
221
|
}
|
|
211
|
-
function toRange(pos) {
|
|
222
|
+
function toRange(pos, lines) {
|
|
212
223
|
return {
|
|
213
|
-
start: {
|
|
214
|
-
|
|
224
|
+
start: {
|
|
225
|
+
line: pos.startLine - 1,
|
|
226
|
+
character: astColumnToCharacter(lines[pos.startLine - 1] ?? '', pos.startColumn ?? 1),
|
|
227
|
+
},
|
|
228
|
+
end: {
|
|
229
|
+
line: pos.endLine - 1,
|
|
230
|
+
character: astColumnToCharacter(lines[pos.endLine - 1] ?? '', pos.endColumn ?? 1),
|
|
231
|
+
},
|
|
215
232
|
};
|
|
216
233
|
}
|
|
217
234
|
function spanSize(pos) {
|
package/dist/hover.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hover.js","sourceRoot":"","sources":["../src/hover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA6B,MAAM,+BAA+B,CAAA;AACrF,OAAO,EACL,KAAK,EACL,OAAO,GAKR,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"hover.js","sourceRoot":"","sources":["../src/hover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recognition of processor-level `{{ … }}` include directives (PART 6 grammar,
|
|
3
|
+
* PART 9 §19 rule I1).
|
|
4
|
+
*
|
|
5
|
+
* Kept separate from {@link ./includes.js} so consumers that only need to
|
|
6
|
+
* RECOGNIZE a directive do not pull in the expander's file-system imports.
|
|
7
|
+
*
|
|
8
|
+
* The regular expressions and `parseDirective` mirror `src/include-directive.ts`
|
|
9
|
+
* in the carve-js engine deliberately: one rule, one spelling. What is added
|
|
10
|
+
* here is source-level scanning. The engine recognizes directives on
|
|
11
|
+
* reassembled inline runs of a parsed AST; this server has no include-aware
|
|
12
|
+
* engine to parse with (the pinned `@markup-carve/carve` predates the pass), so
|
|
13
|
+
* it scans the raw document text and shields verbatim regions itself.
|
|
14
|
+
*/
|
|
15
|
+
export interface Directive {
|
|
16
|
+
raw: string;
|
|
17
|
+
path: string;
|
|
18
|
+
section?: string;
|
|
19
|
+
lines?: {
|
|
20
|
+
start: number;
|
|
21
|
+
end: number;
|
|
22
|
+
};
|
|
23
|
+
/** Literal signed offset, or "auto" to derive it from the include site. */
|
|
24
|
+
shift: number | 'auto';
|
|
25
|
+
/** 0-based start offset in the scanned source, inclusive. */
|
|
26
|
+
start: number;
|
|
27
|
+
/** 0-based end offset in the scanned source, exclusive. */
|
|
28
|
+
end: number;
|
|
29
|
+
}
|
|
30
|
+
export declare const DIRECTIVE_SCAN_RE: RegExp;
|
|
31
|
+
export declare const DIRECTIVE_FULL_RE: RegExp;
|
|
32
|
+
/**
|
|
33
|
+
* Parse one candidate directive token. Returns null when the token is not a
|
|
34
|
+
* well-formed directive per I1 (a bad shape, or an unrecognized or malformed
|
|
35
|
+
* option), in which case it stays ordinary text and `onInvalidOption` fires so
|
|
36
|
+
* the caller can raise `include-unknown-option`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseDirective(raw: string, onInvalidOption?: (part: string) => void): Omit<Directive, 'start' | 'end'> | null;
|
|
39
|
+
/**
|
|
40
|
+
* Regions of `source` whose content is verbatim and therefore shielded from
|
|
41
|
+
* include recognition (I9): fenced blocks of every flavour, raw blocks
|
|
42
|
+
* (```` ```=html ````) included, and inline code spans.
|
|
43
|
+
*
|
|
44
|
+
* Approximate by design, and deliberately so: it is a line-and-backtick scan
|
|
45
|
+
* rather than a parse, because the pinned engine cannot tell us where a
|
|
46
|
+
* directive sits relative to a fence. It errs toward shielding - an ambiguous
|
|
47
|
+
* region is treated as verbatim and the directive stays literal, which is the
|
|
48
|
+
* safe direction for a file-read capability.
|
|
49
|
+
*/
|
|
50
|
+
export declare function verbatimSpans(source: string): Array<{
|
|
51
|
+
start: number;
|
|
52
|
+
end: number;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Locate the well-formed directives in `source`, in source order, skipping any
|
|
56
|
+
* that begin inside a verbatim region (I9).
|
|
57
|
+
*
|
|
58
|
+
* `onInvalidOption` fires for a token of directive shape whose options do not
|
|
59
|
+
* parse, so the caller can raise `include-unknown-option` on it.
|
|
60
|
+
*/
|
|
61
|
+
export declare function findDirectives(source: string, onInvalidOption?: (part: string, start: number, end: number) => void): Directive[];
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recognition of processor-level `{{ … }}` include directives (PART 6 grammar,
|
|
3
|
+
* PART 9 §19 rule I1).
|
|
4
|
+
*
|
|
5
|
+
* Kept separate from {@link ./includes.js} so consumers that only need to
|
|
6
|
+
* RECOGNIZE a directive do not pull in the expander's file-system imports.
|
|
7
|
+
*
|
|
8
|
+
* The regular expressions and `parseDirective` mirror `src/include-directive.ts`
|
|
9
|
+
* in the carve-js engine deliberately: one rule, one spelling. What is added
|
|
10
|
+
* here is source-level scanning. The engine recognizes directives on
|
|
11
|
+
* reassembled inline runs of a parsed AST; this server has no include-aware
|
|
12
|
+
* engine to parse with (the pinned `@markup-carve/carve` predates the pass), so
|
|
13
|
+
* it scans the raw document text and shields verbatim regions itself.
|
|
14
|
+
*/
|
|
15
|
+
export const DIRECTIVE_SCAN_RE = /\{\{\s+(?:"((?:\\.|[^"\\])*)"|\u201c([^\u201d]*)\u201d|([^#@}\s"\u201c]+))((?:\s+#[A-Za-z_][\w-]*)?)(.*?)\s+\}\}/g;
|
|
16
|
+
export const DIRECTIVE_FULL_RE = /^\{\{\s+(?:"((?:\\.|[^"\\])*)"|\u201c([^\u201d]*)\u201d|([^#@}\s"\u201c]+))((?:\s+#[A-Za-z_][\w-]*)?)(.*?)\s+\}\}$/;
|
|
17
|
+
const OPTION_RE = /^@([A-Za-z_][\w-]*):([^#@}\s]+)$/;
|
|
18
|
+
function unescapeQuotedPath(value) {
|
|
19
|
+
return value.replace(/\\(["\\])/g, '$1');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Parse one candidate directive token. Returns null when the token is not a
|
|
23
|
+
* well-formed directive per I1 (a bad shape, or an unrecognized or malformed
|
|
24
|
+
* option), in which case it stays ordinary text and `onInvalidOption` fires so
|
|
25
|
+
* the caller can raise `include-unknown-option`.
|
|
26
|
+
*/
|
|
27
|
+
export function parseDirective(raw, onInvalidOption) {
|
|
28
|
+
const m = DIRECTIVE_FULL_RE.exec(raw);
|
|
29
|
+
if (!m)
|
|
30
|
+
return null;
|
|
31
|
+
const includePath = m[1] !== undefined ? unescapeQuotedPath(m[1]) : (m[2] ?? m[3]);
|
|
32
|
+
if (includePath === '')
|
|
33
|
+
return null;
|
|
34
|
+
const sectionPart = m[4]?.trim();
|
|
35
|
+
const section = sectionPart ? sectionPart.slice(1) : undefined;
|
|
36
|
+
let lines;
|
|
37
|
+
let shift = 0;
|
|
38
|
+
const rest = m[5]?.trim();
|
|
39
|
+
if (rest) {
|
|
40
|
+
for (const part of rest.split(/\s+/)) {
|
|
41
|
+
const opt = OPTION_RE.exec(part);
|
|
42
|
+
const invalid = () => {
|
|
43
|
+
// I1: an unrecognized (or malformed) option makes the directive
|
|
44
|
+
// unresolvable - warning plus literal text, never a silent drop.
|
|
45
|
+
if (part.startsWith('@'))
|
|
46
|
+
onInvalidOption?.(part);
|
|
47
|
+
return null;
|
|
48
|
+
};
|
|
49
|
+
if (!opt)
|
|
50
|
+
return invalid();
|
|
51
|
+
const [, key, value] = opt;
|
|
52
|
+
if (key === 'lines') {
|
|
53
|
+
const lm = /^([1-9]\d*)-([1-9]\d*)$/.exec(value);
|
|
54
|
+
if (!lm)
|
|
55
|
+
return invalid();
|
|
56
|
+
lines = { start: Number(lm[1]), end: Number(lm[2]) };
|
|
57
|
+
if (lines.end < lines.start)
|
|
58
|
+
return invalid();
|
|
59
|
+
}
|
|
60
|
+
else if (key === 'shift') {
|
|
61
|
+
// I8: a signed integer or the literal "auto", never both forms.
|
|
62
|
+
if (value === 'auto')
|
|
63
|
+
shift = 'auto';
|
|
64
|
+
else if (!/^[+-]?\d+$/.test(value))
|
|
65
|
+
return invalid();
|
|
66
|
+
else
|
|
67
|
+
shift = Number(value);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return invalid();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const directive = { raw, path: includePath, shift };
|
|
75
|
+
if (section !== undefined)
|
|
76
|
+
directive.section = section;
|
|
77
|
+
if (lines !== undefined)
|
|
78
|
+
directive.lines = lines;
|
|
79
|
+
return directive;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Regions of `source` whose content is verbatim and therefore shielded from
|
|
83
|
+
* include recognition (I9): fenced blocks of every flavour, raw blocks
|
|
84
|
+
* (```` ```=html ````) included, and inline code spans.
|
|
85
|
+
*
|
|
86
|
+
* Approximate by design, and deliberately so: it is a line-and-backtick scan
|
|
87
|
+
* rather than a parse, because the pinned engine cannot tell us where a
|
|
88
|
+
* directive sits relative to a fence. It errs toward shielding - an ambiguous
|
|
89
|
+
* region is treated as verbatim and the directive stays literal, which is the
|
|
90
|
+
* safe direction for a file-read capability.
|
|
91
|
+
*/
|
|
92
|
+
export function verbatimSpans(source) {
|
|
93
|
+
const spans = [];
|
|
94
|
+
let offset = 0;
|
|
95
|
+
let fence = null;
|
|
96
|
+
for (const line of source.split('\n')) {
|
|
97
|
+
const lineStart = offset;
|
|
98
|
+
const lineEnd = offset + line.length;
|
|
99
|
+
offset = lineEnd + 1;
|
|
100
|
+
if (fence) {
|
|
101
|
+
const close = /^ {0,3}(`{3,}|~{3,})[ \t]*$/.exec(line);
|
|
102
|
+
if (close && close[1][0] === fence.char && close[1].length >= fence.length) {
|
|
103
|
+
spans.push({ start: fence.start, end: lineEnd });
|
|
104
|
+
fence = null;
|
|
105
|
+
}
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const open = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line);
|
|
109
|
+
// A backtick fence's info string may not itself contain a backtick; a
|
|
110
|
+
// tilde fence's may. Same rule the engines apply.
|
|
111
|
+
if (open && !(open[1][0] === '`' && open[2].includes('`'))) {
|
|
112
|
+
fence = { char: open[1][0], length: open[1].length, start: lineStart };
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
// Inline code spans, matched run-for-run within the line.
|
|
116
|
+
const runs = /`+/g;
|
|
117
|
+
let openRun = null;
|
|
118
|
+
for (let m = runs.exec(line); m; m = runs.exec(line)) {
|
|
119
|
+
if (!openRun) {
|
|
120
|
+
openRun = { index: m.index, length: m[0].length };
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (m[0].length === openRun.length) {
|
|
124
|
+
spans.push({ start: lineStart + openRun.index, end: lineStart + m.index + m[0].length });
|
|
125
|
+
openRun = null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// An unterminated fence shields everything to the end of the document.
|
|
130
|
+
if (fence)
|
|
131
|
+
spans.push({ start: fence.start, end: source.length });
|
|
132
|
+
return spans;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Locate the well-formed directives in `source`, in source order, skipping any
|
|
136
|
+
* that begin inside a verbatim region (I9).
|
|
137
|
+
*
|
|
138
|
+
* `onInvalidOption` fires for a token of directive shape whose options do not
|
|
139
|
+
* parse, so the caller can raise `include-unknown-option` on it.
|
|
140
|
+
*/
|
|
141
|
+
export function findDirectives(source, onInvalidOption) {
|
|
142
|
+
const shielded = verbatimSpans(source);
|
|
143
|
+
const inVerbatim = (index) => shielded.some((span) => index >= span.start && index < span.end);
|
|
144
|
+
const re = new RegExp(DIRECTIVE_SCAN_RE.source, 'g');
|
|
145
|
+
const found = [];
|
|
146
|
+
for (let m = re.exec(source); m; m = re.exec(source)) {
|
|
147
|
+
const start = m.index;
|
|
148
|
+
const end = start + m[0].length;
|
|
149
|
+
if (inVerbatim(start))
|
|
150
|
+
continue;
|
|
151
|
+
const parsed = parseDirective(m[0], (part) => onInvalidOption?.(part, start, end));
|
|
152
|
+
if (!parsed)
|
|
153
|
+
continue;
|
|
154
|
+
found.push({ ...parsed, start, end });
|
|
155
|
+
}
|
|
156
|
+
return found;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=include-directive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"include-directive.js","sourceRoot":"","sources":["../src/include-directive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { findDirectives, parseDirective, verbatimSpans } from './include-directive.js';
|
|
4
|
+
// Expectations here follow the cross-engine include-conformance vectors named
|
|
5
|
+
// in each test (markup-carve/carve tests/include-conformance/vectors). Those
|
|
6
|
+
// vectors currently live only on the unmerged spec branch for
|
|
7
|
+
// markup-carve/carve#291, so they are cited rather than vendored.
|
|
8
|
+
test('a bare directive is recognized with its path', () => {
|
|
9
|
+
const [d] = findDirectives('{{ child.crv }}');
|
|
10
|
+
assert.ok(d);
|
|
11
|
+
assert.equal(d.path, 'child.crv');
|
|
12
|
+
assert.equal(d.start, 0);
|
|
13
|
+
assert.equal(d.end, 15);
|
|
14
|
+
});
|
|
15
|
+
test('a quoted path carrying a space is recognized (i01-quoted-path-roundtrip)', () => {
|
|
16
|
+
const [d] = findDirectives('{{ "my chapter.crv" }}');
|
|
17
|
+
assert.ok(d);
|
|
18
|
+
assert.equal(d.path, 'my chapter.crv');
|
|
19
|
+
});
|
|
20
|
+
test('a section selector is separated from the path', () => {
|
|
21
|
+
const [d] = findDirectives('{{ c.crv #Intro }}');
|
|
22
|
+
assert.ok(d);
|
|
23
|
+
assert.equal(d.path, 'c.crv');
|
|
24
|
+
assert.equal(d.section, 'Intro');
|
|
25
|
+
});
|
|
26
|
+
test('empty and pathless brace pairs stay literal (i01-*-literal)', () => {
|
|
27
|
+
// Each of these is ordinary text under the PART 6 production: the path token
|
|
28
|
+
// is mandatory and may not begin with `#` or `@`.
|
|
29
|
+
for (const source of ['{{}}', '{{ }}', '{{ #sec }}', '{{ @shift:1 }}']) {
|
|
30
|
+
assert.deepEqual(findDirectives(source), [], source);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
test('an unknown option makes the directive literal and reports the option (i01-unknown-option-literal)', () => {
|
|
34
|
+
const seen = [];
|
|
35
|
+
const found = findDirectives('{{ child @nope:1 }}', (part) => seen.push(part));
|
|
36
|
+
assert.deepEqual(found, []);
|
|
37
|
+
assert.deepEqual(seen, ['@nope:1']);
|
|
38
|
+
});
|
|
39
|
+
test('a malformed shift value is an unknown option (i01-shift-malformed-value)', () => {
|
|
40
|
+
const seen = [];
|
|
41
|
+
assert.deepEqual(findDirectives('{{ child @shift:x }}', (part) => seen.push(part)), []);
|
|
42
|
+
assert.deepEqual(seen, ['@shift:x']);
|
|
43
|
+
});
|
|
44
|
+
test('several directives in one run are all found (i09a-multi-directive-one-run)', () => {
|
|
45
|
+
const found = findDirectives('start {{ a.crv }} mid {{ b.crv }} end');
|
|
46
|
+
assert.deepEqual(found.map((d) => d.path), ['a.crv', 'b.crv']);
|
|
47
|
+
});
|
|
48
|
+
test('adjacent directives are both found', () => {
|
|
49
|
+
const found = findDirectives('{{ a.crv }}{{ d.crv }}');
|
|
50
|
+
assert.deepEqual(found.map((d) => d.path), ['a.crv', 'd.crv']);
|
|
51
|
+
});
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Verbatim shielding (I9)
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
test('a directive inside a fence or a code span is shielded (i09-verbatim-fence-and-code-span)', () => {
|
|
56
|
+
const source = '```txt\n{{ child }}\n```\n\nUse `{{ child }}`.';
|
|
57
|
+
assert.deepEqual(findDirectives(source), []);
|
|
58
|
+
});
|
|
59
|
+
test('a raw block shields its directive (i09-verbatim-raw-block)', () => {
|
|
60
|
+
assert.deepEqual(findDirectives('```=html\n{{ child }}\n```'), []);
|
|
61
|
+
});
|
|
62
|
+
test('a fence shields only itself; a directive after it still resolves (i09-fence-info-shields-plain-expands)', () => {
|
|
63
|
+
const source = '```js\n{{ child }}\n```\n\n{{ child }}';
|
|
64
|
+
const found = findDirectives(source);
|
|
65
|
+
assert.equal(found.length, 1);
|
|
66
|
+
// The surviving one is the directive AFTER the closing fence.
|
|
67
|
+
assert.equal(found[0].start, source.lastIndexOf('{{ child }}'));
|
|
68
|
+
});
|
|
69
|
+
test('a tilde fence shields too', () => {
|
|
70
|
+
assert.deepEqual(findDirectives('~~~\n{{ child }}\n~~~'), []);
|
|
71
|
+
});
|
|
72
|
+
test('an unterminated fence shields to the end of the document', () => {
|
|
73
|
+
assert.deepEqual(findDirectives('```\n{{ child }}\n'), []);
|
|
74
|
+
});
|
|
75
|
+
test('verbatimSpans covers the whole fence including its markers', () => {
|
|
76
|
+
const source = '```\nx\n```\n';
|
|
77
|
+
assert.deepEqual(verbatimSpans(source), [{ start: 0, end: 9 }]);
|
|
78
|
+
});
|
|
79
|
+
test('parseDirective rejects a token that is not a directive', () => {
|
|
80
|
+
assert.equal(parseDirective('{{ oops'), null);
|
|
81
|
+
assert.equal(parseDirective('{{ }}'), null);
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=include-directive.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"include-directive.test.js","sourceRoot":"","sources":["../src/include-directive.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a resolver refused a target. Reported on {@link IncludeWarning.detail},
|
|
3
|
+
* never folded into the diagnostic message: §19 I7 requires a
|
|
4
|
+
* processor-generated message naming the failure class, and the
|
|
5
|
+
* include-conformance goldens (`i10-fs-*`) spell every containment denial as
|
|
6
|
+
* the single rule `include-unresolved` so that a denial cannot be used to probe
|
|
7
|
+
* host layout. The class is kept available for tooling and tests.
|
|
8
|
+
*/
|
|
9
|
+
export type IncludeDenial = 'remote-not-allowed' | 'absolute-denied' | 'outside-root' | 'not-found' | 'not-a-file';
|
|
10
|
+
export interface IncludeContext {
|
|
11
|
+
/** Identity of the including document, supplied by the host when known. */
|
|
12
|
+
sourcePath?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Include chain, root first: the canonical id of each ancestor. Used for
|
|
15
|
+
* relative resolution (a nested include resolves against its actual parent
|
|
16
|
+
* directory) and for the cycle guard.
|
|
17
|
+
*/
|
|
18
|
+
stack: string[];
|
|
19
|
+
/** Zero-based include depth of the directive being resolved. */
|
|
20
|
+
depth: number;
|
|
21
|
+
}
|
|
22
|
+
export type IncludeResolved = {
|
|
23
|
+
ok: true;
|
|
24
|
+
id: string;
|
|
25
|
+
source: string;
|
|
26
|
+
bytes: number;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
id: string;
|
|
30
|
+
denial: IncludeDenial;
|
|
31
|
+
};
|
|
32
|
+
export type IncludeResolver = (includePath: string, ctx: IncludeContext) => IncludeResolved;
|
|
33
|
+
export interface FileSystemResolverOptions {
|
|
34
|
+
/** Allow absolute include paths, still subject to root containment. Default false. */
|
|
35
|
+
allowAbsolute?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Hosts a remote include may name. Empty (the default) means no remote
|
|
38
|
+
* include is permitted at all.
|
|
39
|
+
*
|
|
40
|
+
* Note that this server never fetches over the network under any setting:
|
|
41
|
+
* an allowlisted host is still refused, because there is no fetcher. The
|
|
42
|
+
* option exists so that "allowlist explicitly configured" is a real,
|
|
43
|
+
* inspectable condition rather than an implicit one, and so a host that
|
|
44
|
+
* later gains a fetcher has the gate already in place.
|
|
45
|
+
*/
|
|
46
|
+
allowedRemoteHosts?: string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Filesystem resolver with canonical root containment, for a workspace the
|
|
50
|
+
* client reports as trusted.
|
|
51
|
+
*
|
|
52
|
+
* Canonicalize-then-contain: the candidate is resolved to its real path
|
|
53
|
+
* (symlinks followed) and only then compared against the real root.
|
|
54
|
+
*
|
|
55
|
+
* Deliberately NOT a lexical ban on `..`, which is both too strict and too
|
|
56
|
+
* weak. Too strict: `../shared/glossary.crv` from `chapters/ch1.crv` is a
|
|
57
|
+
* normal book layout whose canonical target is inside the root, and the
|
|
58
|
+
* conformance vector `i10-fs-dotdot-inside-root-allowed` requires it to
|
|
59
|
+
* resolve. Too weak: a symlink inside the root pointing out of it, or an
|
|
60
|
+
* absolute path, escapes without containing `..` at all
|
|
61
|
+
* (`i10-fs-symlink-dir-escape-denied`). Canonical containment subsumes both.
|
|
62
|
+
*/
|
|
63
|
+
export declare function fileSystemResolver(includeRoot: string, opts?: FileSystemResolverOptions): IncludeResolver;
|