mdast-control 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/README.md +359 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +534 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lsp/requests.d.ts +215 -0
- package/dist/lsp/requests.js +613 -0
- package/dist/lsp/requests.js.map +1 -0
- package/dist/lsp/server.d.ts +1 -0
- package/dist/lsp/server.js +110 -0
- package/dist/lsp/server.js.map +1 -0
- package/dist/lsp-methods.d.ts +25 -0
- package/dist/lsp-methods.js +26 -0
- package/dist/lsp-methods.js.map +1 -0
- package/dist/markdown.d.ts +17 -0
- package/dist/markdown.js +140 -0
- package/dist/markdown.js.map +1 -0
- package/dist/plugins.d.ts +144 -0
- package/dist/plugins.js +833 -0
- package/dist/plugins.js.map +1 -0
- package/dist/query.d.ts +8 -0
- package/dist/query.js +518 -0
- package/dist/query.js.map +1 -0
- package/dist/table-interchange.d.ts +22 -0
- package/dist/table-interchange.js +191 -0
- package/dist/table-interchange.js.map +1 -0
- package/dist/table-model.d.ts +15 -0
- package/dist/table-model.js +105 -0
- package/dist/table-model.js.map +1 -0
- package/dist/table-structural.d.ts +9 -0
- package/dist/table-structural.js +190 -0
- package/dist/table-structural.js.map +1 -0
- package/dist/table.d.ts +49 -0
- package/dist/table.js +131 -0
- package/dist/table.js.map +1 -0
- package/dist/transport.d.ts +6 -0
- package/dist/transport.js +7 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
- package/scripts/verify-package.mjs +252 -0
package/dist/table.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { parseMarkdown, stringifyAst } from './markdown.js';
|
|
2
|
+
import { applyInsert, applyReplace, queryAst } from './query.js';
|
|
3
|
+
import { tableNodeToModel } from './table-model.js';
|
|
4
|
+
import { csvToTableAst, tableModelToDelimited, tsvToTableAst, } from './table-interchange.js';
|
|
5
|
+
import { addTableColumnNode, deleteTableColumnNode, deleteTableRowNode, insertTableRowNode, sortTableNode, updateTableCellNode, } from './table-structural.js';
|
|
6
|
+
export { flattenTableCellText, getTableCell, resolveSingleTableColumnIndex, resolveTableColumnIndexes, tableNodeToModel, } from './table-model.js';
|
|
7
|
+
export { csvToTableAst, delimitedToTableModel, delimiterForFormat, parseDelimited, tableModelToAst, tableModelToDelimited, tsvToTableAst, } from './table-interchange.js';
|
|
8
|
+
export { addTableColumnNode, deleteTableColumnNode, deleteTableRowNode, insertTableRowNode, sortTableNode, updateTableCellNode, } from './table-structural.js';
|
|
9
|
+
export function extractTableModels(markdown, query) {
|
|
10
|
+
const matches = queryAst(parseMarkdown(markdown), query);
|
|
11
|
+
return matches.map((match, index) => {
|
|
12
|
+
if (match.node.type !== 'table') {
|
|
13
|
+
throw new Error(`Table extraction query must match only table nodes; match ${String(index)} has type "${match.node.type}"`);
|
|
14
|
+
}
|
|
15
|
+
return tableNodeToModel(match.node);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export function extractTableModel(markdown, query) {
|
|
19
|
+
const models = extractTableModels(markdown, query);
|
|
20
|
+
if (models.length !== 1) {
|
|
21
|
+
throw new Error(`Table extraction requires exactly one table match, received ${String(models.length)}`);
|
|
22
|
+
}
|
|
23
|
+
return models[0];
|
|
24
|
+
}
|
|
25
|
+
export function tableToCsv(markdown, query, options = {}) {
|
|
26
|
+
return tableModelToDelimited(extractTableModel(markdown, query), ',', options);
|
|
27
|
+
}
|
|
28
|
+
export function tableToTsv(markdown, query, options = {}) {
|
|
29
|
+
return tableModelToDelimited(extractTableModel(markdown, query), '\t', options);
|
|
30
|
+
}
|
|
31
|
+
export function tableToDelimited(markdown, query, format, options = {}) {
|
|
32
|
+
return format === 'csv'
|
|
33
|
+
? tableToCsv(markdown, query, options)
|
|
34
|
+
: tableToTsv(markdown, query, options);
|
|
35
|
+
}
|
|
36
|
+
export function csvToMarkdownTable(input, options = {}) {
|
|
37
|
+
return stringifyTable(csvToTableAst(input, options));
|
|
38
|
+
}
|
|
39
|
+
export function tsvToMarkdownTable(input, options = {}) {
|
|
40
|
+
return stringifyTable(tsvToTableAst(input, options));
|
|
41
|
+
}
|
|
42
|
+
export function delimitedToMarkdownTable(input, format, options = {}) {
|
|
43
|
+
return format === 'csv'
|
|
44
|
+
? csvToMarkdownTable(input, options)
|
|
45
|
+
: tsvToMarkdownTable(input, options);
|
|
46
|
+
}
|
|
47
|
+
export function replaceTableWithCsv(markdown, query, csv, options = {}) {
|
|
48
|
+
return replaceTableWithAst(markdown, query, csvToTableAst(csv, options));
|
|
49
|
+
}
|
|
50
|
+
export function replaceTableWithTsv(markdown, query, tsv, options = {}) {
|
|
51
|
+
return replaceTableWithAst(markdown, query, tsvToTableAst(tsv, options));
|
|
52
|
+
}
|
|
53
|
+
export function insertCsvAsTable(markdown, query, position, csv, options = {}) {
|
|
54
|
+
return insertTableAst(markdown, query, position, csvToTableAst(csv, options));
|
|
55
|
+
}
|
|
56
|
+
export function insertTsvAsTable(markdown, query, position, tsv, options = {}) {
|
|
57
|
+
return insertTableAst(markdown, query, position, tsvToTableAst(tsv, options));
|
|
58
|
+
}
|
|
59
|
+
export function replaceTableWithDelimited(markdown, query, format, input, options = {}) {
|
|
60
|
+
return format === 'csv'
|
|
61
|
+
? replaceTableWithCsv(markdown, query, input, options)
|
|
62
|
+
: replaceTableWithTsv(markdown, query, input, options);
|
|
63
|
+
}
|
|
64
|
+
export function insertDelimitedAsTable(markdown, query, position, format, input, options = {}) {
|
|
65
|
+
return format === 'csv'
|
|
66
|
+
? insertCsvAsTable(markdown, query, position, input, options)
|
|
67
|
+
: insertTsvAsTable(markdown, query, position, input, options);
|
|
68
|
+
}
|
|
69
|
+
export function updateTableCell(markdown, query, rowIndex, column, value) {
|
|
70
|
+
return mutateExactlyOneTable(markdown, query, (table) => updateTableCellNode(table, rowIndex, column, value));
|
|
71
|
+
}
|
|
72
|
+
export function insertTableRow(markdown, query, index, values) {
|
|
73
|
+
return mutateExactlyOneTable(markdown, query, (table) => {
|
|
74
|
+
insertTableRowNode(table, index, values);
|
|
75
|
+
return true;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export function deleteTableRow(markdown, query, rowIndex) {
|
|
79
|
+
return mutateExactlyOneTable(markdown, query, (table) => {
|
|
80
|
+
deleteTableRowNode(table, rowIndex);
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
export function addTableColumn(markdown, query, index, header, values = []) {
|
|
85
|
+
return mutateExactlyOneTable(markdown, query, (table) => {
|
|
86
|
+
addTableColumnNode(table, index, header, values);
|
|
87
|
+
return true;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
export function deleteTableColumn(markdown, query, column) {
|
|
91
|
+
return mutateExactlyOneTable(markdown, query, (table) => {
|
|
92
|
+
deleteTableColumnNode(table, column);
|
|
93
|
+
return true;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
export function sortTable(markdown, query, column, direction) {
|
|
97
|
+
return mutateExactlyOneTable(markdown, query, (table) => sortTableNode(table, column, direction));
|
|
98
|
+
}
|
|
99
|
+
function stringifyTable(table) {
|
|
100
|
+
return stringifyAst({ type: 'root', children: [table] });
|
|
101
|
+
}
|
|
102
|
+
function replaceTableWithAst(markdown, query, table) {
|
|
103
|
+
const tree = parseMarkdown(markdown);
|
|
104
|
+
const match = requireExactlyOneTableMatch(tree, query, 'Table replacement');
|
|
105
|
+
return applyReplace(tree, [match], [table]);
|
|
106
|
+
}
|
|
107
|
+
function mutateExactlyOneTable(markdown, query, mutate) {
|
|
108
|
+
const tree = parseMarkdown(markdown);
|
|
109
|
+
const match = requireExactlyOneTableMatch(tree, query, 'Table editing');
|
|
110
|
+
return { tree, changed: mutate(match.node) ? 1 : 0 };
|
|
111
|
+
}
|
|
112
|
+
function insertTableAst(markdown, query, position, table) {
|
|
113
|
+
const tree = parseMarkdown(markdown);
|
|
114
|
+
const match = requireExactlyOneMatch(tree, query, 'Table insertion');
|
|
115
|
+
return applyInsert(tree, [match], position, [table]);
|
|
116
|
+
}
|
|
117
|
+
function requireExactlyOneMatch(tree, query, operation) {
|
|
118
|
+
const matches = queryAst(tree, query);
|
|
119
|
+
if (matches.length !== 1) {
|
|
120
|
+
throw new Error(`${operation} requires exactly one target match, received ${String(matches.length)}`);
|
|
121
|
+
}
|
|
122
|
+
return matches[0];
|
|
123
|
+
}
|
|
124
|
+
function requireExactlyOneTableMatch(tree, query, operation) {
|
|
125
|
+
const match = requireExactlyOneMatch(tree, query, operation);
|
|
126
|
+
if (match.node.type !== 'table') {
|
|
127
|
+
throw new Error(`${operation} requires a table match, received "${match.node.type}"`);
|
|
128
|
+
}
|
|
129
|
+
return match;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,kBAAkB,CAAC,OAAqB;IACtD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAC,CAAC,CAAC;AACN,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type MdastNode = {
|
|
2
|
+
type: string;
|
|
3
|
+
children?: MdastNode[];
|
|
4
|
+
position?: {
|
|
5
|
+
start?: {
|
|
6
|
+
line?: number;
|
|
7
|
+
column?: number;
|
|
8
|
+
offset?: number;
|
|
9
|
+
};
|
|
10
|
+
end?: {
|
|
11
|
+
line?: number;
|
|
12
|
+
column?: number;
|
|
13
|
+
offset?: number;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
value?: string;
|
|
17
|
+
depth?: number;
|
|
18
|
+
ordered?: boolean;
|
|
19
|
+
spread?: boolean;
|
|
20
|
+
lang?: string | null;
|
|
21
|
+
meta?: string | null;
|
|
22
|
+
url?: string;
|
|
23
|
+
title?: string | null;
|
|
24
|
+
alt?: string | null;
|
|
25
|
+
checked?: boolean | null;
|
|
26
|
+
identifier?: string;
|
|
27
|
+
label?: string | null;
|
|
28
|
+
referenceType?: string;
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
};
|
|
31
|
+
export type QueryOperator = '=' | '*=' | '^=' | '$=' | '!=' | '~=' | '>' | '>=' | '<' | '<=';
|
|
32
|
+
export type QueryPredicate = {
|
|
33
|
+
field: string;
|
|
34
|
+
operator: QueryOperator;
|
|
35
|
+
value: string;
|
|
36
|
+
};
|
|
37
|
+
export type QueryStep = {
|
|
38
|
+
type: string;
|
|
39
|
+
predicates: QueryPredicate[];
|
|
40
|
+
combinator: 'descendant' | 'child';
|
|
41
|
+
};
|
|
42
|
+
export type QueryMatch = {
|
|
43
|
+
node: MdastNode;
|
|
44
|
+
parent: MdastNode | null;
|
|
45
|
+
index: number;
|
|
46
|
+
path: number[];
|
|
47
|
+
};
|
|
48
|
+
export type InsertPosition = 'before' | 'after' | 'prepend' | 'append';
|
|
49
|
+
export type MoveTargetPolicy = 'exactly-one' | 'first' | 'last';
|
|
50
|
+
export type InsertResult = {
|
|
51
|
+
tree: MdastNode;
|
|
52
|
+
inserted: number;
|
|
53
|
+
};
|
|
54
|
+
export type MutationResult = {
|
|
55
|
+
tree: MdastNode;
|
|
56
|
+
changed: number;
|
|
57
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mdast-control",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Markdown AST transformation, querying, and LSP tooling",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/mako10k/mdast.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/mako10k/mdast/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/mako10k/mdast#readme",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "dist/index.js",
|
|
20
|
+
"types": "dist/index.d.ts",
|
|
21
|
+
"bin": {
|
|
22
|
+
"mdastctl": "dist/cli.js",
|
|
23
|
+
"mdast-lsp": "dist/lsp/server.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist/*.js",
|
|
27
|
+
"dist/*.js.map",
|
|
28
|
+
"dist/*.d.ts",
|
|
29
|
+
"dist/lsp/*.js",
|
|
30
|
+
"dist/lsp/*.js.map",
|
|
31
|
+
"dist/lsp/*.d.ts",
|
|
32
|
+
"scripts/verify-package.mjs"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -p tsconfig.json",
|
|
36
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
37
|
+
"test": "node --test dist/test/**/*.test.js",
|
|
38
|
+
"verify:package": "node scripts/verify-package.mjs"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"markdown",
|
|
42
|
+
"mdast",
|
|
43
|
+
"ast",
|
|
44
|
+
"lsp"
|
|
45
|
+
],
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"commander": "^14.0.0",
|
|
48
|
+
"remark-gfm": "^4.0.1",
|
|
49
|
+
"remark-parse": "^11.0.0",
|
|
50
|
+
"remark-stringify": "^11.0.0",
|
|
51
|
+
"unified": "^11.0.5",
|
|
52
|
+
"unist-util-visit": "^5.0.0",
|
|
53
|
+
"vscode-languageserver": "^9.0.1",
|
|
54
|
+
"vscode-languageserver-textdocument": "^1.0.12"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^24.0.0",
|
|
58
|
+
"typescript": "^5.8.3"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
3
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { basename, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const packageRoot = fileURLToPath(new URL('..', import.meta.url));
|
|
9
|
+
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
10
|
+
const tempRoot = mkdtempSync(join(tmpdir(), 'mdast-control-package-'));
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const archiveDirectory = join(tempRoot, 'archive');
|
|
14
|
+
const installDirectory = join(tempRoot, 'install');
|
|
15
|
+
mkdirSync(archiveDirectory);
|
|
16
|
+
mkdirSync(installDirectory);
|
|
17
|
+
|
|
18
|
+
const packResult = JSON.parse(execFileSync(
|
|
19
|
+
npmCommand,
|
|
20
|
+
['pack', '--json', '--pack-destination', archiveDirectory],
|
|
21
|
+
commandOptions(packageRoot),
|
|
22
|
+
))[0];
|
|
23
|
+
assert.ok(packResult && typeof packResult.filename === 'string', 'npm pack returned no artifact');
|
|
24
|
+
|
|
25
|
+
const packedPaths = new Set(packResult.files.map((file) => file.path));
|
|
26
|
+
const requiredPaths = [
|
|
27
|
+
'package.json',
|
|
28
|
+
'README.md',
|
|
29
|
+
'dist/index.js',
|
|
30
|
+
'dist/index.d.ts',
|
|
31
|
+
'dist/cli.js',
|
|
32
|
+
'dist/cli.d.ts',
|
|
33
|
+
'dist/lsp/server.js',
|
|
34
|
+
'dist/lsp/server.d.ts',
|
|
35
|
+
'scripts/verify-package.mjs',
|
|
36
|
+
];
|
|
37
|
+
for (const requiredPath of requiredPaths) {
|
|
38
|
+
assert.ok(packedPaths.has(requiredPath), `package is missing ${requiredPath}`);
|
|
39
|
+
}
|
|
40
|
+
assert.equal(
|
|
41
|
+
packResult.files.some((file) => file.path.startsWith('dist/test/')),
|
|
42
|
+
false,
|
|
43
|
+
'package must not include compiled tests',
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const archivePath = join(archiveDirectory, packResult.filename);
|
|
47
|
+
writeFileSync(join(installDirectory, 'package.json'), JSON.stringify({ private: true }));
|
|
48
|
+
execFileSync(
|
|
49
|
+
npmCommand,
|
|
50
|
+
['install', '--ignore-scripts', '--no-audit', '--no-fund', archivePath],
|
|
51
|
+
commandOptions(installDirectory),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const installedRoot = join(installDirectory, 'node_modules', 'mdast-control');
|
|
55
|
+
const publicApi = await import(pathToFileURL(join(installedRoot, 'dist', 'index.js')).href);
|
|
56
|
+
assert.equal(typeof publicApi.parseMarkdown, 'function');
|
|
57
|
+
assert.equal(typeof publicApi.runPluginOperation, 'function');
|
|
58
|
+
|
|
59
|
+
const formatted = execFileSync(
|
|
60
|
+
process.execPath,
|
|
61
|
+
[
|
|
62
|
+
join(installedRoot, 'dist', 'cli.js'),
|
|
63
|
+
'plugin',
|
|
64
|
+
'run',
|
|
65
|
+
'--plugin',
|
|
66
|
+
'codeBlock',
|
|
67
|
+
'--name',
|
|
68
|
+
'formatContent',
|
|
69
|
+
'--select',
|
|
70
|
+
'code',
|
|
71
|
+
'--args',
|
|
72
|
+
'{"format":"json"}',
|
|
73
|
+
],
|
|
74
|
+
{
|
|
75
|
+
...commandOptions(installDirectory),
|
|
76
|
+
input: '```json\n{"ok":true}\n```\n',
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
assert.match(formatted, /\{\n "ok": true\n\}/);
|
|
80
|
+
|
|
81
|
+
await verifyInstalledLsp(join(installedRoot, 'dist', 'lsp', 'server.js'), installDirectory);
|
|
82
|
+
|
|
83
|
+
process.stdout.write(`${JSON.stringify({
|
|
84
|
+
ok: true,
|
|
85
|
+
package: packResult.id,
|
|
86
|
+
filename: packResult.filename,
|
|
87
|
+
size: packResult.size,
|
|
88
|
+
unpackedSize: packResult.unpackedSize,
|
|
89
|
+
integrity: packResult.integrity,
|
|
90
|
+
entryCount: packResult.entryCount,
|
|
91
|
+
checks: [
|
|
92
|
+
'required package entries',
|
|
93
|
+
'compiled tests excluded',
|
|
94
|
+
'clean install',
|
|
95
|
+
'public API import',
|
|
96
|
+
'installed CLI codeBlock formatting',
|
|
97
|
+
'installed LSP stdio plugin round trip',
|
|
98
|
+
],
|
|
99
|
+
}, null, 2)}\n`);
|
|
100
|
+
} finally {
|
|
101
|
+
assert.ok(basename(tempRoot).startsWith('mdast-control-package-'));
|
|
102
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function commandOptions(cwd) {
|
|
106
|
+
return {
|
|
107
|
+
cwd,
|
|
108
|
+
encoding: 'utf8',
|
|
109
|
+
env: process.env,
|
|
110
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
111
|
+
windowsHide: true,
|
|
112
|
+
shell: false,
|
|
113
|
+
timeout: 120000,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function verifyInstalledLsp(serverPath, cwd) {
|
|
118
|
+
const child = spawn(process.execPath, [serverPath, '--stdio'], {
|
|
119
|
+
cwd,
|
|
120
|
+
env: process.env,
|
|
121
|
+
stdio: 'pipe',
|
|
122
|
+
windowsHide: true,
|
|
123
|
+
shell: false,
|
|
124
|
+
});
|
|
125
|
+
const client = createJsonRpcClient(child);
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const initialized = await client.request('initialize', {
|
|
129
|
+
processId: null,
|
|
130
|
+
rootUri: null,
|
|
131
|
+
capabilities: {},
|
|
132
|
+
});
|
|
133
|
+
assert.equal(initialized.capabilities.documentSymbolProvider, true);
|
|
134
|
+
client.notify('initialized', {});
|
|
135
|
+
client.notify('textDocument/didOpen', {
|
|
136
|
+
textDocument: {
|
|
137
|
+
uri: 'file:///clean-install.md',
|
|
138
|
+
languageId: 'markdown',
|
|
139
|
+
version: 1,
|
|
140
|
+
text: '```json\n{"ok":true}\n```\n',
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
const validation = await client.request('mdast/pluginRun', {
|
|
144
|
+
uri: 'file:///clean-install.md',
|
|
145
|
+
plugin: 'codeBlock',
|
|
146
|
+
operation: 'validateContent',
|
|
147
|
+
query: 'code',
|
|
148
|
+
args: { format: 'json' },
|
|
149
|
+
});
|
|
150
|
+
assert.equal(validation.kind, 'json');
|
|
151
|
+
assert.equal(validation.value.valid, true);
|
|
152
|
+
assert.equal(validation.value.checked, 1);
|
|
153
|
+
assert.equal(await client.request('shutdown', null), null);
|
|
154
|
+
client.notify('exit');
|
|
155
|
+
child.stdin.end();
|
|
156
|
+
const closed = await client.closed;
|
|
157
|
+
assert.equal(closed.code, 0, closed.stderr);
|
|
158
|
+
} finally {
|
|
159
|
+
if (child.exitCode === null) {
|
|
160
|
+
child.kill();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function createJsonRpcClient(child) {
|
|
166
|
+
let buffer = Buffer.alloc(0);
|
|
167
|
+
let nextId = 1;
|
|
168
|
+
let stderr = '';
|
|
169
|
+
const pending = new Map();
|
|
170
|
+
|
|
171
|
+
child.stderr.on('data', (chunk) => {
|
|
172
|
+
stderr += chunk.toString();
|
|
173
|
+
});
|
|
174
|
+
child.stdout.on('data', (chunk) => {
|
|
175
|
+
buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);
|
|
176
|
+
consumeMessages();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const closed = new Promise((resolve) => {
|
|
180
|
+
child.once('close', (code) => {
|
|
181
|
+
for (const request of pending.values()) {
|
|
182
|
+
clearTimeout(request.timeout);
|
|
183
|
+
request.reject(new Error(`LSP exited before responding: ${stderr}`));
|
|
184
|
+
}
|
|
185
|
+
pending.clear();
|
|
186
|
+
resolve({ code, stderr });
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
function write(message) {
|
|
191
|
+
const body = JSON.stringify(message);
|
|
192
|
+
child.stdin.write(`Content-Length: ${String(Buffer.byteLength(body))}\r\n\r\n${body}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function request(method, params) {
|
|
196
|
+
const id = nextId;
|
|
197
|
+
nextId += 1;
|
|
198
|
+
const response = new Promise((resolve, reject) => {
|
|
199
|
+
const timeout = setTimeout(() => {
|
|
200
|
+
pending.delete(id);
|
|
201
|
+
reject(new Error(`Timed out waiting for ${method}: ${stderr}`));
|
|
202
|
+
}, 10000);
|
|
203
|
+
pending.set(id, { resolve, reject, timeout });
|
|
204
|
+
});
|
|
205
|
+
write({ jsonrpc: '2.0', id, method, params });
|
|
206
|
+
return response;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function notify(method, params) {
|
|
210
|
+
write({ jsonrpc: '2.0', method, ...(params === undefined ? {} : { params }) });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function consumeMessages() {
|
|
214
|
+
while (true) {
|
|
215
|
+
const headerEnd = buffer.indexOf('\r\n\r\n');
|
|
216
|
+
if (headerEnd < 0) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const header = buffer.subarray(0, headerEnd).toString('ascii');
|
|
220
|
+
const lengthMatch = header.match(/^Content-Length:\s*(\d+)$/im);
|
|
221
|
+
assert.ok(lengthMatch, `LSP response has no Content-Length header: ${header}`);
|
|
222
|
+
const contentLength = Number(lengthMatch[1]);
|
|
223
|
+
const bodyStart = headerEnd + 4;
|
|
224
|
+
const bodyEnd = bodyStart + contentLength;
|
|
225
|
+
if (buffer.length < bodyEnd) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const message = JSON.parse(buffer.subarray(bodyStart, bodyEnd).toString('utf8'));
|
|
229
|
+
buffer = buffer.subarray(bodyEnd);
|
|
230
|
+
settleResponse(message);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function settleResponse(message) {
|
|
235
|
+
if (typeof message.id !== 'number') {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const requestState = pending.get(message.id);
|
|
239
|
+
if (!requestState) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
pending.delete(message.id);
|
|
243
|
+
clearTimeout(requestState.timeout);
|
|
244
|
+
if (message.error) {
|
|
245
|
+
requestState.reject(new Error(`LSP error ${String(message.error.code)}: ${message.error.message}`));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
requestState.resolve(message.result);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return { request, notify, closed };
|
|
252
|
+
}
|