@mandolin/jsdoc-plugin-hia-sys 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +222 -0
- package/RELEASE_CHECKLIST.md +41 -0
- package/THIRD_PARTY_NOTICES.md +23 -0
- package/examples/basic/README.md +22 -0
- package/examples/basic/i18n/docs.hia-i18n.json +12 -0
- package/examples/basic/jsdoc.conf.json +42 -0
- package/examples/basic/src/greet.js +26 -0
- package/examples/basic/src/shared.js +20 -0
- package/package.json +61 -0
- package/src/config/defaults.cjs +114 -0
- package/src/index.cjs +37 -0
- package/src/micro-plugins/code-fragment.cjs +374 -0
- package/src/micro-plugins/diagnostics.cjs +18 -0
- package/src/micro-plugins/doc-i18n.cjs +796 -0
- package/src/micro-plugins/doclet-normalizer.cjs +23 -0
- package/src/micro-plugins/hia-ir-exporter.cjs +68 -0
- package/src/micro-plugins/index.cjs +23 -0
- package/src/micro-plugins/source-link.cjs +19 -0
- package/src/micro-plugins/source-preview.cjs +18 -0
- package/src/runtime/create-plugin-system.cjs +120 -0
- package/src/runtime/diagnostics.cjs +45 -0
- package/src/runtime/i18n.cjs +221 -0
- package/src/runtime/metadata.cjs +52 -0
- package/src/runtime/output-contract.cjs +416 -0
- package/src/runtime/source.cjs +547 -0
- package/src/version.cjs +5 -0
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
const EXTENSION_LANGUAGE = new Map([
|
|
6
|
+
[".cjs", "javascript"],
|
|
7
|
+
[".mjs", "javascript"],
|
|
8
|
+
[".js", "javascript"],
|
|
9
|
+
[".jsx", "javascript"],
|
|
10
|
+
[".ts", "typescript"],
|
|
11
|
+
[".tsx", "typescript"],
|
|
12
|
+
[".css", "css"],
|
|
13
|
+
[".scss", "scss"],
|
|
14
|
+
[".sass", "sass"],
|
|
15
|
+
[".html", "html"],
|
|
16
|
+
[".htm", "html"],
|
|
17
|
+
[".pug", "pug"],
|
|
18
|
+
[".md", "markdown"]
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
function toPosixPath(filePath) {
|
|
22
|
+
return filePath.split(path.sep).join("/");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function inferLanguage(filePath) {
|
|
26
|
+
return EXTENSION_LANGUAGE.get(path.extname(filePath).toLowerCase()) || "text";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normalizeOpenMode(openMode) {
|
|
30
|
+
const value = String(openMode || "").trim();
|
|
31
|
+
|
|
32
|
+
if (value === "new-tab" || value === "newTab" || value === "external") {
|
|
33
|
+
return "new-tab";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (value === "same-tab" || value === "sameTab" || value === "currentPage" || value === "current-page") {
|
|
37
|
+
return "same-tab";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return value || "same-tab";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getRelativePath(filePath, config) {
|
|
44
|
+
const basePath = config.source.basePath || process.cwd();
|
|
45
|
+
return toPosixPath(path.relative(basePath, filePath));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeAbsolutePath(filePath) {
|
|
49
|
+
return path.resolve(String(filePath || ""));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getDocletSourceCandidates(doclet) {
|
|
53
|
+
const meta = doclet && doclet.meta ? doclet.meta : {};
|
|
54
|
+
const candidates = [];
|
|
55
|
+
|
|
56
|
+
if (meta.filename && path.isAbsolute(meta.filename)) {
|
|
57
|
+
candidates.push(meta.filename);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (meta.filename && meta.path) {
|
|
61
|
+
candidates.push(path.resolve(meta.path, meta.filename));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (meta.filename) {
|
|
65
|
+
candidates.push(path.resolve(meta.filename));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return candidates;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function resolveDocletSourceFile(doclet, state) {
|
|
72
|
+
const sourceFiles = state && state.sourceFiles instanceof Map
|
|
73
|
+
? state.sourceFiles
|
|
74
|
+
: new Map();
|
|
75
|
+
const candidates = getDocletSourceCandidates(doclet).map(normalizeAbsolutePath);
|
|
76
|
+
|
|
77
|
+
for (const [key, record] of sourceFiles.entries()) {
|
|
78
|
+
const filePath = record && record.filePath ? record.filePath : key;
|
|
79
|
+
const normalized = normalizeAbsolutePath(filePath);
|
|
80
|
+
|
|
81
|
+
if (candidates.includes(normalized)) {
|
|
82
|
+
return {
|
|
83
|
+
filePath,
|
|
84
|
+
source: record && typeof record.source === "string" ? record.source : ""
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (candidates.length === 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const target = toPosixPath(candidates[0]).toLowerCase();
|
|
94
|
+
|
|
95
|
+
for (const [key, record] of sourceFiles.entries()) {
|
|
96
|
+
const filePath = record && record.filePath ? record.filePath : key;
|
|
97
|
+
const normalized = toPosixPath(normalizeAbsolutePath(filePath)).toLowerCase();
|
|
98
|
+
|
|
99
|
+
if (target.endsWith(normalized) || normalized.endsWith(target)) {
|
|
100
|
+
return {
|
|
101
|
+
filePath,
|
|
102
|
+
source: record && typeof record.source === "string" ? record.source : ""
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function trimOneOuterNewline(content) {
|
|
111
|
+
return content.replace(/^\r?\n/, "").replace(/\r?\n[ \t]*$/, "");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getLineColumn(source, index) {
|
|
115
|
+
const text = source.slice(0, Math.max(0, index));
|
|
116
|
+
const lines = text.split(/\r\n|\n|\r/);
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
line: lines.length,
|
|
120
|
+
column: lines[lines.length - 1].length + 1
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function buildSourceLink(fragment, config) {
|
|
125
|
+
const linkConfig = config.source.link;
|
|
126
|
+
const openMode = normalizeOpenMode(linkConfig.openMode);
|
|
127
|
+
|
|
128
|
+
if (!linkConfig.enabled || !linkConfig.rootUrl) {
|
|
129
|
+
return {
|
|
130
|
+
enabled: Boolean(linkConfig.enabled),
|
|
131
|
+
fileUrl: "",
|
|
132
|
+
lineUrl: "",
|
|
133
|
+
openMode
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const rootUrl = linkConfig.rootUrl.replace(/\/+$/, "");
|
|
138
|
+
const fileUrl = `${rootUrl}/${fragment.relativePath}`;
|
|
139
|
+
const startLine = fragment.range.start.line;
|
|
140
|
+
const endLine = fragment.range.end.line;
|
|
141
|
+
const lineHash = endLine > startLine ? `#L${startLine}-L${endLine}` : `#L${startLine}`;
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
enabled: true,
|
|
145
|
+
fileUrl,
|
|
146
|
+
lineUrl: `${fileUrl}${lineHash}`,
|
|
147
|
+
openMode
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function buildSourcePreview(fragment, config) {
|
|
152
|
+
return {
|
|
153
|
+
enabled: Boolean(config.source.preview.enabled),
|
|
154
|
+
defaultExpanded: Boolean(config.source.preview.defaultExpanded),
|
|
155
|
+
language: fragment.language,
|
|
156
|
+
content: fragment.content,
|
|
157
|
+
range: fragment.range
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function getDocletPosition(doclet) {
|
|
162
|
+
const meta = doclet && doclet.meta ? doclet.meta : {};
|
|
163
|
+
const line = Number(meta.lineno || meta.line || 1);
|
|
164
|
+
const column = Number(meta.columnno || meta.column || 1);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
line: Number.isFinite(line) && line > 0 ? line : 1,
|
|
168
|
+
column: Number.isFinite(column) && column > 0 ? column : 1
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function createUnresolvedPrimaryBlock(doclet, reason) {
|
|
173
|
+
return {
|
|
174
|
+
kind: "primary-block",
|
|
175
|
+
id: `doclet:${doclet && doclet.kind ? doclet.kind : "unknown"}:${doclet && (doclet.longname || doclet.name) ? doclet.longname || doclet.name : "anonymous"}`,
|
|
176
|
+
relativePath: "",
|
|
177
|
+
language: "text",
|
|
178
|
+
range: null,
|
|
179
|
+
content: "",
|
|
180
|
+
rangeSource: "unresolved",
|
|
181
|
+
confidence: "none",
|
|
182
|
+
link: {
|
|
183
|
+
enabled: false,
|
|
184
|
+
fileUrl: "",
|
|
185
|
+
lineUrl: "",
|
|
186
|
+
openMode: ""
|
|
187
|
+
},
|
|
188
|
+
preview: {
|
|
189
|
+
enabled: false,
|
|
190
|
+
defaultExpanded: false,
|
|
191
|
+
language: "text",
|
|
192
|
+
content: "",
|
|
193
|
+
range: null
|
|
194
|
+
},
|
|
195
|
+
diagnostics: reason
|
|
196
|
+
? [
|
|
197
|
+
{
|
|
198
|
+
code: "HIA_SOURCE_PRIMARY_BLOCK_UNRESOLVED",
|
|
199
|
+
severity: "info",
|
|
200
|
+
message: reason
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
: []
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function splitLines(source) {
|
|
208
|
+
return String(source || "").split(/\r\n|\n|\r/);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function findFirstCodeLine(lines, startIndex, maxScanLines) {
|
|
212
|
+
const limit = Math.min(lines.length - 1, startIndex + maxScanLines);
|
|
213
|
+
|
|
214
|
+
for (let index = startIndex; index <= limit; index += 1) {
|
|
215
|
+
const line = lines[index] || "";
|
|
216
|
+
const trimmed = line.trim();
|
|
217
|
+
|
|
218
|
+
if (!trimmed) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (
|
|
223
|
+
trimmed.startsWith("/**") ||
|
|
224
|
+
trimmed.startsWith("/*") ||
|
|
225
|
+
trimmed.startsWith("*") ||
|
|
226
|
+
trimmed.startsWith("*/")
|
|
227
|
+
) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return index;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return startIndex;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function stripStringsAndLineComments(line) {
|
|
238
|
+
return String(line || "")
|
|
239
|
+
.replace(/(["'`])(?:\\.|(?!\1).)*\1/g, "")
|
|
240
|
+
.replace(/\/\/.*$/, "");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function countChar(value, char) {
|
|
244
|
+
return Array.from(value).filter((item) => item === char).length;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function escapeRegExp(value) {
|
|
248
|
+
return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function getDocletCandidateNames(doclet) {
|
|
252
|
+
const names = new Set();
|
|
253
|
+
const values = [
|
|
254
|
+
doclet && doclet.name,
|
|
255
|
+
doclet && doclet.longname,
|
|
256
|
+
doclet && doclet.alias
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
for (const value of values) {
|
|
260
|
+
const text = String(value || "").trim();
|
|
261
|
+
|
|
262
|
+
if (!text) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const parts = text.split(/[.#~:]/).filter(Boolean);
|
|
267
|
+
const tail = parts[parts.length - 1] || text;
|
|
268
|
+
|
|
269
|
+
for (const candidate of [text, tail]) {
|
|
270
|
+
if (/^[A-Za-z_$][\w$]*$/.test(candidate)) {
|
|
271
|
+
names.add(candidate);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return Array.from(names);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function matchesJsDeclarationLine(line, doclet) {
|
|
280
|
+
const kind = doclet && doclet.kind ? String(doclet.kind) : "";
|
|
281
|
+
|
|
282
|
+
if (kind === "module" || kind === "typedef") {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const stripped = stripStringsAndLineComments(line);
|
|
287
|
+
const names = getDocletCandidateNames(doclet);
|
|
288
|
+
|
|
289
|
+
if (names.length === 0) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
for (const name of names) {
|
|
294
|
+
const escapedName = escapeRegExp(name);
|
|
295
|
+
const exportPrefix = "(?:export\\s+)?(?:default\\s+)?";
|
|
296
|
+
const patterns = [
|
|
297
|
+
new RegExp(`^\\s*${exportPrefix}(?:async\\s+)?function\\s*\\*?\\s+${escapedName}\\b`),
|
|
298
|
+
new RegExp(`^\\s*${exportPrefix}class\\s+${escapedName}\\b`),
|
|
299
|
+
new RegExp(`^\\s*${exportPrefix}(?:const|let|var)\\s+${escapedName}\\b`),
|
|
300
|
+
new RegExp(`^\\s*(?:exports|module\\.exports)\\.${escapedName}\\s*=`),
|
|
301
|
+
new RegExp(`^\\s*${escapedName}\\s*[:=]\\s*(?:async\\s*)?(?:function\\b|class\\b|\\([^)]*\\)\\s*=>|[A-Za-z_$][\\w$]*\\s*=>)`),
|
|
302
|
+
new RegExp(`^\\s*${escapedName}\\s*\\([^)]*\\)\\s*\\{`)
|
|
303
|
+
];
|
|
304
|
+
|
|
305
|
+
if (patterns.some((pattern) => pattern.test(stripped))) {
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function findJsDeclarationLine(lines, startIndex, maxScanLines, doclet) {
|
|
314
|
+
const limit = Math.min(lines.length - 1, startIndex + maxScanLines);
|
|
315
|
+
|
|
316
|
+
for (let index = startIndex; index <= limit; index += 1) {
|
|
317
|
+
if (matchesJsDeclarationLine(lines[index] || "", doclet)) {
|
|
318
|
+
return index;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return -1;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function getRangeContent(lines, startIndex, endIndex) {
|
|
326
|
+
return {
|
|
327
|
+
range: {
|
|
328
|
+
start: {
|
|
329
|
+
line: startIndex + 1,
|
|
330
|
+
column: 1
|
|
331
|
+
},
|
|
332
|
+
end: {
|
|
333
|
+
line: endIndex + 1,
|
|
334
|
+
column: (lines[endIndex] || "").length + 1
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
content: lines.slice(startIndex, endIndex + 1).join("\n")
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function findBalancedJsBlockEnd(lines, startIndex, maxLines) {
|
|
342
|
+
const limit = Math.min(lines.length - 1, startIndex + maxLines - 1);
|
|
343
|
+
let balance = 0;
|
|
344
|
+
let sawBrace = false;
|
|
345
|
+
|
|
346
|
+
for (let index = startIndex; index <= limit; index += 1) {
|
|
347
|
+
const stripped = stripStringsAndLineComments(lines[index]);
|
|
348
|
+
const opens = countChar(stripped, "{");
|
|
349
|
+
const closes = countChar(stripped, "}");
|
|
350
|
+
|
|
351
|
+
if (opens > 0 || closes > 0) {
|
|
352
|
+
sawBrace = true;
|
|
353
|
+
balance += opens - closes;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (sawBrace && balance <= 0) {
|
|
357
|
+
return index;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return -1;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function findJsStatementEnd(lines, startIndex, maxLines) {
|
|
365
|
+
const limit = Math.min(lines.length - 1, startIndex + maxLines - 1);
|
|
366
|
+
let parenBalance = 0;
|
|
367
|
+
let bracketBalance = 0;
|
|
368
|
+
|
|
369
|
+
for (let index = startIndex; index <= limit; index += 1) {
|
|
370
|
+
const stripped = stripStringsAndLineComments(lines[index]);
|
|
371
|
+
|
|
372
|
+
for (const char of stripped) {
|
|
373
|
+
if (char === "(") {
|
|
374
|
+
parenBalance += 1;
|
|
375
|
+
} else if (char === ")") {
|
|
376
|
+
parenBalance -= 1;
|
|
377
|
+
} else if (char === "[") {
|
|
378
|
+
bracketBalance += 1;
|
|
379
|
+
} else if (char === "]") {
|
|
380
|
+
bracketBalance -= 1;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (parenBalance <= 0 && bracketBalance <= 0 && /;\s*$/.test(stripped)) {
|
|
385
|
+
return index;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (index > startIndex && parenBalance <= 0 && bracketBalance <= 0 && /^\s*$/.test(lines[index])) {
|
|
389
|
+
return index - 1;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return -1;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function inferJsParserRange(lines, startIndex, maxLines, maxScanLines, doclet) {
|
|
397
|
+
const declarationIndex = findJsDeclarationLine(lines, startIndex, maxScanLines, doclet);
|
|
398
|
+
|
|
399
|
+
if (declarationIndex < 0) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const blockEndIndex = findBalancedJsBlockEnd(lines, declarationIndex, maxLines);
|
|
404
|
+
const endIndex = blockEndIndex >= 0
|
|
405
|
+
? blockEndIndex
|
|
406
|
+
: findJsStatementEnd(lines, declarationIndex, maxLines);
|
|
407
|
+
|
|
408
|
+
if (endIndex < declarationIndex) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return {
|
|
413
|
+
...getRangeContent(lines, declarationIndex, endIndex),
|
|
414
|
+
rangeSource: "parser-js",
|
|
415
|
+
confidence: blockEndIndex >= 0 ? "high" : "medium"
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function inferHeuristicRange(lines, startIndex, maxLines, maxScanLines) {
|
|
420
|
+
const codeStartIndex = findFirstCodeLine(lines, startIndex, maxScanLines);
|
|
421
|
+
const limit = Math.min(lines.length - 1, codeStartIndex + maxLines - 1);
|
|
422
|
+
let balance = 0;
|
|
423
|
+
let sawBrace = false;
|
|
424
|
+
let endIndex = codeStartIndex;
|
|
425
|
+
|
|
426
|
+
for (let index = codeStartIndex; index <= limit; index += 1) {
|
|
427
|
+
const stripped = stripStringsAndLineComments(lines[index]);
|
|
428
|
+
const opens = countChar(stripped, "{");
|
|
429
|
+
const closes = countChar(stripped, "}");
|
|
430
|
+
|
|
431
|
+
if (opens > 0 || closes > 0) {
|
|
432
|
+
sawBrace = true;
|
|
433
|
+
balance += opens - closes;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
endIndex = index;
|
|
437
|
+
|
|
438
|
+
if (sawBrace && balance <= 0 && index > codeStartIndex) {
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (!sawBrace && index > codeStartIndex && /^\s*$/.test(lines[index])) {
|
|
443
|
+
endIndex = index - 1;
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
...getRangeContent(lines, codeStartIndex, endIndex),
|
|
450
|
+
rangeSource: "heuristic",
|
|
451
|
+
confidence: sawBrace && balance <= 0 ? "medium" : "low"
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function inferPrimaryRange(source, position, config, doclet) {
|
|
456
|
+
const lines = splitLines(source);
|
|
457
|
+
|
|
458
|
+
if (lines.length === 0) {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const previewConfig = config.source.preview || {};
|
|
463
|
+
const maxLines = Math.max(1, Number(previewConfig.maxLines || 80));
|
|
464
|
+
const maxScanLines = Math.max(8, Number(previewConfig.maxScanLines || 40));
|
|
465
|
+
const rangeStrategy = previewConfig.rangeStrategy || "parser-js";
|
|
466
|
+
const startIndex = Math.min(Math.max(position.line - 1, 0), lines.length - 1);
|
|
467
|
+
|
|
468
|
+
if (rangeStrategy !== "heuristic") {
|
|
469
|
+
const parserRange = inferJsParserRange(lines, startIndex, maxLines, maxScanLines, doclet);
|
|
470
|
+
|
|
471
|
+
if (parserRange) {
|
|
472
|
+
return parserRange;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return inferHeuristicRange(lines, startIndex, maxLines, maxScanLines);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function buildDefinedIn(doclet, context) {
|
|
480
|
+
const record = resolveDocletSourceFile(doclet, context.state);
|
|
481
|
+
|
|
482
|
+
if (!record) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const position = getDocletPosition(doclet);
|
|
487
|
+
const definedIn = {
|
|
488
|
+
kind: "defined-in",
|
|
489
|
+
relativePath: getRelativePath(record.filePath, context.config),
|
|
490
|
+
language: inferLanguage(record.filePath),
|
|
491
|
+
position,
|
|
492
|
+
range: {
|
|
493
|
+
start: position,
|
|
494
|
+
end: position
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
definedIn.link = buildSourceLink(definedIn, context.config);
|
|
499
|
+
|
|
500
|
+
return definedIn;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function buildPrimaryBlock(doclet, context) {
|
|
504
|
+
const record = resolveDocletSourceFile(doclet, context.state);
|
|
505
|
+
|
|
506
|
+
if (!record || !record.source) {
|
|
507
|
+
return createUnresolvedPrimaryBlock(doclet, "Doclet source file was not captured before parsing.");
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const position = getDocletPosition(doclet);
|
|
511
|
+
const inferred = inferPrimaryRange(record.source, position, context.config, doclet);
|
|
512
|
+
|
|
513
|
+
if (!inferred || !inferred.content) {
|
|
514
|
+
return createUnresolvedPrimaryBlock(doclet, "Doclet source range could not be inferred.");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const block = {
|
|
518
|
+
kind: "primary-block",
|
|
519
|
+
id: `doclet:${doclet.kind || "unknown"}:${doclet.longname || doclet.name || "anonymous"}`,
|
|
520
|
+
relativePath: getRelativePath(record.filePath, context.config),
|
|
521
|
+
language: inferLanguage(record.filePath),
|
|
522
|
+
range: inferred.range,
|
|
523
|
+
content: inferred.content,
|
|
524
|
+
rangeSource: inferred.rangeSource || "heuristic",
|
|
525
|
+
confidence: inferred.confidence,
|
|
526
|
+
diagnostics: []
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
block.link = buildSourceLink(block, context.config);
|
|
530
|
+
block.preview = buildSourcePreview(block, context.config);
|
|
531
|
+
|
|
532
|
+
return block;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
module.exports = {
|
|
536
|
+
buildDefinedIn,
|
|
537
|
+
buildPrimaryBlock,
|
|
538
|
+
buildSourceLink,
|
|
539
|
+
buildSourcePreview,
|
|
540
|
+
getLineColumn,
|
|
541
|
+
getRelativePath,
|
|
542
|
+
inferLanguage,
|
|
543
|
+
normalizeOpenMode,
|
|
544
|
+
resolveDocletSourceFile,
|
|
545
|
+
toPosixPath,
|
|
546
|
+
trimOneOuterNewline
|
|
547
|
+
};
|